[英]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 继续下载其余的文件,即使一次下载失败。)
如果您的系统有像pidof
或pgrep
这样的命令,那么运行有限数量的进程很容易,给定一个进程名称,返回 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)
脚本的卷曲行未经测试。
我想出了一个基于fmt
和xargs
的解决方案。 这个想法是在大括号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
从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.