朋友托我寫個爬蟲,本身是個爬蟲小白的我還是接受了此次重任,總共曆時五天左右,過程中遇到過無數bug,好在一路披荊斬棘,還是大差不差的完成了此次委托!但感覺這次的經曆還是有必要和大家分享一下,正好最近也沒有寫博文了,趁這次機會趕趕進度!
項目需求:股吧中人們的言論行為和股市漲跌的延遲相關性
數據來源:、
數據字段:閱讀、評論、标題、作者、更新時間
實現功能:讀取每個公司股吧的全部頁面的數據并寫入excel表中
以為例
我們需要爬取的是東方财富吧中全部發帖信息的閱讀、評論、标題、作者及最後更新時間這五個字段的數據,我一開始想也不是很難,解析一下網頁匹配一下對應的标簽值就可以了,但後面還是出現了各種各樣的問題,需要大家注意一下。
首先打開網頁的開發者工具(Ctrl+Shift+i),在源代碼中查找對應字段的标簽結構。
從圖中可以看出,這五個字段分别位于行标簽内,對應的屬性分别是
"l1 a1"、"l2 a2"、"l3 a3"、"l4 a4"、"l5 a5"
。想必大家已經有思路了,我們可以通過先獲取網頁代碼,再解析網頁查詢對應的五個字段,最後做一個提取就可以了。
【東方财富吧:300059】
首頁:
第二頁:
可以看出個股吧鍊接主要由三部分組成:list、名稱代碼、頁數
I.的數字代碼
II. 翻頁數據
如何得到不同股吧的所有翻頁數據,着實讓我找了好久,各種資源我都找了可惜還是沒有發現,突然無意之中我找到了解決辦法,我直接一個好家夥!
跟上述的五類字段一樣,我們查看一下頁數的代碼字段,如下圖所示:
我的第一個辦法是直接解析網頁後找到标簽下的
sumpage
屬性,其内容即為總頁數,本來以為原來這麼好獲取,結果解析完才發現,pagernums
裡的内容是動态的,即span.on
是會随頁而變化的,故直接requests并不能獲取到,但是還是被我發現了玄機!
大家可以看data-pager
這裡,裡面的内容是list,300059_|452885|80|2
,我對比了幾個頁面後發現其中數字分别代表的是:
300059:股吧數字代碼452885:該股吧共發帖452885條 80:每個頁面分别有80條貼子 2:當前所處頁面為第2頁
那麼這時候,我們就可以直接用累積多年的算力(小學除法)算出東方财富吧共有452885/80=5661.0625
,向上取整共5662
頁!如果你也脫口而出好家夥的話,請在屏幕下方打出來!
基本問題解決了,我們可以開始編寫爬蟲了。這部分不講代碼原理,隻解釋代碼功能。自己也是小白,如果代碼存在問題或不清楚的話,歡迎大家在下方留言,我一定及時回複。
① 獲取網頁源代碼
defgetHTMLText(url):try:r=requests.get(url,timeout=30)r.raise_for_status()r.encoding=r.apparent_encodingreturnr.textexcept:print("獲取網頁内容失敗!")
② 解析網頁并提取數據字段
defparsePage(html):list=[]#我用的二維數組存儲read=[]comment=[]title=[]author=[]time=[]try:#print(html)soup=BeautifulSoup(html,"html.parser")foreachinsoup.find_all('span','l1a1'):if'萬'ineach.string:each.string=each.string[:-2]read.append(each.string)read=read[1:]#read[0]=='閱讀'list.append(read)foreachinsoup.find_all('span','l2a2'):comment.append(each.string)comment=comment[1:]#comment[0]=='評論'list.append(comment)foreachinsoup.find_all('span','l3a3'):first=each.select('a:nth-of-type(1)')foriinfirst:i.find_all("a")#print(i.title)title.append(i.title)list.append(title)foreachinsoup.find_all('span','l4a4'):first=each.select('font:nth-of-type(1)')foriinfirst:i.find_all("font")#print(i.title)author.append(i.title)list.append(author)foreachinsoup.find_all('span','l5a5'):time.append(each.string)time=time[1:]#time[0]=='最後更新'list.append(time)except:print("解析網頁字段失敗!")returnlist
③ 獲取貼吧總頁數
基于解析的網頁直接find_all也是可以的defget_total_pages_num(url):try:chrome_options=webdriver.ChromeOptions()chrome_options.add_argument('--headless')chrome_options.add_argument('lang=zh_CN.UTF-8')chrome_options.add_argument('User-Agent="Mozilla/5.0(Macintosh;IntelMacOSX10_12_6)AppleWebKit/537.36(KHTML,likeGecko)Chrome/65.0.3325.162Safari/537.36"')chrome_options.add_argument('--disable-extensions')chrome_options.add_argument('--disable-gpu')chrome_options.add_argument('--no-sandbox')driver=webdriver.Chrome(options=chrome_options)driver.get(url)page_data=driver.find_element_by_xpath('//div[@id="mainbody"]/div[@id="articlelistnew"]/div[@class="pager"]/span[@class="pagernums"]').get_attribute('data-pager')#print(page_data)ifpage_data:#page_nums=re.findall('\|(\d+)',page_data[0])page_nums=page_data.split("|")#print(page_nums)total_pages=math.ceil(int(page_nums[1])/int(page_nums[2]))driver.quit()exceptExceptionase:total_pages=1returnint(total_pages)
上述代碼基本的字段已經可以實現爬取了,結果如下:
可以看到,我這裡的時間多了年份,這是由于朋友研究的需要,在基于一次爬取的結果上,進行二次爬取标題所帶的鍊接網頁獲得的,有關二次爬取的内容,我們再下一節再和大家分享。
在爬取過程中,我還遇到了很多問題諸如:
① 部分帖子結構不同或存在冗餘該如何處理(問董秘等鍊接)
② 爬取過程中ip被屏蔽自動跳轉頁面該如何處理(代理IP池)
… …
這些内容在後續章節中再和大家分享,下期再見啦!
有話要說...