1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > python爬虫:Selenium 爬取东方财富网上市公司财务报表

python爬虫:Selenium 爬取东方财富网上市公司财务报表

时间:2018-10-13 07:56:23

相关推荐

python爬虫:Selenium 爬取东方财富网上市公司财务报表

前言

本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理。

PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取

python免费学习资料以及群交流解答点击即可加入

1. 实战背景

很多网站都提供上市公司的公告、财务报表等金融投资信息和数据,比如:腾讯财经、网易财经、新浪财经、东方财富网等。这之中,发现东方财富网的数据非常齐全。

东方财富网有一个数据中心:/center/,该数据中心提供包括特色数据、研究报告、年报季报等在内的大量数据(见下图)。

以年报季报类别为例,我们点开该分类查看一下中报(见下图),可以看到该分类下又包括:业绩报表、业绩快报、利润表等7个报表的数据。以业绩报表为例,报表包含全部3000多只股票的业绩报表数据,一共有70多页。

假如,我们想获取所有股票中的业绩报表数据,然后对该数据进行一些分析。采取手动复制的方法,70多页可以勉强完成。但如果想获取任意一年、任意季度、任意报表的数据,要再通过手动复制的方法,工作量会非常地大。举个例子,假设要获取间(40个季度)、所有7个报表的数据,那么手动复制的工作量大约将是:40×7×70(每个报表大约70页),差不多要重复性地复制2万次!!!可以说是人工不可能完成的任务。所以,本文的目标就是利用Selenium自动化技术,爬取年报季报类别下,任意一年(网站有数据至今)、任意财务报表数据。我们所需要做的,仅是简单输入几个字符,其他就全部交给电脑,然后过一会儿打开excel,就可以看到所需数据"静静地躺在那里",是不是挺酷的?

好,下面我们就开始实操一下。首先,需要分析要爬取的网页对象。

2. 网页分析

之前,我们已经爬过表格型的数据,所以对表格数据的结构应该不会太陌生。

我们这里以上面的中报的业绩报表为例,查看一下表格的形式。

网址url:/bbsj/06/lrb.html,bbsj代表年报季报,03代表一季报,类似地,06表示年中报;lrb是利润表的首字母缩写,同理,yjbb表示业绩报表。可以看出,该网址格式很简单,便于构造url。

接着,我们点击下一页按钮,可以看到表格更新后url没有发生改变,可以判定是采用了Javscript。那么,我们首先判断是不是采用了Ajax加载的。方法也很简单,右键检查或按F12,切换到network并选择下面的XHR,再按F5刷新。可以看到只有一个Ajax请求,点击下一页也并没有生成新的Ajax请求,可以判断该网页结构不是常见的那种点击下一页或者下拉会源源不断出现的Ajax请求类型,那么便无法构造url来实现分页爬取。

XHR选项里没有找到我们需要的请求,接下来试试看能不能再JS里找到表格的数据请求。将选项选为JS,再次F5刷新,可以看到出现了很多JS请求,然后我们点击几次下一页,会发现弹出新的请求来,然后右边为响应的请求信息。url链接非常长,看上去很复杂。好,这里我们先在这里打住不往下了。

可以看到,通过分析后台元素来爬取该动态网页的方法,相对比较复杂。那么有没有干脆、直截了当地就能够抓取表格内容的方法呢?有的,就是本文接下来要介绍的Selenium大法。

3. Selenium知识

Selenium 是什么?一句话,自动化测试工具。它是为了测试而出生的,但在近几年火热的爬虫领域中,它摇身一变,变成了爬虫的利器。直白点说, Seleninm能控制浏览器, 像人一样"上网"。比如,可以实现网页自动翻页、登录网站、发送邮件、下载图片/音乐/视频等等。举个例子,写几行python代码就可以用Selenium实现登录IT桔子,然后浏览网页的功能。

只需要记住重要的一点就是:Selenium能做到"可见即可爬"。也就是说网页上你能看到的东西,Selenium基本上都能爬取下来。包括上面我们提到的东方财富网的财务报表数据,它也能够做到,而且非常简单直接,不用去后台查看用了什么JavaScript技术或者Ajax参数。下面我们就实际来操练下吧

编码实现

思路

安装配置好Selenium运行的相关环境,浏览器可以用Chrome、Firefox、PhantomJS等,我用的是Chrome;东方财富网的财务报表数据不用登录可直接获得,Selenium更加方便爬取;先以单个网页中的财务报表为例,表格数据结构简单,可先直接定位到整个表格,然后一次性获取所有td节点对应的表格单元内容;接着循环分页爬取所有上市公司的数据,并保存为csv文件。重新构造灵活的url,实现可以爬取任意时期、任意一张财务报表的数据。

根据上述思路,下面就用代码一步步来实现。

爬取单页表格

我们先以中报的利润表为例,抓取该网页的第一页表格数据,网页url:/bbsj/06/lrb.html

快速定位到表格所在的节点:id = dt_1,然后可以用Selenium进行抓取了,方法如下:

1from selenium import webdriver2browser = webdriver.Chrome()3# 当测试好能够顺利爬取后,为加快爬取速度可设置无头模式,即不弹出浏览器4# 添加无头headlesss 1使用chrome headless,2使用PhantomJS5# 使用 PhantomJS 会警告高不建议使用phantomjs,建议chrome headless6# chrome_options = webdriver.ChromeOptions()7# chrome_options.add_argument('--headless')8# browser = webdriver.Chrome(chrome_options=chrome_options)9# browser = webdriver.PhantomJS()10# browser.maximize_window() # 最大化窗口,可以选择设置1112browser.get('/bbsj/06/lrb.html')13element = browser.find_element_by_css_selector('#dt_1') # 定位表格,element是WebElement类型14# 提取表格内容td15td_content = element.find_elements_by_tag_name("td") # 进一步定位到表格内容所在的td节点16lst = [] # 存储为list17for td in td_content:18 lst.append(td.text)19print(lst) # 输出表格内容

完整代码

1from selenium import webdriver2from mon.exceptions import TimeoutException3from mon.by import By4from selenium.webdriver.support import expected_conditions as EC5from selenium.webdriver.support.wait import WebDriverWait6import time7import pandas as pd8import os910# 先chrome,后phantomjs11# browser = webdriver.Chrome()12# 添加无头headlesss13chrome_options = webdriver.ChromeOptions()14chrome_options.add_argument('--headless')15browser = webdriver.Chrome(chrome_options=chrome_options)1617# browser = webdriver.PhantomJS() # 会报警高提示不建议使用phantomjs,建议chrome添加无头18browser.maximize_window() # 最大化窗口19wait = WebDriverWait(browser, 10)def index_page(page):22 try:23 print('正在爬取第: %s 页' % page)24 wait.until(25 EC.presence_of_element_located((By.ID, "dt_1")))26 # 判断是否是第1页,如果大于1就输入跳转,否则等待加载完成。27 if page > 1:28 # 确定页数输入框29 input = wait.until(EC.presence_of_element_located(30(By.XPATH, '//*[@id="PageContgopage"]')))31 input.click()32 input.clear()33 input.send_keys(page)34 submit = wait.until(EC.element_to_be_clickable(35(By.CSS_SELECTOR, '#PageCont > a.btn_link')))36 submit.click()37 time.sleep(2)38 # 确认成功跳转到输入框中的指定页39 wait.until(EC.text_to_be_present_in_element(40 (By.CSS_SELECTOR, '#PageCont > span.at'), str(page)))41 except Exception:42 return None4344def parse_table():45 # 提取表格第一种方法46 # element = wait.until(EC.presence_of_element_located((By.ID, "dt_1")))47 # 第二种方法48 element = browser.find_element_by_css_selector('#dt_1')4950 # 提取表格内容td51 td_content = element.find_elements_by_tag_name("td")52 lst = []53 for td in td_content:54 # print(type(td.text)) # str55 lst.append(td.text)5657 # 确定表格列数58 col = len(element.find_elements_by_css_selector('tr:nth-child(1) td'))59 # 通过定位一行td的数量,可获得表格的列数,然后将list拆分为对应列数的子list60 lst = [lst[i:i + col] for i in range(0, len(lst), col)]6162 # 原网页中打开"详细"链接,可以查看更详细的数据,这里我们把url提取出来,方便后期查看63 lst_link = []64 links = element.find_elements_by_css_selector('#dt_1 a.red')65 for link in links:66 url = link.get_attribute('href')67 lst_link.append(url)6869 lst_link = pd.Series(lst_link)70 # list转为dataframe71 df_table = pd.DataFrame(lst)72 # 添加url列73 df_table['url'] = lst_link7475 # print(df_table.head())76 return df_table7778# 写入文件79def write_to_file(df_table, category):80 # 设置文件保存在D盘eastmoney文件夹下81 file_path = 'D:\\eastmoney'82 if not os.path.exists(file_path):83 os.mkdir(file_path)84 os.chdir(file_path)85 df_table.to_csv('{}.csv' .format(category), mode='a',86encoding='utf_8_sig', index=0, header=0)8788# 设置表格获取时间、类型89def set_table():90 print('*' * 80)91 print('\t\t\t\t东方财富网报表下载')92 print('作者:高级农民工 .10.6')93 print('--------------')9495 # 1 设置财务报表获取时期96 year = int(float(input('请输入要查询的年份(四位数-):\n')))97 # int表示取整,里面加float是因为输入的是str,直接int会报错,float则不会98 # /questions/1841565/valueerror-invalid-literal-for-int-with-base-1099 while (year < or year > ):100 year = int(float(input('年份数值输入错误,请重新输入:\n')))101102 quarter = int(float(input('请输入小写数字季度(1:1季报,2-年中报,3:3季报,4-年报):\n')))103 while (quarter < 1 or quarter > 4):104 quarter = int(float(input('季度数值输入错误,请重新输入:\n')))105106 # 转换为所需的quarter 两种方法,2表示两位数,0表示不满2位用0补充,107 # /python/att-string-format.html108 quarter = '{:02d}'.format(quarter * 3)109 # quarter = '%02d' %(int(month)*3)110 date = '{}{}' .format(year, quarter)111 # print(date) 测试日期 ok112113 # 2 设置财务报表种类114 tables = int(115 input('请输入查询的报表种类对应的数字(1-业绩报表;2-业绩快报表:3-业绩预告表;4-预约披露时间表;5-资产负债表;6-利润表;7-现金流量表): \n'))116117 dict_tables = {1: '业绩报表', 2: '业绩快报表', 3: '业绩预告表',118 4: '预约披露时间表', 5: '资产负债表', 6: '利润表', 7: '现金流量表'}119 dict = {1: 'yjbb', 2: 'yjkb/13', 3: 'yjyg',120 4: 'yysj', 5: 'zcfz', 6: 'lrb', 7: 'xjll'}121 category = dict[tables]122123 # 3 设置url124 # url = '/bbsj/03/lrb.html' eg.125 url = '/{}/{}/{}.html' .format(126 'bbsj', date, category)127128 # # 4 选择爬取页数范围129 start_page = int(input('请输入下载起始页数:\n'))130 nums = input('请输入要下载的页数,(若需下载全部则按回车):\n')131 print('*' * 80)132133 # 确定网页中的最后一页134 browser.get(url)135 # 确定最后一页页数不直接用数字而是采用定位,因为不同时间段的页码会不一样136 try:137 page = browser.find_element_by_css_selector('.next+ a') # next节点后面的a节点138 except:139 page = browser.find_element_by_css_selector('.at+ a')140 # else:141 #print('没有找到该节点')142 # 上面用try.except是因为绝大多数页码定位可用'.next+ a',但是业绩快报表有的只有2页,无'.next+ a'节点143 end_page = int(page.text)144145 if nums.isdigit():146 end_page = start_page + int(nums)147 elif nums == '':148 end_page = end_page149 else:150 print('页数输入错误')151 # 输入准备下载表格类型152 print('准备下载:{}-{}' .format(date, dict_tables[tables]))153 print(url)154 yield{155 'url': url,156 'category': dict_tables[tables],157 'start_page': start_page,158 'end_page': end_page159 }160161def main(category, page):162 try:163 index_page(page)164 # parse_table() #测试print165 df_table = parse_table()166 write_to_file(df_table, category)167 print('第 %s 页抓取完成' % page)168 print('--------------')169 except Exception:170 print('网页爬取失败,请检查网页中表格内容是否存在')171# 单进程172if __name__ == '__main__':173174 for i in set_table():175 # url = i.get('url')176 category = i.get('category')177 start_page = i.get('start_page')178 end_page = i.get('end_page')179180 for page in range(start_page, end_page):181 # for page in range(44,pageall+1): # 如果下载中断,可以尝试手动更改网页继续下载182 main(category, page)183 print('全部抓取完成')

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。