标准输出附加到文件大小限制

kem*_*mri 5 shell-script curl stdout gnu-parallel

我正在从美国国家公路交通安全管理局 API 中提取大约 25,000,000 个 VIN 编号的 VIN 规范。这是大量数据,因为我没有以任何方式转换数据,curl似乎是一种比 Python 更高效、更轻量级的完成任务的方式(因为 Python 的 GIL 使并行处理有点痛苦)。

在下面的代码中,vins.csv是一个包含 2500 万个 VIN 的大样本的文件,分成 100 个 VIN 的块。这些将被传递给使用 4 个内核的 GNU Parallel。一切都nhtsa_vin_data.csv在最后倾倒。

$ cat vins.csv | parallel -j10% curl -s --data "format=csv" \
   --data "data={1}" https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVINValuesBatch/ \
      >> /nas/BIGDATA/kemri/nhtsa_vin_data.csv
Run Code Online (Sandbox Code Playgroud)

这个过程开始时每分钟写入大约 3,000 个 VIN,随着时间的推移逐渐变慢(目前约为 1,200/分钟)。

我的问题

  • 我的命令中是否有任何内容会nhtsa_vin_data.csv随着规模的增长而增加开销?
  • 这与 Linux 处理>>操作的方式有关吗?

更新 #1 - 解决方案

每个@slm 的第一个解决方案 - 使用并行的 tmp 文件选项将每个 curl 输出写入其自己的 .par 文件,最后合并:

$ cat vins.csv | parallel \
--tmpdir /home/kemri/vin_scraper/temp_files \
--files \
-j10% curl -s \
--data "format=csv" \
--data "data={1}" https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVINValuesBatch/ > /dev/null

cat <(head -1 $(ls *.par|head -1)) <(tail -q -n +2 *.par) > all_data.csv
Run Code Online (Sandbox Code Playgroud)

每个@oletange 的第二个解决方案 - 使用 --line-buffer 将输出缓冲到内存而不是磁盘:

$ cat test_new_mthd_vins.csv | parallel \
    --line-buffer \
    -j10% curl -s \
    --data "format=csv" \
    --data "data={1}" https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVINValuesBatch/ \
    >> /home/kemri/vin_scraper/temp_files/nhtsa_vin_data.csv
Run Code Online (Sandbox Code Playgroud)

性能注意事项

我发现这里建议的两个解决方案都非常有用和有趣,并且将来肯定会更多地使用这两个版本(用于比较性能和额外的 API 工作)。希望我能够运行一些测试,看看哪一个在我的用例中表现更好。

此外,像@oletange 和@slm 建议的那样运行某种吞吐量测试是明智的,因为 NHTSA 成为这里的瓶颈的可能性是不可忽略的。

slm*_*slm 3

我怀疑这>>会导致您在分叉收集 API 数据的命令nhtsa_vin_data.csv之间争用文件。curlparallel

\n\n

我会这样调整你的应用程序:

\n\n
$ cat p.bash\n#!/bin/bash\n\ncat vins.csv | parallel --will-cite -j10% --progress --tmpdir . --files \\\n   curl -s --data "format=csv" \\\n     --data "data={1}" https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVINValuesBatch/\n
Run Code Online (Sandbox Code Playgroud)\n\n

这将为您的命令提供curl自己的独立文件来写入数据。

\n\n

例子

\n\n

我获取了您提供给我的这 3 个 VIN 1HGCR3F95FA017875;1HGCR3F83HA034135;3FA6P0T93GR335818;,并将它们放入名为 的文件中vins.csv。然后我将它们复制了很多次,以便该文件最终具有以下特征:

\n\n每行 VIN \n\n
$ tail -1 vins.csv | grep -o \';\' | wc -l\n26\n
Run Code Online (Sandbox Code Playgroud)\n\n行数\n\n
$ wc -l vins.csv\n15 vins.csv\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后我使用这些数据运行我的脚本:

\n\n
$ ./p.bash\n\nComputers / CPU cores / Max jobs to run\n1:local / 1 / 1\n\nComputer:jobs running/jobs completed/%of started jobs/Average seconds to complete\nlocal:1/0/100%/0.0s ./pard9QD3.par\nlocal:1/1/100%/10.0s ./paruwK9L.par\nlocal:1/2/100%/8.5s ./parT6rCS.par\nlocal:1/3/100%/7.3s ./pardzT2g.par\nlocal:1/4/100%/6.8s ./parDAsaO.par\nlocal:1/5/100%/6.8s ./par9X2Na.par\nlocal:1/6/100%/6.7s ./par6aRla.par\nlocal:1/7/100%/6.7s ./parNR_r4.par\nlocal:1/8/100%/6.4s ./parVoa9k.par\nlocal:1/9/100%/6.1s ./parXJQTc.par\nlocal:1/10/100%/6.0s ./parDZZrp.par\nlocal:1/11/100%/6.0s ./part0tlA.par\nlocal:1/12/100%/5.9s ./parydQlI.par\nlocal:1/13/100%/5.8s ./par4hkSL.par\nlocal:1/14/100%/5.8s ./parbGwA2.par\nlocal:0/15/100%/5.4s\n
Run Code Online (Sandbox Code Playgroud)\n\n

把事情放在一起

\n\n

当上述运行完成后,您可以将cat所有文件放在一起以获得单个.csv文件ala:

\n\n
$ cat *.par > all_data.csv\n
Run Code Online (Sandbox Code Playgroud)\n\n

执行此操作时请务必小心,因为每个文件对于其中包含的 CSV 数据都有自己的标题行。要处理从结果文件中取出标头:

\n\n
$ cat <(head -1 $(ls *.par|head -1)) <(tail -q -n +2 *.par) > all_data.csv\n
Run Code Online (Sandbox Code Playgroud)\n\n

你的表现变慢

\n\n

在我的测试中,DOT 网站确实在查询继续访问其 API 时对其进行了限制。我在实验中看到的上述时间虽然很小,但随着每个查询发送到 API 网站而减少。

\n\n

我在笔记本电脑上的表现如下:

\n\n
$ seq 5 | parallel --will-cite --line-buffer \'yes {} | head -c 1G\' | pv >> /dev/null\n   5GiB 0:00:51 [99.4MiB/s] [                                                                                                                                                                  <=>       ]\n
Run Code Online (Sandbox Code Playgroud)\n\n

注意:以上内容借自 Ole Tange 的答案并进行了修改。它通过管道写入 5GB 数据parallel并将其传输到pv >> /dev/null. pv使用它,我们可以监控通过管道的吞吐量并获得 MB/s 类型的测量结果。

\n\n

我的笔记本电脑能够达到约 100MB/s 的吞吐量。

\n\n

NHTSA API 常见问题解答

\n\n
\n

应用程序编程接口

\n\n

对于 Batch\xe2\x80\x99 中的 \xe2\x80\x98Decode VIN(平面格式),是否有通过 URL 进行此查询的示例,类似于其他操作?

\n\n

对于这个特定的 API,您只需将一组 VIN 放入框中,并用 \xe2\x80\x9c;\xe2\x80\x9d 分隔。您还可以在 \xe2\x80\x9c;\xe2\x80\x9d 之前指示型号年份,并用 \xe2\x80\x9c,\xe2\x80\x9d 分隔。您可以通过此服务输入的 VIN 数量有上限。

\n\n

盒内示例为样本:5UXWX7C5*BA,2011;5YJSA3DS*EF

\n\n

来源: https: //vpic.nhtsa.dot.gov/MfrPortal/home/faq搜索“rate”

\n
\n\n

上面提到了使用API​​有一个上限:

\n\n
\n

您可以通过此服务输入的 VIN 数量有上限。

\n
\n\n

参考

\n\n\n