【问题标题】:Multi threading cURL request?多线程 cURL 请求?
【发布时间】:2021-12-31 03:19:28
【问题描述】:

我想在 ubuntu 上生成数字列表并发出 http 请求并使用 cURL 和 jq 保存结果。

我已经使用 FOR 循环编写了单线程并且它可以工作,但我想在我的脚本中添加线程以更快地工作。

    for i in `seq 12345 12550 `; do echo $i ;curl -s -k -X $'GET' \
-H $'Host: example.com' -H $'User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36' -H $'Accept: application/json, text/plain, */*' -H $'Accept-Encoding: gzip, deflate' -H $'Accept-Language: en-US,en;q=0.9,fa;q=0.8' -H $'Connection: close' \
$'https://example.com/pages/json/'$i'/list' | jq ".files[]"; done

如何向我的脚本添加线程或并行以加快我的进程?

我的目标是制作一个 bash 脚本,它接收来自用户的数字范围和线程计数的参数,并将每个请求的结果按请求的数字名称保存到特定路径。

$script.sh -numbers 12345-12550 -threads 100 -outpath ~/result/

【问题讨论】:

  • 多线程多个 API 调用可能会被解释为滥用,并导致您的 IP/应用程序/主机被禁止或至少受到限制。 API 提供者通常对每日允许的请求设置限制,并实现分页和结果计数。在实施可能滥用的内容之前,请查看 API 的 TOS 允许的速率/数量和功能。
  • @LéaGris 我已经检查了我的限制,对此没有任何问题。

标签: bash multithreading shell curl


【解决方案1】:
doit() {
  i="$1"
  echo $i
  curl -s -k -X $'GET' \
    -H $'Host: example.com' -H $'User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36' -H $'Accept: application/json, text/plain, */*' -H $'Accept-Encoding: gzip, deflate' -H $'Accept-Language: en-US,en;q=0.9,fa;q=0.8' -H $'Connection: close' \
    $'https://example.com/pages/json/'$i'/list' |
    jq ".files[]"
}
export -f doit

seq 12345 12550 | parallel -j100 --results ~/result/{} doit

【讨论】:

  • 谢谢!工作完美!如何添加额外的步骤来处理 JQ 结果,如果包含“***”作为响应,将序列号保存在 result.txt 中?示例:如果 12345 在响应中包含 ***,则将 12345 保存在 result.txt 中。
  • doit() { i="$1" echo $i if curl -s -k -X $'GET' \ -H $'Host: example.com' -H $'User-Agent : Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36' -H $'Accept: application/json, text/plain, /' -H $'Accept-Encoding: gzip, deflate' -H $'Accept-Language: en-US,en;q=0.9,fa;q=0.8' -H $'Connection: close' \ $ 'example.com/pages/json/'$i'/list' | jq '.files[]|包含("***")' | grep 真;然后 echo $i >> result.txt fi } export -f doit seq 12345 12550 |并行 -j100 --results ~/result/{} doit
  • @nerdg33k 如果 $i 足够大,该解决方案会存在竞争条件的轻微风险:mywiki.wooledge.org/… 在您的用例中可能没问题。
【解决方案2】:

如果curl --version 表示您拥有7.66.0 或更高版本,那么您可以使用-Z/--parallel 进行并行传输。 --parallel-max <num> 设置同时传输的数量限制(默认限制为 50)。

但要使用它,您需要在 curl 的单个调用中指定目标 URL。一种方法是使用 curl 的 URL globbing(在man curl 中有描述)。例如,要获取 1 到 100 的范围:

curl 'https://example.com/pages/json/[1-100]/list'

还可以提供计步器(例如,[1-30:10] 对应于11121)。即使没有-Z,这应该更有效,因为 curl 将能够重用连接。它还将允许 #1 的使用在 --output 文件名中具有特殊含义(这在 curl 手册中进行了描述,并且我认为您的意思是 save the result for each request to the specific path by requested number name)。

顺便说一句,您可能更喜欢使用-H 'Connection: keep-alive' 而不是-H $'Connection: close'(在发出多个请求时要求保持连接打开,$'' 引用用于处理我不使用的 C 样式反斜杠转义认为你想在这里做)。

我的目标是……

$script.sh -numbers 12345-12550 -threads 100 -outpath ~/result/

这是一个关于数字和线程的想法(在这里简单地作为第一个和第二个位置参数给出 - 选项解析取决于你):

#!  /bin/sh   -

range=${1:?}
parallel=$2

url="https://example.com/pages/json/[$range]/list"
out='example_page_#1.json'

ua="Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"

curl -qfsS --compressed ${parallel:+-Z --parallel-max "$parallel"} \
  -H 'Accept: application/json, text/plain, */*'    \
  -H 'Accept-Encoding: gzip, deflate'               \
  -H 'Accept-Language: en-US,en;q=0.9,fa;q=0.8'     \
  -H 'Connection: keep-alive'                       \
  -A "$ua" -o "$out" "$url"
$ chmod +x example
$ ./example 01-15  5
$ ls
example_page_01.json  example_page_06.json  example_page_11.json
example_page_02.json  example_page_07.json  example_page_12.json
example_page_03.json  example_page_08.json  example_page_13.json
example_page_04.json  example_page_09.json  example_page_14.json
example_page_05.json  example_page_10.json  example_page_15.json

这会将每次传输保存到其自己的example_page_N.json 文件(在当前目录中)。之后您可以使用jq 进行任何处理。也许:

for f in example_page_*.json
  jq '.files[]' < "$f" > "files_$f"
done

我在评论中注意到您想要一个输出文件,其中每个结果的编号取决于.files[] 是否包含“***”。根据 json 数据,您可以使用 jq 在单个管道中执行此操作(但这与“多线程 cURL 请求?”的问题不同)。

【讨论】:

    【解决方案3】:

    最简单的方法是获取您拥有的脚本并使其接受输入以运行一次,然后创建另一个脚本以各种值调用该脚本。

    例如:

    Script1.sh

    echo "%1 Hello World" >> output.txt
    

    Script2.sh

     for name in $namesList ;do
            ./Script1.sh $name &
     done
     wait
    
    
    cat output.txt
    Dave Hello World
    Tom Hello World
    ... 
    

    注意:这更像是一种初学者方法,但您可以从那里调用类似这样的函数并使其成为一个脚本。

    请参阅有关工作的文档: https://tldp.org/LDP/abs/html/x9644.html

    【讨论】:

    • 我没有从您的回答中得到任何提示。为什么我应该使用 Script1.sh 来生成名称?只需将该单行操作放入 Script2.sh 的 DO 部分即可。没有关于我的问题的任何相关信息。
    • 我猜你在哪里被抛弃了,因为我没有包含“卷曲语句。另外,是的,我没有时间研究卷曲线程,或者认为你可以使用这样的东西,但是似乎其他人对你更有信心。我的意思是向你展示如何启动一个 for 循环来启动 X 请求并立即运行它们,等待命令将确保脚本一直运行直到完成。你也可以通过如果作业数量超过我未包含在伪代码中的 X,则告诉它停止。
    猜你喜欢
    • 2020-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-04
    • 2018-08-17
    • 1970-01-01
    相关资源
    最近更新 更多