本文最初写成于 2021 年 7 月,由于作者的拖延问题以致今天才得以和各位同学(即便大概率是自说自话了)见面,因此文中具体细节可能已经发生变化。
Cover Photo by Maksym Kaharlytskyi on Unsplash
使用 UN Comtrade API 批量下载数据
写在前面
谨以本文记录我与 UN Comtrade Database 的初次交手,四天时间里的爬虫初体验着实让我感到精疲力竭。今天我把这经验分享在这里,抛砖引玉,也许能给后来的同学一些启发。
背景知识
这一部分将向你介绍什么是 UN Comtrade Database,其 API 的使用方法,和所需的 Python 知识。
什么是 UN Comtrade DB
UN COMTRADE is the pseudonym for United Nations International Trade Statistics Database. Over 170 reporter countries/areas provide the United Nations Statistics Division (UNSD) with their annual and monthly l international trade statistics data detailed by commodities/service categories and partner countries.
根据联合国统计司 knowledge base 的上述说明,你可以在 Comtrade Database 中查询170+国家报告的各品类商品/服务的年度/月度贸易数据。数据的具体类型将会在 Hands-on 部分详细分析,在此之前你可以通过官网的在线预览粗略感知,在这里你可以在这一页面指定时间、产品类型、产品种类、报告国和合作国等特征。
The UN Comtrade data extraction API
我们可以通过这一 API 来提取数据库中的特定数据,以 CSV 或 JSON 格式下载,或利用 AJAX call 来将数据融入进你的网页。
统计司鼓励用户把自己的可视化作品分享给他们,优秀的作品有可能被引用在 UN Comtrade Labs 中。我在这里发现了很多有意思的项目,非常有启发性(aka 我实在是太菜自己根本搞不出来)
Python 知识
只需了解基本语法即可!如果你对 Python 感到陌生,可以跟着廖雪峰的官方网站的 Python 教程学习,只需要学到高级特性部分就足够理解下面涉及的所有内容了。
Hands-on
我的工作是根据博主王蛋糕cake的这两篇慷慨的博文的完善,针对中断处理 (刚刚考完计组,写到这里不由得心头一颤) 和下载速度和下载内容有效性的判断等方面进行了优化。此外,还补充了下载后的清理过程,清理下载失败的文件。
Understanding the Data Request Form
通过阅读 API文档 ,我们能在 UN Comtrade data request format 部分了解到发送请求的基本格式:
http://comtrade.un.org/api/get?parameters
其中,parameters
允许的参数在文档中有完整的说明,我就不再在此处赘述了,但其中值得强调的一点是:如果 freq
参数赋值为 M
(代表以月份为单位获取数据)时,px
(classification) 参数不要选择 SITC 那一套 (ST, S1, S2, … , S4) 因为没有这样的数据,你获得的都将是空表。
假设,我们现在想要查询 csv 格式下,美国2020年9月的AG4精度的进出口商品数据,该如何写这个请求 url 呢?
其中一种答案是:
https://comtrade.un.org/api/get?max=100000&type=C&freq=M&px=HS&ps=202006&r=all&p=842&rg=all&cc=AG4&fmt=csv
也许和你的答案有些出入,请注意API 对参数的前后顺序并不敏感;r
参数和 p
参数任意分别设置为 842 (code for USA) 和 all 即可;如果仔细的阅读过 Knowledgebase 文档,你就会清楚:对于同一条贸易数据,站在 importer 和 exporter 的视角(指选择 importer/exporter 为 reporter),trade value 是不同的,通常 import value 会高于 export value 因为:
Imports is generally reported on the basis of Cost, Insurance and Freight, (CIF) while exports is reported on a Free on Board (FOB) basis. For this reason, import values tend to be higher than export values.
另,根据 Worldbank 的提示,通常来讲,import data 更加准确,出于关税计算的原因。
但回到现实,这样的请求很难获得目标数据;因为美国的进出口贸易量较大,4 digits HS Code (AG4) 描述下的进口和出口数据条数实在是太大了,很可能超过 guest 用户的下载条数限额。
面对这样的问题,如果我们不愿意在精度上做出妥协,那么就需要将这条请求拆分为多条,这里可以有很多种逻辑,例如:
- 分别下载进口和出口数据(by altering the
rg
parameter) - 遍历国家代码,每次请求指定 reporter 的国家代码 (by specifying the
r
parameter)
这也就引出了参数设置的迷思,由于下面介绍的,对于 guest 用户的访问频率和总量的限制的存在,成为了下载条数和下载次数之间的博弈(每次下载拆分的越细,需要下载的次数就越多、由于频率的限制,下载的次数越多,耗费的时间就越长)我们要尽可能地保持平衡。
你会在 Usage limits 部分了解到:
- 对于 guest 用户,每秒不能发送多于 1 条请求(rate),且每小时不能发送超过 100 条请求 。
- 对于
ps
,r
和p
参数,输入的限制条件 (code) 不能超过五条,上述三个参数只能出现一次 ALL;
我将在下面的例子中展示,如何通过混合 动态IP 和 简易的多线程 技术实现效率更高的数据获取。
Dynamic IP
选择 TB 买家提供的最廉价的 (15 rmb/d 至少在 07/2021 是这样的行情) 动态 IP 服务即可。我遇到的买家很贴心的提供了一些语言的不同包的 Proxy 设置语法,但对于我们的例子,只需要了解 IP 端口 用户名 和 密码,填写到下面代码的对应部分即可。
Coding
- Requirement: 获取2019-2021,各国家月度 AG2 精度的进出口数据;
- Analysis: 由于数据条数的限制,采取以下下载策略:分月度分别下载每个国家为 partner 的进口和出口数据;
import requests
import os
import json
from random import randint
import time
import threading
import datetime
# proxy varification process, it might be different depending on your proxy provider
# please change the content quoted below accordingly
proxyHost = "your host link"
proxyPort = "port your host provided"
proxyUser = "username"
proxyPass = "password"
# User agents (UA)
user_agents = [
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
"M