Obtaining Data From the MOT Official Website by Python Module Urllib and Re
基于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() |