繁体   English   中英

使用 Curl 命令行实用程序并行下载

[英]Parallel download using Curl command line utility

我想从一个网站下载一些页面,我使用curl成功地做到了,但我想知道curl是否像大多数下载管理器一样一次下载多个页面,它会加快速度一点。 是否可以在curl命令行实用程序中执行此操作?

我正在使用的当前命令是

curl 'http://www...../?page=[1-10]' 2>&1 > 1.html

在这里,我从 1 到 10 下载页面并将它们存储在名为1.html的文件中。

此外,curl 是否可以将每个 URL 的curl写入单独的文件,例如URL.html ,其中URL是正在处理的页面的实际 URL

我的回答有点晚了,但我相信所有现有的答案都有些短。 我这样做的方式是使用xargs ,它能够在子进程中运行指定数量的命令。

我会使用的单线很简单:

$ seq 1 10 | xargs -n1 -P2 bash -c 'i=$0; url="http://example.com/?page${i}.html"; curl -O -s $url'

这需要一些解释。 使用-n 1指示xargs处理单个输入参数。 在本例中,数字1 ... 10分别被处理。 并且-P 2告诉xargs保持 2 个子进程一直运行,每个子进程处理一个参数,直到处理完所有输入参数。

您可以将其视为 shell 中的 MapReduce。 或者也许只是地图阶段。 无论如何,这是完成大量工作的有效方法,同时确保您不会分叉炸弹您的机器。 可以在 shell 的 for 循环中做类似的事情,但最终会做进程管理,一旦你意识到xargs这种使用是多么的疯狂,这开始看起来毫无意义。

更新:我怀疑我的xargs示例可以改进(至少在带有-J标志的 Mac OS X 和 BSD 上)。 使用 GNU Parallel,该命令也不再那么笨拙:

parallel --jobs 2 curl -O -s http://example.com/?page{}.html ::: {1..10}

好吧, curl只是一个简单的 UNIX 进程。 您可以让尽可能多的curl进程并行运行并将它们的输出发送到不同的文件。

curl可以使用 URL 的文件名部分来生成本地文件。 只需使用-O选项( man curl了解详细信息)。

您可以使用以下内容

urls="http://example.com/?page1.html http://example.com?page2.html" # add more URLs here

for url in $urls; do
   # run the curl job in the background so we can start another job
   # and disable the progress bar (-s)
   echo "fetching $url"
   curl $url -O -s &
done
wait #wait for all background jobs to terminate

从 7.66.0 开始, curl实用程序终于内置了对在单个非阻塞进程中并行下载多个 URL 的支持,在大多数情况下,与xargs和后台生成相比,它应该更快且资源效率更高:

curl -Z 'http://httpbin.org/anything/[1-9].{txt,html}' -o '#1.#2'

这将并行下载 18 个链接并将它们写入 18 个不同的文件,也是并行的。 Daniel Stenberg 对此功能的官方公告在这里: https : //daniel.haxx.se/blog/2019/07/22/curl-goez-parallel/

Curl 还可以通过将文件分成几部分来加速文件的下载:

$ man curl |grep -A2 '\--range'
       -r/--range <range>
              (HTTP/FTP/SFTP/FILE)  Retrieve a byte range (i.e a partial docu-
              ment) from a HTTP/1.1, FTP or  SFTP  server  or  a  local  FILE.

这是一个脚本,它将自动启动具有所需并发进程数的 curl: https : //github.com/axelabs/splitcurl

为了启动并行命令,为什么不使用古老的make命令行实用程序。它支持并行执行和依赖项跟踪等等。

如何? 在下载文件的目录中,创建一个名为Makefile的新文件,内容如下:

# which page numbers to fetch
numbers := $(shell seq 1 10)

# default target which depends on files 1.html .. 10.html
# (patsubst replaces % with %.html for each number)
all: $(patsubst %,%.html,$(numbers))

# the rule which tells how to generate a %.html dependency
# $@ is the target filename e.g. 1.html
%.html:
        curl -C - 'http://www...../?page='$(patsubst %.html,%,$@) -o $@.tmp
        mv $@.tmp $@

注意最后两行应以 TAB 字符(而不是 8 个空格)开头,否则 make 将不接受该文件。

现在你只需运行:

make -k -j 5

我使用的 curl 命令会将输出存储在1.html.tmp并且只有当 curl 命令成功时才会将其重命名为1.html (通过下一行的mv命令)。 因此,如果某些下载失败,您只需重新运行相同的make命令,它将继续/重试下载第一次下载失败的文件。 一旦所有文件都成功下载,make 将报告没有更多的事情要做,所以为了“安全”多运行一次也没有坏处。

-k开关告诉 make 继续下载其余的文件,即使一次下载失败。)

如果您的系统有像pidofpgrep这样的命令,那么运行有限数量的进程很容易,给定一个进程名称,返回 pids(pids 的计数告诉有多少正在运行)。

像这样的东西:

#!/bin/sh
max=4
running_curl() {
    set -- $(pidof curl)
    echo $#
}
while [ $# -gt 0 ]; do
    while [ $(running_curl) -ge $max ] ; do
        sleep 1
    done
    curl "$1" --create-dirs -o "${1##*://}" &
    shift
done

像这样调用:

script.sh $(for i in `seq 1 10`; do printf "http://example/%s.html " "$i"; done)

脚本的卷曲行未经测试。

我想出了一个基于fmtxargs的解决方案。 这个想法是在大括号http://example.com/page{1,2,3}.html内指定多个 URL,并与xargs并行运行它们。 以下将在 3 个过程中开始下载:

seq 1 50 | fmt -w40 | tr ' ' ',' \
| awk -v url="http://example.com/" '{print url "page{" $1 "}.html"}' \
| xargs -P3 -n1 curl -o

因此生成 4 行可下载的 URL 并将其发送到xargs

curl -o http://example.com/page{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}.html
curl -o http://example.com/page{17,18,19,20,21,22,23,24,25,26,27,28,29}.html
curl -o http://example.com/page{30,31,32,33,34,35,36,37,38,39,40,41,42}.html
curl -o http://example.com/page{43,44,45,46,47,48,49,50}.html

curlwget无法以并行块下载单个文件,但有替代方案:

  • aria2 (用 C++ 编写,在 Deb 和 Cygwin 存储库中可用)

     aria2c -x 5 <url>
  • axel (用 C 编写,可在 Deb 仓库中找到)

     axel -n 5 <url>
  • wget2 (用 C 编写,在 Deb仓库中可用)

     wget2 --max-threads=5 <url>
  • lftp (用 C++ 编写,在 Deb 仓库中可用)

     lftp -n 5 <url>
  • hget (用 Go 编写)

     hget -n 5 <url>
  • pget (用 Go 编写)

     pget -p 5 <url>

7.68.0开始 curl 可以并行获取多个 url。 此示例将从具有 3 个并行连接的urls.txt文件中获取 url:

curl --parallel --parallel-immediate --parallel-max 3 --config urls.txt

网址.txt:

url = "example1.com"
output = "example1.html"
url = "example2.com"
output = "example2.html"
url = "example3.com"
output = "example3.html"
url = "example4.com"
output = "example4.html"
url = "example5.com"
output = "example5.html"

Bash 3 或以上允许您在扩展序列表达式时用多个值填充数组:

$ urls=( "" http://example.com?page={1..4} )
$ unset urls[0]

请注意[0]值,它作为简写形式提供,以使索引与页码对齐,因为 bash arrays 自动编号从零开始。 这种策略显然可能并不总是奏效。 无论如何,您可以在此示例中取消设置它。

现在你有了一个数组,你可以使用declare -p验证内容:

$ declare -p urls
declare -a urls=([1]="http://example.com?Page=1" [2]="http://example.com?Page=2" [3]="http://example.com?Page=3" [4]="http://example.com?Page=4")

现在您在数组中有了 URL 列表,将数组展开为 curl 命令行:

$ curl $(for i in ${!urls[@]}; do echo "-o $i.html ${urls[$i]}"; done)

curl命令可以获取多个 URL 并获取所有 URL,将现有连接 (HTTP/1.1) 回收到公共服务器,但它需要在每个 URL 之前加上-o选项才能下载和保存每个目标。 请注意,某些 URL 中的字符可能需要转义以避免与您的 shell 交互。

我不确定 curl ,但您可以使用wget来做到这一点。

wget \
     --recursive \
     --no-clobber \
     --page-requisites \
     --html-extension \
     --convert-links \
     --restrict-file-names=windows \
     --domains website.org \
     --no-parent \
         www.website.org/tutorials/html/

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM