我们的目标网址:http://data.stats.gov.cn/easyquery.htm?cn=C01
里面可以查询国家统计局发布的各种数据,这里我们针对人口进行爬取,其它项方法都是一样。
提示:看之前需要了解HTTP协议和XMLHttpRequest方法。
推荐用谷歌浏览器
首先进入下面这个页面:
然后按F12,出现下面这个页面:
再次刷新页面,看看有什么新的东西:
注意图中选择XHR
然后看到有三条记录
按顺序点开看看:
可以看到里面有一些信息:
Request URL:请求的网址
Request Method:请求方法
Status Code:状态码等等
在Headers里面下滑到最后面,看到以下几个字段:
这个Form Data里面的东西很重要,以后要用到,现在只要记得有这个东西就行。然后看m对应着getTree,然后应该可以猜到这个请求应该是获取一些目录结构的。
然后我们直接看第三个(你也可以看看第二个)
看第三个的m对应的QueryData,其实这里表示这是用来获取数据的,如何验证呢,我们点开Preview(预览),这里你可以看见请求过来的信息。
可以看见请求过来的是一个json格式(简单来说就是字典里面套字典的一种格式)
然后可以看到非常清楚的信息对应着网页上的数据
所以现在只要我们请求这个地址,就会给我们返回这些信息,那么这个地址是什么。这时,我们要回到Headers里面查看
在Request URL:后面就是我们要请求的地址,还可以看到这是用get方法获取的。
仔细分析这个url,我把这个url拆成2个部分,一个是问号之前另一个是问号之后
懂http协议的应该都明白,问号之后的是传的一些参数给服务器的,突然发现问号之后就是一个m,好像在哪见过不是吗,滑倒最下面
其实对应的就是上面那些参数,这里我只解释一个k1是什么东西(因为其它的我还没有完成弄明白),k1后面跟的数字其实是一个时间戳(不懂的百度搜下就明白啦)
在Python里面可以用time库来获取
import time
ntime = int(round(time.time() * 1000))
来段代码结合来说:
# 我采用requests库
import requests
import time
# 用来获取 时间戳
def gettime():
return int(round(time.time() * 1000))
if __name__ == '__main__':
# 用来自定义头部的
headers = {}
# 用来传递参数的
keyvalue = {}
# 目标网址(问号前面的东西)
url = 'http://data.stats.gov.cn/easyquery.htm'
# 头部的填充
headers['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14) ' \
'AppleWebKit/605.1.15 (KHTML, like Gecko) ' \
'Version/12.0 Safari/605.1.15'
# 下面是参数的填充,参考图10
keyvalue['m'] = 'QueryData'
keyvalue['dbcode'] = 'hgnd'
keyvalue['rowcode'] = 'zb'
keyvalue['colcode'] = 'sj'
keyvalue['wds'] = '[]'
keyvalue['dfwds'] = '[]'
keyvalue['k1'] = str(gettime())
# 发出请求,使用get方法,这里使用我们自定义的头部和参数
r = requests.get(url, headers=headers, params=keyvalue)
# 打印返回过来的状态码
print r.status_code
# 打印我们构造的url
print r.url
# 打印返回的数据
print r.text
好像没有什么问题,但是如果你仔细看看返回过来的数据就会发现有问题,根本不是我们想要的数据,虽然也是json结构,但是里面的数据完全是错的。所以如何获取到我们想要的呢,返回到谷歌浏览器,进行如下操作。
先在如上的页面刷新一次,然后下面会有三条记录,然后依次点击人口->总人口,然后又会多出两条记录。
很明显,多出来的第一条是第一次点击得到,多出来的第二条是第二次点击得到,由于我们是想查看总人口,也就是第二次点击传来的数据,所以点击多出来的第二条,查看它的详细信息。
滑到最下面,可以看到dfwds字段有东西了,之前的都没有的(参考图10),原来还要传参数。修改代码:
# 我采用requests库
import requests
import time
# 用来获取 时间戳
def gettime():
return int(round(time.time() * 1000))
if __name__ == '__main__':
# 用来自定义头部的
headers = {}
# 用来传递参数的
keyvalue = {}
# 目标网址(问号前面的东西)
url = 'http://data.stats.gov.cn/easyquery.htm'
# 头部的填充
headers['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14) ' \
'AppleWebKit/605.1.15 (KHTML, like Gecko) ' \
'Version/12.0 Safari/605.1.15'
# 下面是参数的填充,参考图10
keyvalue['m'] = 'QueryData'
keyvalue['dbcode'] = 'hgnd'
keyvalue['rowcode'] = 'zb'
keyvalue['colcode'] = 'sj'
keyvalue['wds'] = '[]'
# keyvalue['dfwds'] = '[]'
# 上面那个修改成下面这个
keyvalue['dfwds'] = '[{"wdcode":"zb","valuecode":"A0301"}]'
keyvalue['k1'] = str(gettime())
# 发出请求,使用get方法,这里使用我们自定义的头部和参数
r = requests.get(url, headers=headers, params=keyvalue)
# 打印返回过来的状态码
print r.status_code
# 打印我们构造的url
print r.url
# 打印返回的数据
print r.text
再来看看结果
如果耐心点可以看出,我们获取了从2008到2017的所有数据,然后我们就可以从里面进行解析数据进行存储来。但是如果要找2000年的呢,看下面。
从上图可以看见这个页面还提供搜索功能,于是我们也可以进行搜索。比如2000年。
点击确定
然后我们发现又多了一条记录,然后查看Headers里面,滑到最下面看,dfwds的内容又变了,于是如果我们想要用程序模拟搜索功能,把代码中的dfwds改成图片里面的就可以了呢。我帮你们已经测试过了,会返回数据,但是数据会跟第一次一样,有数据,但不是我们想要的,那怎么办。
经过我几个小时到摸索,我终于搞出来了,针对于目前的情况,我们是每一次都进行一次请求,得到数据后就断了,如果要进行搜索,我们必须要建立一个对话(Session),然后在这个对话中把dfwds改成上图中的内容就可以成功获取。
代码修改:
# 我采用requests库
import requests
import time
# 用来获取 时间戳
def gettime():
return int(round(time.time() * 1000))
if __name__ == '__main__':
# 用来自定义头部的
headers = {}
# 用来传递参数的
keyvalue = {}
# 目标网址(问号前面的东西)
url = 'http://data.stats.gov.cn/easyquery.htm'
# 头部的填充
headers['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14) ' \
'AppleWebKit/605.1.15 (KHTML, like Gecko) ' \
'Version/12.0 Safari/605.1.15'
# 下面是参数的填充,参考图10
keyvalue['m'] = 'QueryData'
keyvalue['dbcode'] = 'hgnd'
keyvalue['rowcode'] = 'zb'
keyvalue['colcode'] = 'sj'
keyvalue['wds'] = '[]'
keyvalue['dfwds'] = '[{"wdcode":"zb","valuecode":"A0301"}]'
keyvalue['k1'] = str(gettime())
# 发出请求,使用get方法,这里使用我们自定义的头部和参数
# r = requests.get(url, headers=headers, params=keyvalue)
# 建立一个Session
s = requests.session()
# 在Session基础上进行一次请求
r = s.get(url, params=keyvalue, headers=headers)
# 打印返回过来的状态码
print r.status_code
# 修改dfwds字段内容
keyvalue['dfwds'] = '[{"wdcode":"sj","valuecode":"2000"}]'
# 再次进行请求
r = s.get(url, params=keyvalue, headers=headers)
# 此时我们就能获取到我们搜索到的数据了
print r.text
至此一次完整获取数据的过程就结束了,上面代码进行修改就可以获取很多东西的数据,当然如果弄清各个字段的意思就更好了。如有错误请及时告知。