爬取深圳证券交易所专题统计

有一段时间没有更新博客了,其中原因很多,先是接下了一个公司的工作,当时项目很急,也经常加班,也造成了没有时间去更新博客,后面因为工作中的一些理念不同,我也离开了那个公司到别的地方去工作了,换了新工作很多东西需要去学习和熟悉,换了工作也换了住的地方,这也花费了很多的时间去处理。本来这篇文章在过年的时候就想写的了,结果一晃都半年过去了,现在才动笔。

首先还是老规矩,做爬虫,先分析网络请求。其中的目标URL是深圳交易所专题统计,我们要爬取的就是其中的主要财务指标,如图所示.
CbqQUO.png

这样看到这些报告的链接,很多都直接是指向文件的链接,这意味着我们如果直接访问这些链接就相当于是直接访问文件了,如果要保存到本地的话我们只需要用二进制讲访问返回的内容写到本地即可。多翻几页能看出来,文件都是doc,docx,pdf,xlsx,还有shtml之类的组成的,这些不影响,还是采取使用二进制写进到本地进行存储,得到文件之后再进行处理。
Cbqwa8.png

我们再往下看,发现一共有10页,我们尝试点击翻页,看看浏览器上面的链接有没有上面变化。结果我们发现,并没有在url中暴露翻页的信息。
CbqDPg.png

如此这般,那么只只好抓包分析了,上F12,然后点解下一页分析一下请求的情况。
CbqrGQ.png

这样看来就很明显了,请求不是很多,不得不说金融行业真是话不多,但是说话都命中要害,减少了很多不必要的开销。看图我们发现是一个post请求,让后返回了一个HTML页面,我们可以看到返回的HTML中有第二页所出现的报告的名字和相应的链接,那么我们也不用去分析别的链接了,就是这个链接了,下面进行分析。

CbqTz9.png

我们可以看到,翻页的时候触发了一个js,再加上一个随机的浮点型数字,我当时翻了一下网页中的代码,奈何金融行业比较社会,人狠话不多,基本没看懂这个随机数是怎么来的,并且每次点击翻页的随机数都不一样,从这个地方看,我基本就放弃使用requests或是urllib来进行访问了。不过爬虫与反爬就是这样子,你有张良计,我有过墙梯,直接使用python发包不行的话就只能借助别的工具来进行访问了。

介绍一个小工具,phantomjs,网上有很多这个工具的教程,重复phantomjs入门使用就不在这里赘述了,关于这个工具,大家可以理解成一个没有界面的浏览器即可。这个小工具下载好了直接双击,然后就会自动在系统里面了,使用的时候只需要将phantomJS和脚本放在一起就行了。

首先,我们先来实例化一个phantomJS无头浏览器。

1
driver = webdriver.PhantomJS()

CbOpkT.md.png

因为我们第一次打开目标URL的时候其实时用的get请求,于是我们也可以尝试使用phantomJS来进行get请求。

1
2
driver.get("http://www.szse.cn/main/marketdata/zttj_front/") #coding='ISO-8859-1'
html = driver.page_source

我们发现,在这段代码中可以获取正确的HTML,于是我们再尝试定位到下一页的按钮,尝试是否可以正常点击下一页。

1
driver.find_element_by_xpath('//*[@id="Table1"]/tbody/tr/td[3]/input[2]').click()

运行之后发现,在phantomJS的click()执行之后,确实是可以获取第二页的HTML,那么翻页暂时来说不是问题了,下面的问题就是怎么把每一页的链接获取到,然后下载文件到本地。我们在网页上点击文件的链接,发现可以直接打开一些文件,这时我们就可以在获取文件链接之后使用python进行请求发包了。来看看下面这段代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 获取文件链接,此处doc包括但不限于doc文件,还报考页面中所有的链接,
# 当然,更正确的叫法应该get_url,但是当时只想到了链接直接指向文档,就get_文档了
def get_doc(html):
source = html
bsContent = BeautifulSoup(source, 'html.parser')
urlContent = bsContent.find_all(class_='tdline2')
for i in urlContent:
a = i.find("a")
url = a.get('href')
download_url = domain_url + url
download(download_url)
# 下载文件
def download(download_url):
# 记录下载过的文档的链接
with open(file='url_log.txt',mode='r+') as fp:
log=fp.read()
if download_url not in log:
r = requests.get(url=download_url)
with open(file=download_url[download_url.rfind('/') + 1:], mode="wb") as code:
code.write(r.content)
fp.write(download_url + "\n")
else:
pass

这样的话文件是下载下来的,但是也仅仅是下载下来了,里面的信息没有办法提取出来,关于这几种文件在python中怎么解析,相关的模块的文档也有,这里也不去介绍入门了。这里有一点需要注意的是,因为doc的兼容性问题,我们可以将其转换成docx再来进行读取,这个地方需要电脑安装了MS office或是wps,因为是调用这两个应用程序的组件进行转换的,此处需要声明一下。多的不说的,代码的注释还是比较详细的,我直接讲代码放出。

整个脚本的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# -*-coding:'utf-8' -*-
import requests
from bs4 import BeautifulSoup
import os
import win32com
from win32com.client import Dispatch
from win32com import client
from docx import Document
import xlrd
from selenium import webdriver
import time
from pdfminer.pdfparser import PDFParser,PDFDocument
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LTTextBoxHorizontal,LAParams
from pdfminer.pdfinterp import PDFTextExtractionNotAllowed
domain_url = 'http://www.szse.cn'
# 分析网页,下载文件
def parse_html():
driver = webdriver.PhantomJS()
driver.get("http://www.szse.cn/main/marketdata/zttj_front/") # coding='ISO-8859-1'
html = driver.page_source
for i in range(0,10):
if i ==0:
get_doc(html=html)
else:
try:
driver.find_element_by_xpath('//*[@id="Table1"]/tbody/tr/td[3]/input[2]').click()
time.sleep(3)
get_doc(driver.page_source)
except Exception as e:
break
# 获取文件链接,此处doc包括但不限于doc文件,还报考页面中所有的链接,
# 当然,更正确的叫法应该get_url,但是当时只想到了链接直接指向文档,就get_文档了
def get_doc(html):
source = html
bsContent = BeautifulSoup(source, 'html.parser')
urlContent = bsContent.find_all(class_='tdline2')
for i in urlContent:
a = i.find("a")
url = a.get('href')
download_url = domain_url + url
download(download_url)
# 下载文件
def download(download_url):
with open(file='url_log.txt',mode='r+') as fp:
log=fp.read()
if download_url not in log:
r = requests.get(url=download_url)
with open(file=download_url[download_url.rfind('/') + 1:], mode="wb") as code:
code.write(r.content)
fp.write(download_url + "\n")
else:
pass
def translate_doc(f):
"""
将doc文件转换为docx
"""
word = client.Dispatch('kwps.Application') #如果电脑安装是MS office,应该使用word.Application
word.Visible = 0
doc = word.Documents.Open(f) # 目标路径下的文件
doc.SaveAs2(f[0:-3]+'docx',12) # 转化后路径下的文件
doc.Close()
word.Quit()
parse_docx(f[0:-3]+'docx')
def parse_docx(f):
"""
读取docx中的表格
"""
d = Document(f)
t = d.tables[0]
rows=len(t._cells)
try:
for row in range (1,(rows//t._column_count)+1):
stock_number = t.cell(row,0).text
stock_name = t.cell(row,1).text
profits = t.cell(row,2).text
per_earn = t.cell(row,3).text
per_profits=t.cell(row,4).text
per_cash=t.cell(row,5).text
if t.cell(row,6).text=='':
r_plant ='无预案'
else:
r_plant=t.cell(row,6).text
print(stock_number, stock_name, profits, per_earn, per_profits, per_cash, r_plant)
except Exception as e:
print(e)
def parse_docx2(f):
'''
此函数作用是读取纯文本docx文件
:param f:文件参数
:return:
'''
document = Document(f)
for paragraph in document.paragraphs:
print(paragraph.text)
def parse_pdf(f):
fp = open(f, 'rb') # 以二进制读模式打开
#用文件对象来创建一个pdf文档分析器
praser = PDFParser(fp)
# 创建一个PDF文档
doc = PDFDocument()
# 连接分析器 与文档对象
praser.set_document(doc)
doc.set_parser(praser)
# 提供初始化密码
# 如果没有密码 就创建一个空的字符串
doc.initialize()
# 检测文档是否提供txt转换,不提供就忽略
if not doc.is_extractable:
raise PDFTextExtractionNotAllowed
else:
# 创建PDf 资源管理器 来管理共享资源
rsrcmgr = PDFResourceManager()
# 创建一个PDF设备对象
laparams = LAParams()
device = PDFPageAggregator(rsrcmgr, laparams=laparams)
# 创建一个PDF解释器对象
interpreter = PDFPageInterpreter(rsrcmgr, device)
# 循环遍历列表,每次处理一个page的内容
for page in doc.get_pages(): # doc.get_pages() 获取page列表
interpreter.process_page(page)
# 接受该页面的LTPage对象
layout = device.get_result()
# 这里layout是一个LTPage对象 里面存放着 这个page解析出的各种对象 一般包括LTTextBox, LTFigure, LTImage, LTTextBoxHorizontal 等等 想要获取文本就获得对象的text属性,
for x in layout:
if (isinstance(x, LTTextBoxHorizontal)):
with open(str(f)[:-3]+'txt', 'wb') as f:
results = x.get_text()
print(results)
f.write(results + '\n')
def parse_excel(f):
workbook=xlrd.open_workbook(f)
for sheet_name in workbook.sheet_names():
sheet=workbook.sheet_by_name(sheet_name)
for i in range(1, sheet.nrows):
name = sheet.row(i)[1].value
deal_money = str(sheet.row(i)[2].value)
print(name, deal_money)
def parse_shtml(f):
with open(f, 'r') as fp:
shtml = fp.read()
p = BeautifulSoup(shtml, 'html.parser')
biaozhi = p.find_all(class_='MsoNormal')
for i in biaozhi:
print(i.get_text(), )
def run():
# 遍历文件
PATH = r'D:\huarun\BankSpider_self\szse_spider' # windows文件路径
files = os.listdir(PATH)
for doc in files:
if os.path.splitext(doc)[1] == '.docx':
try:
parse_docx(PATH + '\\' + doc)
except Exception as e:
parse_docx2(PATH + '\\' + doc)
elif os.path.splitext(doc)[1] == '.doc' or os.path.splitext(doc)[1]=='.DOC':
try:
translate_doc(PATH + '\\' + doc)
except Exception as e:
print(e)
elif os.path.splitext(doc)[1]=='.pdf':
try:
parse_pdf(PATH+'\\'+doc)
except Exception as e:
print(e)
elif os.path.splitext(doc)[1]=='.xls':
try:
parse_excel(PATH+'\\'+doc)
except Exception as e:
print(e)
elif os.path.splitext(doc)[1]=='.shtml':
try:
parse_shtml(PATH+'\\'+doc)
except Exception as e:
print(e)
if __name__ == "__main__":
parse_html()
# run()

最后实现的效果是可以进行增量爬取,因为已经爬取过的都会被记录下来,然后在获取到相同链接的时候忽略掉该链接的下载和解析,并且将文件都解析成字符串,如何分析,此处便是后话了。下图是当时爬取的一些记录。

CbjSWF.md.png

本文地址:http://damiantuan.xyz/2018/06/09/爬取深圳证券交易所专题统计/
转载请注明出处,谢谢!

坚持原创技术分享,您的支持将鼓励我继续创作!
-------------本文结束感谢您的阅读-------------