欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

利用Python爬虫,只需要会requests + re 即可抓取中国国家统计局70w+城乡数据信息

程序员文章站 2022-05-04 16:40:31
...

技术路线: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数据库中。有兴趣的朋友可以查阅相关资料试一试。提醒一下,爬取的时间可能要挺久!