利用Python爬虫,只需要会requests + re 即可抓取中国国家统计局70w+城乡数据信息
技术路线:requests + re正则表达式
初始网站:http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2009/index.html
爬虫信息:2009年统计用区划代码和城乡划分代码,
爬取前用 http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2009/index.html/robots.txt Robots协议查看,发现网站并未限制爬虫。
数据量:70w+
所用Python版本:Python3.5
源代码如下:
import re
import requests
import operator
from functools import reduce
import csv
class TongJi(object):
def GetUrls1(self):
url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2009/index.html'
kv = {'user-agent': 'Mozilla/5.0'}
r = requests.get(url, headers=kv)
r.raise_for_status()
r.encoding = r.apparent_encoding
pattern = re.compile("<a href='(.*?)'>")
result = list(set(re.findall(pattern, r.text)))
for i in result:
yield i
def GetUrls2(self):
result1 = []
for i in self.GetUrls1():
try:
url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2009/' + i
kv = {'user-agent': 'Mozilla/5.0'}
r = requests.get(url, headers=kv)
r.raise_for_status()
r.encoding = r.apparent_encoding
pattern = re.compile("<a href='(.*?)'>")
result = list(set(re.findall(pattern, r.text)))
result1.append(result)
result2 = reduce(operator.add, result1)
except:
print(url, '爬取失败')
continue
for i in result2:
yield i
def GetUrls3(self):
result1 = []
for i in self.GetUrls2():
try:
url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2009/' + i
kv = {'user-agent': 'Mozilla/5.0'}
r = requests.get(url, headers=kv)
r.raise_for_status()
r.encoding = r.apparent_encoding
pattern = re.compile("<a href='(.*?)'>")
result = list(set(re.findall(pattern, r.text)))
result1.append(result)
result2 = reduce(operator.add, result1)
result2 = reduce(operator.add, result1)
except:
print(url, '爬取失败')
continue
for i in result2:
yield i
def GetUrls4(self):
result1 = []
for i in self.GetUrls3():
try:
url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2009/' + i[3:5] + '/' + i
kv = {'user-agent': 'Mozilla/5.0'}
r = requests.get(url, headers=kv)
r.raise_for_status()
r.encoding = r.apparent_encoding
pattern = re.compile("<a href='(.*?)'>")
result = list(set(re.findall(pattern, r.text)))
result1.append(result)
result2 = reduce(operator.add, result1)
except:
print(url, '爬取失败')
continue
for i in result2:
yield i
def GetUrls5(self):
result1 = []
for i in self.GetUrls4():
try:
url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2009/' + i[3:5] + '/' + i[5:7] + '/' + i
kv = {'user-agent': 'Mozilla/5.0'}
r = requests.get(url, headers=kv)
r.raise_for_status()
r.encoding = r.apparent_encoding
pattern = re.compile("<a href='(.*?)'>")
result = list(set(re.findall(pattern, r.text)))
result1.append(result)
result2 = reduce(operator.add, result1)
except:
print(url, '爬取失败')
continue
for i in result2:
yield i
def GetDates(self):
Dates1 = []
for i in self.GetUrls5():
try:
url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2009/' + i
kv = {'user-agent': 'Mozilla/5.0'}
r = requests.get(url, headers=kv)
r.raise_for_status()
r.encoding = r.apparent_encoding
html = r.text
pattern = re.compile(r"<tr class='villagetr'><td>(.*?)</td><td>(.*?)</td><td>(.*?)</td></tr>")
Date = re.findall(pattern, html)
Dates1.append(Date)
Dates = reduce(operator.add, Dates1)
except:
print(url, '爬取失败')
continue
for i in Dates:
yield i
def DateImport(self):
headers = ['代码', '城乡分类', '名称']
with open('数据1.csv', 'w') as f:
f_csv = csv.writer(f)
f_csv.writerow(headers)
for i in self.GetDates():
f_csv.writerows(i)
tongji = TongJi()
tongji.DateImport()
代码解析:
1.首先导入我们需要的5个模块,这5个模块均为Python3.5自带的,所以不需要pip下载。
2.创建 Class TongJi 类,把它当做一个项目。接下来就是编写类中的函数。
3.GetUrls1()函数返回一个生成器类型(用关键字yield定义,读者可自行百度查看有关该关键字的运用!),里面的元素是列表result中的元素,而result是什么呢? result = list(set(re.findall(pattern, r.text))) 是集合元素 set(re.findall(pattern, r.text)) 转化而成的列表。而set(re.findall(pattern, r.text)) 是 re.findall(pattern, r.text) 转化而成的集合,那么re.findall(pattern, r.text)又是什么呢? re.findall()是正则表达式中的一个函数,返回一个列表。参数 pattern = re.compile("<a href='(.*?)'>") 是一个匹配模式,r.text 是一个由 r = requests.get(url, headers=kv) 获取的Html内容,相当于在网站任一个空白地点击右键->查看网页源代码,弹出的网站内容。我们的
url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2009/index.html'
kv = {'user-agent': 'Mozilla/5.0'}
因为有些网站设置反爬虫,我们要将网站的头部字段改为 kv 来模拟浏览器登录,才可以获取到Html内容,即 r.text 。在刚刚查看网页源代码的网站中,我们发现,你想打开的各省份的链接包含在 <a href='...'>这个标签中,我们用正则表达式re.compile()函数 pattern = re.compile("<a href='(.*?)'>")即可得到获得各省份链接的匹配模式,用re.findall()函数找到所有一样的匹配模式返回一个列表,但是!返回的列表中同一个链接元素会出现2次,所以我们先用集合set()将重复元素删去,只保留一个,然后再用list()函数转换回列表形式。此时我们可以获得 形式为 ['11.html','12.html',.......] 这样一个列表。
4.为什么要获得上述的列表呢?因为我们要循环访问每个省的网页链接,而每个省下面又有每个市级的网页链接,每个市级的网页下面又有每个区的网页链接,每个区下面又有每个街道办事处的网页链接,最后进入每个街道办事处网页才是我们需要爬取的信息。所以我们总共要进行5次循环才能获得我们需要的信息。
5.查看上述列表,我们知道,我们并未完整的获得每个省的网页链接,但是我们获得了每个省网页链接的最后一部分字符串内容,我们只需要用一个循环,获得
url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2009/' + i
此时的 i 是每个省级网站的最后部分内容。形式为 '11.html'
即可得到每个省完整的网页链接。爬取各省完整链接,又得到一个类似3.中得到的列表 ['11/11.html','11/12.html',.......] ,元素为每个市级中最后一部分的字符串内容。我们再次用循环,获得
url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2009/' + i
此时的 i 是每个市级网站的最后部分内容。形式为 '11/11.html'
即可得到每个市完整的网页链接。循环下去,爬取各市完整链接,获得各街道办事处网站最后一部分的字符串内容,但是!!此时如果我们用
url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2009/' + i
此时的 i 是每个街道办事处网站的最后部分内容,形式为 01/110101.html
用该链接爬取的时候,是爬取不到的。为什么??
因为我们进入街道办事处的原网站,会发现网址是这样的
http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2009/11/01/110101.html
而我们获得的最后部分内容,即 i 的形式是 01/110101.html,所以如果我们用
url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2009/' + i
则url会变成是 http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2009/01/110101.html 这种形式,
我们比较原网站,发现在 '2009/01/' 里少了 '11/' 所以我们 GetUrls4() 函数中用的是
url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2009/' + i[3:5] + '/' + i
相当于 把少了的 '11/' 补上。同理 GetUrls5() 函数中用的是
url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2009/' + i[3:5] + '/' + i[5:7] + '/' + i
爬取各区完整链接,爬取各街道办事处完整链接,最后在各街道办事处网站爬取的是 代码、城乡、名称 三个信息,GetDates() 函数即返回一个元素为元组的列表,形式为
[('110101001001','111','多福巷社区居委会'),('110101001002','111','银闸社区居委会'),...]
6.最后用DateImport()函数将上述获得的列表数据依次导入 数据1.csv 文件中。
7.有些初学读者对程序中的一些语句不理解,可以自行百度相关函数用法。
8.观察我们的原始网址,可以发现。在 2009 处改成 2010 或 2011 -2018 ,有各年份的统计数据。此处有兴趣的朋友可以用类似的方法写个循环,爬取2009-2018的所有城乡数据,不过这时我们爬取的数据量就很大了,要导入到SQL数据库中。有兴趣的朋友可以查阅相关资料试一试。提醒一下,爬取的时间可能要挺久!
上一篇: English--音标拼读
下一篇: 数据类型的简单应用