有一段时间没有更新博客了,其中原因很多,先是接下了一个公司的工作,当时项目很急,也经常加班,也造成了没有时间去更新博客,后面因为工作中的一些理念不同,我也离开了那个公司到别的地方去工作了,换了新工作很多东西需要去学习和熟悉,换了工作也换了住的地方,这也花费了很多的时间去处理。本来这篇文章在过年的时候就想写的了,结果一晃都半年过去了,现在才动笔。
首先还是老规矩,做爬虫,先分析网络请求。其中的目标URL是深圳交易所专题统计,我们要爬取的就是其中的主要财务指标,如图所示.
这样看到这些报告的链接,很多都直接是指向文件的链接,这意味着我们如果直接访问这些链接就相当于是直接访问文件了,如果要保存到本地的话我们只需要用二进制讲访问返回的内容写到本地即可。多翻几页能看出来,文件都是doc,docx,pdf,xlsx,还有shtml之类的组成的,这些不影响,还是采取使用二进制写进到本地进行存储,得到文件之后再进行处理。
我们再往下看,发现一共有10页,我们尝试点击翻页,看看浏览器上面的链接有没有上面变化。结果我们发现,并没有在url中暴露翻页的信息。
如此这般,那么只只好抓包分析了,上F12,然后点解下一页分析一下请求的情况。
这样看来就很明显了,请求不是很多,不得不说金融行业真是话不多,但是说话都命中要害,减少了很多不必要的开销。看图我们发现是一个post请求,让后返回了一个HTML页面,我们可以看到返回的HTML中有第二页所出现的报告的名字和相应的链接,那么我们也不用去分析别的链接了,就是这个链接了,下面进行分析。
我们可以看到,翻页的时候触发了一个js,再加上一个随机的浮点型数字,我当时翻了一下网页中的代码,奈何金融行业比较社会,人狠话不多,基本没看懂这个随机数是怎么来的,并且每次点击翻页的随机数都不一样,从这个地方看,我基本就放弃使用requests或是urllib来进行访问了。不过爬虫与反爬就是这样子,你有张良计,我有过墙梯,直接使用python发包不行的话就只能借助别的工具来进行访问了。
介绍一个小工具,phantomjs,网上有很多这个工具的教程,重复phantomjs入门使用就不在这里赘述了,关于这个工具,大家可以理解成一个没有界面的浏览器即可。这个小工具下载好了直接双击,然后就会自动在系统里面了,使用的时候只需要将phantomJS和脚本放在一起就行了。
首先,我们先来实例化一个phantomJS无头浏览器。
1 driver = webdriver.PhantomJS()
因为我们第一次打开目标URL的时候其实时用的get请求,于是我们也可以尝试使用phantomJS来进行get请求。
12 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进行请求发包了。来看看下面这段代码。
123456789101112131415161718192021222324 # 获取文件链接,此处doc包括但不限于doc文件,还报考页面中所有的链接,# 当然,更正确的叫法应该get_url,但是当时只想到了链接直接指向文档,就get_文档了def get_doc(html):source = htmlbsContent = 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 + urldownload(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,因为是调用这两个应用程序的组件进行转换的,此处需要声明一下。多的不说的,代码的注释还是比较详细的,我直接讲代码放出。
整个脚本的代码如下
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 # -*-coding:'utf-8' -*-import requestsfrom bs4 import BeautifulSoupimport osimport win32comfrom win32com.client import Dispatchfrom win32com import clientfrom docx import Documentimport xlrdfrom selenium import webdriverimport timefrom pdfminer.pdfparser import PDFParser,PDFDocumentfrom pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreterfrom pdfminer.converter import PDFPageAggregatorfrom pdfminer.layout import LTTextBoxHorizontal,LAParamsfrom pdfminer.pdfinterp import PDFTextExtractionNotAlloweddomain_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_sourcefor 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 = htmlbsContent = 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 + urldownload(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:passdef translate_doc(f):"""将doc文件转换为docx"""word = client.Dispatch('kwps.Application') #如果电脑安装是MS office,应该使用word.Applicationword.Visible = 0doc = 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).textstock_name = t.cell(row,1).textprofits = t.cell(row,2).textper_earn = t.cell(row,3).textper_profits=t.cell(row,4).textper_cash=t.cell(row,5).textif t.cell(row,6).text=='':r_plant ='无预案'else:r_plant=t.cell(row,6).textprint(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 PDFTextExtractionNotAllowedelse:# 创建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].valuedeal_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()最后实现的效果是可以进行增量爬取,因为已经爬取过的都会被记录下来,然后在获取到相同链接的时候忽略掉该链接的下载和解析,并且将文件都解析成字符串,如何分析,此处便是后话了。下图是当时爬取的一些记录。