本文轉(zhuǎn)載自一下網(wǎng)站:Python爬蟲(5):Selenium 爬取東方財(cái)富網(wǎng)股票財(cái)務(wù)報(bào)表 https://www./web_scraping_withpython5.html 需要學(xué)習(xí)的地方: 1.Selenium的安裝,配置 2.Selenium的初步使用(自動(dòng)翻頁) 利用Selenium爬取東方財(cái)富網(wǎng)各上市公司歷年的財(cái)務(wù)報(bào)表數(shù)據(jù)。 摘要: 現(xiàn)在很多網(wǎng)頁都采取JavaScript進(jìn)行動(dòng)態(tài)渲染,其中包括Ajax技術(shù)。上一篇文章通過分析Ajax接口數(shù)據(jù),順利爬取了澎湃新聞網(wǎng)動(dòng)態(tài)網(wǎng)頁中的圖片。但有的網(wǎng)頁雖然也Ajax技術(shù),但接口參數(shù)可能是加密的無法直接獲得,比如淘寶;有的動(dòng)態(tài)網(wǎng)頁也采用JavaScript,但不是Ajax技術(shù),比如Echarts官網(wǎng)。所以,當(dāng)遇到這兩類網(wǎng)頁時(shí),上一篇文章中的方法便不再奏效,需要新的采取新的方法,這其中包括干脆、直接、好用的的Selenium大法。東方財(cái)富網(wǎng)的財(cái)務(wù)報(bào)表網(wǎng)頁也是通過JavaScript動(dòng)態(tài)加載的,本文利用Selenium方法爬取該網(wǎng)站上市公司的財(cái)務(wù)報(bào)表數(shù)據(jù)。 [TOC] 1. 實(shí)戰(zhàn)背景很多網(wǎng)站都提供上市公司的公告、財(cái)務(wù)報(bào)表等金融投資信息和數(shù)據(jù),比如:騰訊財(cái)經(jīng)、網(wǎng)易財(cái)經(jīng)、新浪財(cái)經(jīng)、東方財(cái)富網(wǎng)等。這之中,發(fā)現(xiàn)東方財(cái)富網(wǎng)的數(shù)據(jù)非常齊全。 東方財(cái)富網(wǎng) 有一個(gè)數(shù)據(jù)中心:http://data.eastmoney.com/center/,該數(shù)據(jù)中心提供包括特色數(shù)據(jù)、研究報(bào)告、年報(bào)季報(bào)等在內(nèi)的大量數(shù)據(jù)(見下圖)。
以年報(bào)季報(bào)類別為例,我們點(diǎn)開該分類查看一下2018年中報(bào)(見下圖),可以看到該分類下又包括:業(yè)績報(bào)表、業(yè)績快報(bào)、利潤表等7個(gè)報(bào)表的數(shù)據(jù)。以業(yè)績報(bào)表為例,報(bào)表包含全部3000多只股票的業(yè)績報(bào)表數(shù)據(jù),一共有70多頁。
假如,我們想獲取所有股票2018年中的業(yè)績報(bào)表數(shù)據(jù),然后對該數(shù)據(jù)進(jìn)行一些分析。采取手動(dòng)復(fù)制的方法,70多頁可以勉強(qiáng)完成。但如果想獲取任意一年、任意季度、任意報(bào)表的數(shù)據(jù),要再通過手動(dòng)復(fù)制的方法,工作量會(huì)非常地大。舉個(gè)例子,假設(shè)要獲取10年間(40個(gè)季度)、所有7個(gè)報(bào)表的數(shù)據(jù),那么手動(dòng)復(fù)制的工作量大約將是:40×7×70(每個(gè)報(bào)表大約70頁),差不多要重復(fù)性地復(fù)制2萬次!?。】梢哉f是人工不可能完成的任務(wù)。所以,本文的目標(biāo)就是利用Selenium自動(dòng)化技術(shù),爬取年報(bào)季報(bào)類別下,任意一年(網(wǎng)站有數(shù)據(jù)至今)、任意財(cái)務(wù)報(bào)表數(shù)據(jù)。我們所需要做的,僅是簡單輸入幾個(gè)字符,其他就全部交給電腦,然后過一會(huì)兒打開excel,就可以看到所需數(shù)據(jù)”靜靜地躺在那里”,是不是挺酷的? 好,下面我們就開始實(shí)操一下。首先,需要分析要爬取的網(wǎng)頁對象。 2. 網(wǎng)頁分析之前,我們已經(jīng)爬過表格型的數(shù)據(jù),所以對表格數(shù)據(jù)的結(jié)構(gòu)應(yīng)該不會(huì)太陌生,如果忘了,可以再看一下這篇文章:https://www./web_scraping_withpython2.html 我們這里以上面的2018年中報(bào)的業(yè)績報(bào)表為例,查看一下表格的形式。 網(wǎng)址url:http://data.eastmoney.com/bbsj/201806/lrb.html,bbsj 代表年報(bào)季報(bào),201803 代表2018年一季報(bào) ,類似地,201806表示年中報(bào);lrb 是利潤表 的首字母縮寫,同理,yjbb 表示業(yè)績報(bào)表 。可以看出,該網(wǎng)址格式很簡單,便于構(gòu)造url。 接著,我們點(diǎn)擊下一頁 按鈕,可以看到表格更新后,url沒有發(fā)生改變,可以判定是采用了Javscript。那么,我們首先判斷是不是采用了Ajax加載的。方法也很簡單,右鍵檢查或按F12,切換到network并選擇下面的XHR,再按F5刷新??梢钥吹街挥幸粋€(gè)Ajax請求,點(diǎn)擊下一頁也并沒有生成新的Ajax請求,可以判斷該網(wǎng)頁結(jié)構(gòu)不是常見的那種點(diǎn)擊下一頁或者下拉會(huì)源源不斷出現(xiàn)的Ajax請求類型,那么便無法構(gòu)造url來實(shí)現(xiàn)分頁爬取。
XHR選項(xiàng)里沒有找到我們需要的請求,接下來試試看能不能再JS里找到表格的數(shù)據(jù)請求。將選項(xiàng)選為JS,再次F5刷新,可以看到出現(xiàn)了很多JS請求,然后我們點(diǎn)擊幾次下一頁,會(huì)發(fā)現(xiàn)彈出新的請求來,然后右邊為響應(yīng)的請求信息。url鏈接非常長,看上去很復(fù)雜。好,這里我們先在這里打住不往下了。 可以看到,通過分析后臺(tái)元素來爬取該動(dòng)態(tài)網(wǎng)頁的方法,相對比較復(fù)雜。那么有沒有干脆、直截了當(dāng)?shù)鼐湍軌蜃ト”砀駜?nèi)容的方法呢?有的,就是本文接下來要介紹的Selenium大法。
3. Selenium知識(shí)Selenium 是什么?一句話,自動(dòng)化測試工具。它是為了測試而出生的,但在近幾年火熱的爬蟲領(lǐng)域中,它搖身一變,變成了爬蟲的利器。直白點(diǎn)說, Seleninm能控制瀏覽器, 像人一樣”上網(wǎng)”。比如,可以實(shí)現(xiàn)網(wǎng)頁自動(dòng)翻頁、登錄網(wǎng)站、發(fā)送郵件、下載圖片/音樂/視頻等等。舉個(gè)例子,寫幾行python代碼就可以用Selenium實(shí)現(xiàn)登錄IT桔子,然后瀏覽網(wǎng)頁的功能。
怎么樣,僅用幾行代碼就能實(shí)現(xiàn)自動(dòng)上網(wǎng)操作,是不是挺神奇的?當(dāng)然,這僅僅是Selenium最簡單的功能,還有很多更加豐富的操作,可以參考以下幾篇教程: 參考網(wǎng)站: Selenium官網(wǎng): https://selenium-python./ SeleniumPython文檔(英文版):http://selenium-python./index.html SeleniumPython文檔(中文版):https://selenium-python-zh./en/latest/faq.html Selenium 基本操作:https://www./2017/7/python-spider-Selenium-PhantomJS-basic/ Selenium爬取淘寶信息實(shí)戰(zhàn):https:///2852.html
只需要記住重要的一點(diǎn)就是:Selenium能做到'可見即可爬' 。也就是說網(wǎng)頁上你能看到的東西,Selenium基本上都能爬取下來。包括上面我們提到的東方財(cái)富網(wǎng)的財(cái)務(wù)報(bào)表數(shù)據(jù),它也能夠做到,而且非常簡單直接,不用去后臺(tái)查看用了什么JavaScript技術(shù)或者Ajax參數(shù)。下面我們就實(shí)際來操練下吧。 4. 編碼實(shí)現(xiàn)4.1. 思路- 安裝配置好Selenium運(yùn)行的相關(guān)環(huán)境,瀏覽器可以用Chrome、Firefox、PhantomJS等,我用的是Chrome;
- 東方財(cái)富網(wǎng)的財(cái)務(wù)報(bào)表數(shù)據(jù)不用登錄可直接獲得,Selenium更加方便爬??;
- 先以單個(gè)網(wǎng)頁中的財(cái)務(wù)報(bào)表為例,表格數(shù)據(jù)結(jié)構(gòu)簡單,可先直接定位到整個(gè)表格,然后一次性獲取所有td節(jié)點(diǎn)對應(yīng)的表格單元內(nèi)容;
- 接著循環(huán)分頁爬取所有上市公司的數(shù)據(jù),并保存為csv文件。
- 重新構(gòu)造靈活的url,實(shí)現(xiàn)可以爬取任意時(shí)期、任意一張財(cái)務(wù)報(bào)表的數(shù)據(jù)。
根據(jù)上述思路,下面就用代碼一步步來實(shí)現(xiàn)。 4.2. 爬取單頁表格我們先以2018年中報(bào)的利潤表 為例,抓取該網(wǎng)頁的第一頁表格數(shù)據(jù),網(wǎng)頁url:http://data.eastmoney.com/bbsj/201806/lrb.html
快速定位到表格所在的節(jié)點(diǎn):id = dt_1,然后可以用Selenium進(jìn)行抓取了,方法如下: from selenium import webdriver browser = webdriver.Chrome() # 當(dāng)測試好能夠順利爬取后,為加快爬取速度可設(shè)置無頭模式,即不彈出瀏覽器 # 添加無頭headlesss 1使用chrome headless,2使用PhantomJS # 使用 PhantomJS 會(huì)警告高不建議使用phantomjs,建議chrome headless # chrome_options = webdriver.ChromeOptions() # chrome_options.add_argument('--headless') # browser = webdriver.Chrome(chrome_options=chrome_options) # browser = webdriver.PhantomJS() # browser.maximize_window() # 最大化窗口,可以選擇設(shè)置
browser.get('http://data.eastmoney.com/bbsj/201806/lrb.html') element = browser.find_element_by_css_selector('#dt_1') # 定位表格,element是WebElement類型 # 提取表格內(nèi)容td td_content = element.find_elements_by_tag_name('td') # 進(jìn)一步定位到表格內(nèi)容所在的td節(jié)點(diǎn) lst = [] # 存儲(chǔ)為list for td in td_content: lst.append(td.text) print(lst) # 輸出表格內(nèi)容
|
這里,使用Chrome瀏覽器構(gòu)造一個(gè)Webdriver對象,賦值給變量browser,browser調(diào)用get()方法請求想要抓取的網(wǎng)頁。接著使用find_element_by_css_selector 方法查找表格所在的節(jié)點(diǎn):'#dt_1’。 這里推薦一款小巧、快速定位css/xpath的Chrome插件:SelectorGadget,使用這個(gè)插件就不用再去源代碼中手動(dòng)定位節(jié)點(diǎn)那么麻煩了。 插件地址:https://chrome.google.com/webstore/detail/selectorgadget/mhjhnkcfbdhnjickkkdbjoemdmbfginb 緊接著再向下定位到td節(jié)點(diǎn),因?yàn)榫W(wǎng)頁中有很多個(gè)td節(jié)點(diǎn),所以要用find_elements方法。然后,遍歷數(shù)據(jù)節(jié)點(diǎn)存儲(chǔ)到list中。打印查看一下結(jié)果:
# list形式: ['1', '002161', '遠(yuǎn)望谷', ...'-7960萬', '09-29', '2','002316', '亞聯(lián)發(fā)展', ...'1.79億', '09-29', '3',... '50', '002683', '宏大爆破',...'1.37億', '09-01']
|
是不是很方便,幾行代碼就能抓取下來這一頁表格,除了速度有點(diǎn)慢。 為了便于后續(xù)存儲(chǔ),我們將list轉(zhuǎn)換為DataFrame。首先需要把這一個(gè)大的list分割為多行多列的子list,實(shí)現(xiàn)如下: import pandas as pd # 確定表格列數(shù) col = len(element.find_elements_by_css_selector('tr:nth-child(1) td')) # 通過定位一行td的數(shù)量,可獲得表格的列數(shù),然后將list拆分為對應(yīng)列數(shù)的子list lst = [lst[i:i + col] for i in range(0, len(lst), col)] # 原網(wǎng)頁中打開'詳細(xì)'鏈接可以查看更詳細(xì)的數(shù)據(jù),這里我們把url提取出來,方便后期查看 lst_link = [] links = element.find_elements_by_css_selector('#dt_1 a.red') for link in links: url = link.get_attribute('href') lst_link.append(url) lst_link = pd.Series(lst_link) # list轉(zhuǎn)為dataframe df_table = pd.DataFrame(lst) # 添加url列 df_table['url'] = lst_link print(df_table.head()) # 查看DataFrame
|
這里,要將list分割為子list,只需要確定表格有多少列即可,然后將每相隔這么多數(shù)量的值劃分為一個(gè)子list。如果我們數(shù)一下該表的列數(shù),可以發(fā)現(xiàn)一共有16列。但是這里不能使用這個(gè)數(shù)字,因?yàn)槌死麧櫛恚渌麍?bào)表的列數(shù)并不是16,所以當(dāng)后期爬取其他表格可能就會(huì)報(bào)錯(cuò)。這里仍然通過find_elements_by_css_selector方法,定位首行td節(jié)點(diǎn)的數(shù)量,便可獲得表格的列數(shù),然后將list拆分為對應(yīng)列數(shù)的子list。同時(shí),原網(wǎng)頁中打開”詳細(xì)”列的鏈接可以查看更詳細(xì)的數(shù)據(jù),這里我們把url提取出來,并增加一列到DataFrame中,方便后期查看。打印查看一下輸出結(jié)果:
可以看到,表格所有的數(shù)據(jù)我們都抓取到了,下面只需要進(jìn)行分頁循環(huán)爬取就行了。 這里,沒有抓取表頭是因?yàn)楸眍^有合并單元格,處理起來就非常麻煩。建議表格抓取下來后,在excel中復(fù)制表頭進(jìn)去就行了。如果,實(shí)在想要用代碼完成,可以參考這篇文章:https://blog.csdn.net/weixin_39461443/article/details/75456962 4.3. 分頁爬取上面完成了單頁表格的爬取,下面我們來實(shí)現(xiàn)分頁爬取。 首先,我們先實(shí)現(xiàn)Selenium模擬翻頁跳轉(zhuǎn)操作,成功后再爬取每頁的表格內(nèi)容。 from selenium import webdriver from selenium.common.exceptions import TimeoutException from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait import WebDriverWait import time
browser = webdriver.Chrome() browser.maximize_window() # 最大化窗口,可以選擇設(shè)置 wait = WebDriverWait(browser, 10) def index_page(page): try: browser.get('http://data.eastmoney.com/bbsj/201806/lrb.html') print('正在爬取第: %s 頁' % page) wait.until( EC.presence_of_element_located((By.ID, 'dt_1'))) # 判斷是否是第1頁,如果大于1就輸入跳轉(zhuǎn),否則等待加載完成。 if page > 1: # 確定頁數(shù)輸入框 input = wait.until(EC.presence_of_element_located( (By.XPATH, '//*[@id='PageContgopage']'))) input.click() input.clear() input.send_keys(page) submit = wait.until(EC.element_to_be_clickable( (By.CSS_SELECTOR, '#PageCont > a.btn_link'))) submit.click() time.sleep(2) # 確認(rèn)成功跳轉(zhuǎn)到輸入框中的指定頁 wait.until(EC.text_to_be_present_in_element( (By.CSS_SELECTOR, '#PageCont > span.at'), str(page))) except Exception: return None
def main(): for page in range(1,5): # 測試翻4頁 index_page(page) if __name__ == '__main__': main()
|
這里,我們先加載了相關(guān)包,使用WebDriverWait對象,設(shè)置最長10s的顯式等待時(shí)間,以便網(wǎng)頁加載出表格。判斷表格是否加載出來,用到了EC.presence_of_element_located條件。表格加載出來后,設(shè)置一個(gè)頁面判斷,如果在第1頁就等待頁面加載完成,如果大于第1頁就開始跳轉(zhuǎn)。 要完成跳轉(zhuǎn)操作,我們需要通過獲取輸入框input節(jié)點(diǎn),然后用clear()方法清空輸入框,再通過send_keys()方法填寫相應(yīng)的頁碼,接著通過submit.click()方法擊下一頁完成翻頁跳轉(zhuǎn)。 這里,我們測試一下前4頁跳轉(zhuǎn)效果,可以看到網(wǎng)頁成功跳轉(zhuǎn)了。下面就可以對每一頁應(yīng)用第一頁爬取表格內(nèi)容的方法,抓取每一頁的表格,轉(zhuǎn)為DataFrame然后存儲(chǔ)到csv文件中去。
4.4. 通用爬蟲構(gòu)造上面,我們完成了2018年中報(bào)利潤表: http://data.eastmoney.com/bbsj/201806/lrb.html,一個(gè)網(wǎng)頁表格的爬取。但如果我們想爬取任意時(shí)期、任意一張報(bào)表的表格,比如2017年3季度的利潤表、2016年全年的業(yè)績報(bào)表、2015年1季度的現(xiàn)金流量表等等。上面的代碼就行不通了,下面我們對代碼進(jìn)行一下改造,變成更通用的爬蟲。從圖中可以看到,東方財(cái)富網(wǎng)年報(bào)季報(bào)有7張表格,財(cái)務(wù)報(bào)表最早從2007年開始每季度一次。基于這兩個(gè)維度,可重新構(gòu)造url的形式,然后爬取表格數(shù)據(jù)。下面,我們用代碼進(jìn)行實(shí)現(xiàn):
# 重構(gòu)url # 1 設(shè)置財(cái)務(wù)報(bào)表獲取時(shí)期 year = int(float(input('請輸入要查詢的年份(四位數(shù)2007-2018): '))) # int表示取整,里面加float是因?yàn)檩斎氲氖莝tr,直接int會(huì)報(bào)錯(cuò),float則不會(huì) while (year < 2007 or year > 2018): year = int(float(input('年份數(shù)值輸入錯(cuò)誤,請重新輸入:'))) quarter = int(float(input('請輸入小寫數(shù)字季度(1:1季報(bào),2-年中報(bào),3:3季報(bào),4-年報(bào)): '))) while (quarter < 1 or quarter > 4): quarter = int(float(input('季度數(shù)值輸入錯(cuò)誤,請重新輸入: '))) # 轉(zhuǎn)換為所需的quarter 兩種方法,2表示兩位數(shù),0表示不滿2位用0補(bǔ)充 quarter = '{:02d}'.format(quarter * 3) # quarter = '%02d' %(int(month)*3) date = '{}{}' .format(year, quarter)
# 2 設(shè)置財(cái)務(wù)報(bào)表種類 tables = int( input('請輸入查詢的報(bào)表種類對應(yīng)的數(shù)字(1-業(yè)績報(bào)表;2-業(yè)績快報(bào)表:3-業(yè)績預(yù)告表;4-預(yù)約披露時(shí)間表;5-資產(chǎn)負(fù)債表;6-利潤表;7-現(xiàn)金流量表): ')) dict_tables = {1: '業(yè)績報(bào)表', 2: '業(yè)績快報(bào)表', 3: '業(yè)績預(yù)告表', 4: '預(yù)約披露時(shí)間表', 5: '資產(chǎn)負(fù)債表', 6: '利潤表', 7: '現(xiàn)金流量表'} dict = {1: 'yjbb', 2: 'yjkb/13', 3: 'yjyg', 4: 'yysj', 5: 'zcfz', 6: 'lrb', 7: 'xjll'} category = dict[tables]
# 3 設(shè)置url url = 'http://data.eastmoney.com/{}/{}/{}.html' .format('bbsj', date, category) print(url) # 測試輸出的url
|
經(jīng)過上面的設(shè)置,我們通過輸入想要獲得指定時(shí)期、制定財(cái)務(wù)報(bào)表類型的數(shù)值,就能返回相應(yīng)的url鏈接。將該鏈接應(yīng)用到前面的爬蟲中,就可以爬取相應(yīng)的報(bào)表內(nèi)容了。 另外,除了從第一頁開始爬取到最后一頁的結(jié)果以外,我們還可以自定義設(shè)置想要爬取的頁數(shù)。比如起始頁數(shù)從第1頁開始,然后爬取10頁。 # 4 選擇爬取頁數(shù)范圍 start_page = int(input('請輸入下載起始頁數(shù):\n')) nums = input('請輸入要下載的頁數(shù),(若需下載全部則按回車):\n') # 確定網(wǎng)頁中的最后一頁 browser.get(url) # 確定最后一頁頁數(shù)不直接用數(shù)字而是采用定位,因?yàn)椴煌瑫r(shí)間段的頁碼會(huì)不一樣 try: page = browser.find_element_by_css_selector('.next+ a') # next節(jié)點(diǎn)后面的a節(jié)點(diǎn) except: page = browser.find_element_by_css_selector('.at+ a') else: print('沒有找到該節(jié)點(diǎn)') # 上面用try.except是因?yàn)榻^大多數(shù)頁碼定位可用'.next+ a',但是業(yè)績快報(bào)表有的只有2頁,無'.next+ a'節(jié)點(diǎn) end_page = int(page.text)
if nums.isdigit(): end_page = start_page + int(nums) elif nums == '': end_page = end_page else: print('頁數(shù)輸入錯(cuò)誤') # 輸入準(zhǔn)備下載表格類型 print('準(zhǔn)備下載:{}-{}' .format(date, dict_tables[tables]))
|
經(jīng)過上面的設(shè)置,我們就可以實(shí)現(xiàn)自定義時(shí)期和財(cái)務(wù)報(bào)表類型的表格爬取了,將代碼再稍微整理一下,可實(shí)現(xiàn)下面的爬蟲效果: 視頻截圖:
視頻地址:https://v.qq.com/x/page/y07335thsn2.html 背景中類似黑客帝國的代碼雨效果,其實(shí)是動(dòng)態(tài)網(wǎng)頁效果。素材來源于下面這個(gè)網(wǎng)站,該網(wǎng)站還有很多酷炫的動(dòng)態(tài)背景可以下載下來。 http://wallpaper./store/paperDetail-1783830052.htm 4.5. 完整代碼整個(gè)爬蟲的完整代碼如下所示: from selenium import webdriver from selenium.common.exceptions import TimeoutException from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait import WebDriverWait import time import pandas as pd import os
# 先chrome,后phantomjs # browser = webdriver.Chrome() # 添加無頭headlesss chrome_options = webdriver.ChromeOptions() chrome_options.add_argument('--headless') browser = webdriver.Chrome(chrome_options=chrome_options)
# browser = webdriver.PhantomJS() # 會(huì)報(bào)警高提示不建議使用phantomjs,建議chrome添加無頭 browser.maximize_window() # 最大化窗口 wait = WebDriverWait(browser, 10)
def index_page(page): try: print('正在爬取第: %s 頁' % page) wait.until( EC.presence_of_element_located((By.ID, 'dt_1'))) # 判斷是否是第1頁,如果大于1就輸入跳轉(zhuǎn),否則等待加載完成。 if page > 1: # 確定頁數(shù)輸入框 input = wait.until(EC.presence_of_element_located( (By.XPATH, '//*[@id='PageContgopage']'))) input.click() input.clear() input.send_keys(page) submit = wait.until(EC.element_to_be_clickable( (By.CSS_SELECTOR, '#PageCont > a.btn_link'))) submit.click() time.sleep(2) # 確認(rèn)成功跳轉(zhuǎn)到輸入框中的指定頁 wait.until(EC.text_to_be_present_in_element( (By.CSS_SELECTOR, '#PageCont > span.at'), str(page))) except Exception: return None
def parse_table(): # 提取表格第一種方法 # element = wait.until(EC.presence_of_element_located((By.ID, 'dt_1'))) # 第二種方法 element = browser.find_element_by_css_selector('#dt_1')
# 提取表格內(nèi)容td td_content = element.find_elements_by_tag_name('td') lst = [] for td in td_content: # print(type(td.text)) # str lst.append(td.text)
# 確定表格列數(shù) col = len(element.find_elements_by_css_selector('tr:nth-child(1) td')) # 通過定位一行td的數(shù)量,可獲得表格的列數(shù),然后將list拆分為對應(yīng)列數(shù)的子list lst = [lst[i:i + col] for i in range(0, len(lst), col)]
# 原網(wǎng)頁中打開'詳細(xì)'鏈接,可以查看更詳細(xì)的數(shù)據(jù),這里我們把url提取出來,方便后期查看 lst_link = [] links = element.find_elements_by_css_selector('#dt_1 a.red') for link in links: url = link.get_attribute('href') lst_link.append(url)
lst_link = pd.Series(lst_link) # list轉(zhuǎn)為dataframe df_table = pd.DataFrame(lst) # 添加url列 df_table['url'] = lst_link
# print(df_table.head()) return df_table
# 寫入文件 def write_to_file(df_table, category): # 設(shè)置文件保存在D盤eastmoney文件夾下 file_path = 'D:\\eastmoney' if not os.path.exists(file_path): os.mkdir(file_path) os.chdir(file_path) df_table.to_csv('{}.csv' .format(category), mode='a', encoding='utf_8_sig', index=0, header=0)
# 設(shè)置表格獲取時(shí)間、類型 def set_table(): print('*' * 80) print('\t\t\t\t東方財(cái)富網(wǎng)報(bào)表下載') print('作者:高級(jí)農(nóng)民工 2018.10.6') print('--------------')
# 1 設(shè)置財(cái)務(wù)報(bào)表獲取時(shí)期 year = int(float(input('請輸入要查詢的年份(四位數(shù)2007-2018):\n'))) # int表示取整,里面加float是因?yàn)檩斎氲氖莝tr,直接int會(huì)報(bào)錯(cuò),float則不會(huì) # https:///questions/1841565/valueerror-invalid-literal-for-int-with-base-10 while (year < 2007 or year > 2018): year = int(float(input('年份數(shù)值輸入錯(cuò)誤,請重新輸入:\n'))) quarter = int(float(input('請輸入小寫數(shù)字季度(1:1季報(bào),2-年中報(bào),3:3季報(bào),4-年報(bào)):\n'))) while (quarter < 1 or quarter > 4): quarter = int(float(input('季度數(shù)值輸入錯(cuò)誤,請重新輸入:\n'))) # 轉(zhuǎn)換為所需的quarter 兩種方法,2表示兩位數(shù),0表示不滿2位用0補(bǔ)充, # http://www.runoob.com/python/att-string-format.html quarter = '{:02d}'.format(quarter * 3) # quarter = '%02d' %(int(month)*3) date = '{}{}' .format(year, quarter) # print(date) 測試日期 ok # 2 設(shè)置財(cái)務(wù)報(bào)表種類 tables = int( input('請輸入查詢的報(bào)表種類對應(yīng)的數(shù)字(1-業(yè)績報(bào)表;2-業(yè)績快報(bào)表:3-業(yè)績預(yù)告表;4-預(yù)約披露時(shí)間表;5-資產(chǎn)負(fù)債表;6-利潤表;7-現(xiàn)金流量表): \n')) dict_tables = {1: '業(yè)績報(bào)表', 2: '業(yè)績快報(bào)表', 3: '業(yè)績預(yù)告表', 4: '預(yù)約披露時(shí)間表', 5: '資產(chǎn)負(fù)債表', 6: '利潤表', 7: '現(xiàn)金流量表'} dict = {1: 'yjbb', 2: 'yjkb/13', 3: 'yjyg', 4: 'yysj', 5: 'zcfz', 6: 'lrb', 7: 'xjll'} category = dict[tables] # 3 設(shè)置url # url = 'http://data.eastmoney.com/bbsj/201803/lrb.html' eg. url = 'http://data.eastmoney.com/{}/{}/{}.html' .format( 'bbsj', date, category) # # 4 選擇爬取頁數(shù)范圍 start_page = int(input('請輸入下載起始頁數(shù):\n')) nums = input('請輸入要下載的頁數(shù),(若需下載全部則按回車):\n') print('*' * 80) # 確定網(wǎng)頁中的最后一頁 browser.get(url) # 確定最后一頁頁數(shù)不直接用數(shù)字而是采用定位,因?yàn)椴煌瑫r(shí)間段的頁碼會(huì)不一樣 try: page = browser.find_element_by_css_selector('.next+ a') # next節(jié)點(diǎn)后面的a節(jié)點(diǎn) except: page = browser.find_element_by_css_selector('.at+ a') # else: # print('沒有找到該節(jié)點(diǎn)') # 上面用try.except是因?yàn)榻^大多數(shù)頁碼定位可用'.next+ a',但是業(yè)績快報(bào)表有的只有2頁,無'.next+ a'節(jié)點(diǎn) end_page = int(page.text) if nums.isdigit(): end_page = start_page + int(nums) elif nums == '': end_page = end_page else: print('頁數(shù)輸入錯(cuò)誤') # 輸入準(zhǔn)備下載表格類型 print('準(zhǔn)備下載:{}-{}' .format(date, dict_tables[tables])) print(url) yield{ 'url': url, 'category': dict_tables[tables], 'start_page': start_page, 'end_page': end_page }def main(category, page): try: index_page(page) # parse_table() #測試print df_table = parse_table() write_to_file(df_table, category) print('第 %s 頁抓取完成' % page) print('--------------') except Exception: print('網(wǎng)頁爬取失敗,請檢查網(wǎng)頁中表格內(nèi)容是否存在')# 單進(jìn)程if __name__ == '__main__': for i in set_table(): # url = i.get('url') category = i.get('category') start_page = i.get('start_page') end_page = i.get('end_page') for page in range(start_page, end_page): # for page in range(44,pageall+1): # 如果下載中斷,可以嘗試手動(dòng)更改網(wǎng)頁繼續(xù)下載 main(category, page) print('全部抓取完成') |
這里,我下載了所有上市公司的部分報(bào)表。 2018年中報(bào)業(yè)績報(bào)表:
2017年報(bào)的利潤表:
如果你想下載更多的報(bào)表,可以使用文中的代碼,代碼和素材資源可以在下面的鏈接中獲?。?/p> https://github.com/makcyun/eastmoney_spider 另外,爬蟲還可以再完善一下,比如增加爬取上市公司的公告信息,設(shè)置可以爬任意一家(數(shù)家/行業(yè))的公司數(shù)據(jù)而不用全部。 還有一個(gè)問題是,Selenium爬取的速度很慢而且很占用內(nèi)存,建議盡量先嘗試采用Requests請求的方法,抓不到的時(shí)候再考慮這個(gè)。文章開頭在進(jìn)行網(wǎng)頁分析的時(shí)候,我們初步分析了表格JS的請求數(shù)據(jù),是否能從該請求中找到我們需要的表格數(shù)據(jù)呢? 后續(xù)文章,我們換一個(gè)思路再來嘗試爬取一次。
|