Contents
  1. 1. 基于Python Urllib库和Re库的爬虫应用
    1. 1.1. 分析所需数据的位置及其上下文特征
    2. 1.2. 获取源代码
    3. 1.3. 匹配字符串
    4. 1.4. 储存数据
    5. 1.5. 完整程序
    6. 1.6. 运行结果

基于Python Urllib库和Re库的爬虫应用

获取交通部官网2019年交通固定资产投资额等数据并将其存入Excel表格中

分析所需数据的位置及其上下文特征

进入交通部官网数据网站
通过右键检查表格元素,可以发现,每一个表格都被封装在一个html文件中,他们的网址位于网页源代码的872~893行,而他们的标题则位于864~867行。
代码如下:

1
        <div class="fl w100 mod_spsj mod1_kshsj_sj mart15">
2
			<ul id="myTabs" class="nav nav-tabs hidden-sm hidden-xs" role="tablist">
3
				<li class="sj_w180625"><b>可视化数据</b></li>
4
				<li class="active"><a href="#jtgdzctzwce" role="tab" data-toggle="tab">交通固定资产投资完成额</a></li>
5
				<li><a href="#yyxkhysl" role="tab" data-toggle="tab">营业性客货运输量</a></li>
6
				<li><a href="#cskyl" role="tab" data-toggle="tab">城市客运量</a></li>
7
				<li><a href="#gkscl" role="tab" data-toggle="tab">港口生产量</a></li>
8
			</ul>
9
......
10
<div class="list-group tab-content mod1_kshsj1_sj">
11
	<div role="tabpanel" class="tab-pane active" id="jtgdzctzwce">
12
		<div class="fl w100 mod1_kshsj1_a_sj">
13
			<IFRAME align="top" width="1110px" height="820px" frameborder="0" src="http://106.3.149.173/CatsCategory/listGLJCSJ5?tableName=cats_jtysybsjk_csky&pubflag=1&code=C13" scrolling="no" ></IFRAME>
14
		</div>
15
	</div>
16
	<div role="tabpanel" class="tab-pane" id="yyxkhysl">
17
		<div class="fl w100 mod1_kshsj1_a_sj">
18
			<IFRAME align="top" width="1110px" height="820px" frameborder="0" src="http://106.3.149.173/CatsCategory/listGLJCSJ5?tableName=cats_jtysybsjk_gksc&pubflag=1&code=C13" scrolling="no" ></IFRAME>
19
		</div>
20
	</div>
21
	<div role="tabpanel" class="tab-pane" id="cskyl">
22
		<div class="fl w100 mod1_kshsj1_a_sj">
23
			<IFRAME align="top" width="1110px" height="820px" frameborder="0" src="http://106.3.149.173/CatsCategory/listGLJCSJ5?tableName=cats_jtysybsjk_jtgdzctzwce&pubflag=1&code=C13" scrolling="no" ></IFRAME>
24
		</div>
25
	</div>
26
	<div role="tabpanel" class="tab-pane" id="gkscl">
27
		<div class="fl w100 mod1_kshsj1_a_sj">
28
			<IFRAME align="top" width="1110px" height="820px" frameborder="0" src="http://106.3.149.173/CatsCategory/listGLJCSJ5?tableName=cats_jtysybsjk_yyxkhysl&pubflag=1&code=C13" scrolling="no" ></IFRAME>
29
		</div>
30
	</div>
31
</div>

可见,上述网址前都具有属性class="fl w100 mod1_kshsj1_a_sj"故可以使用正则表达式<div class="fl w100 mod1_kshsj1_a_sj">.*?src="(.*?)"\s.*?</div>进行识别。
标题与此类似,不再赘述。

进入每一个表格的源代码,发现每一格中的数据都被<td></td>所包围,故可以使用正则表达式<td>(.*?)</td>识别。表头与之类似。

分析好了上下文,就可以进入编程环节了。

获取源代码


使用urllib库中的urllib.request.urlopen(url)方法获取网页信息该方法接受一个字符串,返回一个http.client.HTTPResponse对象,该对象的read()方法返回一个字符串,即网页的源代码。通过decode('utf-8')方法使源代码按照UTF-8标准编码。

1
response = urllib.request.urlopen(url)
2
    src = response.read().decode('utf-8')

匹配字符串


通过re库中的re.compile()方法将正则表达式编译,并在第二个参数位置加上re.S以确保查找不会被换行符所中断。使用re.findall()方法找出源代码中所有符合匹配模式的字符串,并将其以列表的形式储存。为方便编写代码,我编写了一个get_data()函数整合了这个过程

1
def get_data(url, pattern):
2
    response = urllib.request.urlopen(url)
3
    src = response.read().decode('utf-8')
4
    results = re.findall(pattern, src)
5
    return results

储存数据


通过openpyxl库储存数据。
我编写了fill_sheet()函数以实现这一过程。该函数接收文件对象,工作表名,要填写数据的区域左上角和右下角的坐标,以及数据来源和开始位置(默认为0),填写表格后返回最后填写的一个数据的下一位置。

1
def fill_sheet(workbook, sheetname, pos1, pos2, src_text, reading_pos=0):
2
    if sheetname not in workbook:  # 若工作表不存在,则创建一个
3
        workbook.create_sheet(sheetname)
4
    sh = workbook[sheetname]
5
    workbook.close() # 关闭文件后才能写入
6
    for i in range(pos1[0], pos2[0]+1):
7
        for j in range(pos1[1], pos2[1]+1):
8
            sh.cell(row=i, column=j, value=src_text[reading_pos])  # 在相应单元格中写入数据
9
            reading_pos += 1
10
    return reading_pos

完整程序


1
import urllib.request
2
import re
3
import openpyxl
4
5
6
def get_data(url, pattern):
7
    response = urllib.request.urlopen(url)
8
    src = response.read().decode('utf-8')  # 获取源代码
9
    results = re.findall(pattern, src)  # 找出所有匹配的结果
10
    return results
11
12
13
def fill_sheet(workbook, sheetname, pos1, pos2, src_text, reading_pos=0):
14
    if sheetname not in workbook:  # 如果工作表不存在,则创建一个
15
        workbook.create_sheet(sheetname)
16
    sh = workbook[sheetname]  # 获取工作表对象
17
    workbook.close()  # 关闭文件后才能写入
18
    for i in range(pos1[0], pos2[0]+1):
19
        for j in range(pos1[1], pos2[1]+1):
20
            sh.cell(row=i, column=j, value=src_text[reading_pos])  # 在对应单元格中写入数据
21
            reading_pos += 1
22
    return reading_pos
23
24
25
def main():
26
    url = 'http://www.mot.gov.cn/shuju/'
27
    # 编译各种所需的正则表达式
28
    pattern_get_graph_sites = re.compile(r'<div class="fl w100 mod1_kshsj1_a_sj">.*?src="(.*?)"\s.*?</div>', re.S)  # 图表网址
29
    pattern_get_datas = re.compile('<td>(.*?)</td>', re.S)  # 表中数据
30
    pattern_get_graph_headers = re.compile('<th style="width:120px">(.*?)</th>', re.S)  # 表头
31
    pattern_get_graph_names_block = re.compile('<div class="fl w100 mod_spsj mod1_kshsj_sj mart15">(.*?)</ul>', re.S)  # 标题代码区块
32
    pattern_get_graph_names = re.compile('<a.*?>(.*?)</a>', re.S)  # 图表标题
33
    # 匹配数据
34
    graph_names_block = get_data(url, pattern_get_graph_names_block)
35
    graph_sites = get_data(url, pattern_get_graph_sites)
36
    graph_names = re.findall(pattern_get_graph_names, graph_names_block[0])
37
    datas = []
38
    graph_headers = []
39
    for graph_site in graph_sites:
40
        datas.append(get_data(graph_site, pattern_get_datas))
41
        graph_headers.append(get_data(graph_site, pattern_get_graph_headers))
42
    # 储存数据
43
    wb = openpyxl.Workbook()
44
    for i in range(0, len(graph_names)):
45
        len_gh = len(graph_headers[i])
46
        fill_sheet(wb, graph_names[i], (1, 1), (1, len_gh), graph_headers[i])
47
        fill_sheet(wb, graph_names[i], (2, 1), (int(len(datas[i])/len_gh+1), len_gh), datas[i])
48
    del wb['Sheet']  # 删除默认工作表
49
    wb.save('results.xlsx')  # 保存文件
50
51
52
if __name__ == '__main__':  # 执行主函数
53
    main()

运行结果


表1
表2
表3
表4

Contents
  1. 1. 基于Python Urllib库和Re库的爬虫应用
    1. 1.1. 分析所需数据的位置及其上下文特征
    2. 1.2. 获取源代码
    3. 1.3. 匹配字符串
    4. 1.4. 储存数据
    5. 1.5. 完整程序
    6. 1.6. 运行结果