Web crawler with Python - 03.豆瓣電影TOP250(轉)

作者:xlzd

鏈接:https://zhuanlan.zhihu.com/p/20423182

來源:知乎

著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

開發環境已經搭建完成,那么讓我們正式開始第一個爬蟲程序吧,今天,我們的目標是——豆瓣電影TOP250

和完成其他代碼一樣,編寫爬蟲之前,我們需要先思考爬蟲需要干什么、目標網站有什么特點,以及根據目標網站的數據量和數據特點選擇合適的架構。編寫爬蟲之前,推薦使用Chrome的開發者工具來觀察網頁結構。在OS X上,通過"option+command+i"可以打開Chrome的開發者工具,在Windows和Linux,對應的快捷鍵是"F12"。效果如下:

OK,可以看出,這個頁面其實有一個列表,其中放著25條電源信息。我們選中某一條電影,右鍵選擇檢查即可查看選中條目的HTML結構。如下圖所示:

到這一步,我們已經得到的信息有如下:

每頁有25條電影,共有10頁。

電影列表在頁面上的位置為一個class屬性為grid_view的ol標簽中。

每條電影信息放在這個ol標簽的一個li標簽里。

到這一步,我們可以開始寫代碼了。先完成下載網頁源碼的代碼吧,這里我們使用requests庫:

#!/usr/bin/env python# encoding=utf-8importrequestsDOWNLOAD_URL='http://movie.douban.com/top250'defdownload_page(url):data=requests.get(url).contentreturndatadefmain():printdownload_page(DOWNLOAD_URL)if__name__=='__main__':main()

先來簡單測試一下,沒想到運行之后得到的結果是:

403 Forbidden

403 Forbidden


dae

產生403的原因,一般可能是因為需要登錄的網站沒有登錄或者被服務器認為是爬蟲而拒絕訪問,這里很顯然屬于第二種情況。一般,瀏覽器在向服務器發送請求的時候,會有一個請求頭——User-Agent,它用來標識瀏覽器的類型.當我們使用requests來發送請求的時候,默認的User-Agent是python-requests/2.8.1(后面的數字可能不同,表示版本號)。那么,我們試試看如果將User-Agent偽裝成瀏覽器的,會不會解決這個問題呢?

#!/usr/bin/env python# encoding=utf-8importrequestsDOWNLOAD_URL='http://movie.douban.com/top250/'defdownload_page(url):headers={'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.80 Safari/537.36'}data=requests.get(url,headers=headers).contentreturndatadefmain():printdownload_page(DOWNLOAD_URL)if__name__=='__main__':main()

上面的代碼中,我們通過手動指定User-Agent為Chrome瀏覽器,再此訪問就得到了真實的網頁源碼。服務器通過校驗請求的U-A來識別爬蟲,這算是最簡單的一種反爬蟲機制了,通過模擬瀏覽器的U-A,能夠很輕松地繞過這個問題。

當我們拿到網頁源碼之后,就需要解析HTML源碼了。這里,我們使用BeautifulSoup來搞定這件事。在使用之前,你需要通過運行pip install beautifulsoup4來安裝BeautifulSoup。

使用BeautifulSoup解析網頁的大致過程如下:

1. from bs4 import BeautifulSoup2.3. def parse_html(html):4.5.? ? soup = BeautifulSoup(html)6.7.? ? movie_list_soup = soup.find('ol', attrs={'class': 'grid_view'})8.9.? ? for movie_li in movie_list_soup.find_all('li'):10.11.? ? ? ? detail = movie_li.find('div', attrs={'class': 'hd'})12.? ? ? ? movie_name = detail.find('span', attrs={'class': 'title'}).getText()13.14.? ? ? ? print movie_name

我將詳細解釋這段代碼:import用來導入BeautifulSoup,這很容易理解。接著我們定義了函數parse_html,它接受html源碼作為輸入,并將這個網頁中的電影名稱打印到控制臺。第5行我們創建了一個BeautifulSoup對象(這樣的創建方式會產生一個warning,我們下一節再聊這個問題),然后緊接著在第7行使用剛剛創建的對象搜索這篇html文檔中查找那個class為grid_view的ol標簽(上面分析的第2步),接著通過find_all方法,我們得到了電影的集合,通過對它迭代取出每一個電影的名字,打印出來。至于for循環之間的內容,其實就是在解析每個li標簽。你可以很簡單的在剛才的瀏覽器窗口通過開發者工具查看li中的網頁結構。

到這一步,我們已經得到了電影名稱(由于只是演示BeautifulSoup的用法,這里不詳細取出每條電影的所有信息),剛才提到一共有10頁數據,怎么處理翻頁的問題呢?一般在我們確定內容的前提下,可以直接在代碼中寫死如何跳轉頁面,但是為了讓我們的爬蟲更像爬蟲,我們讓它找到頁碼導航中的下一頁的鏈接。

還是借助開發者工具,我們找到了下一頁的鏈接放置在一個span標簽中,這個span標簽的class為next。具體鏈接則在這個span的a標簽中,到了最后一頁之后,這個span中的a標簽消失了,就不需要再翻頁了。于是,根據這段邏輯,我們將上面parse_html函數稍作修改:

defparse_html(html):soup=BeautifulSoup(html)movie_list_soup=soup.find('ol',attrs={'class':'grid_view'})movie_name_list=[]formovie_liinmovie_list_soup.find_all('li'):detail=movie_li.find('div',attrs={'class':'hd'})movie_name=detail.find('span',attrs={'class':'title'}).getText()movie_name_list.append(movie_name)next_page=soup.find('span',attrs={'class':'next'}).find('a')ifnext_page:returnmovie_name_list,DOWNLOAD_URL+next_page['href']returnmovie_name_list,None

我們需要在解析html之后取回我們需要的數據,于是將打印變成了返回一個包含電影名的list,以及下一頁的鏈接,如果到了最后一頁,則返回None。

到這里,大部分代碼已經完成了,我們將其組裝成一個完整的程序即可:

importcodecsdefmain():url=DOWNLOAD_URLwithcodecs.open('movies','wb',encoding='utf-8')asfp:whileurl:html=download_page(url)movies,url=parse_html(html)fp.write(u'{movies}\n'.format(movies='\n'.join(movies)))

上面的代碼完成了對程序的拼裝,并將結果輸出到一個文件中,其中使用了codecs這個包是為了更方便處理中文編碼。當程序運行結束之后,所有的電影名稱就都寫入到了movies這個文件中。

小結

這篇博客總結了最簡單的處理反爬蟲機制,以及簡單的BeautifulSoup的使用,最后完成了將結果寫入到文件中去。麻雀雖小,五臟俱全,這個程序雖然功能簡單,但卻算是一個完整的爬蟲程序了。

接下來,我們將會面臨更加復雜的反爬蟲機制,面對更加復雜的網頁結構,以及會使用數據庫來持久化存儲爬取結果。

完整的代碼如下:

#!/usr/bin/env python# encoding=utf-8"""爬取豆瓣電影TOP250 - 完整示例代碼"""importcodecsimportrequestsfrombs4importBeautifulSoupDOWNLOAD_URL='http://movie.douban.com/top250/'defdownload_page(url):returnrequests.get(url,headers={'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.80 Safari/537.36'}).contentdefparse_html(html):soup=BeautifulSoup(html)movie_list_soup=soup.find('ol',attrs={'class':'grid_view'})movie_name_list=[]formovie_liinmovie_list_soup.find_all('li'):detail=movie_li.find('div',attrs={'class':'hd'})movie_name=detail.find('span',attrs={'class':'title'}).getText()movie_name_list.append(movie_name)next_page=soup.find('span',attrs={'class':'next'}).find('a')ifnext_page:returnmovie_name_list,DOWNLOAD_URL+next_page['href']returnmovie_name_list,Nonedefmain():url=DOWNLOAD_URLwithcodecs.open('movies','wb',encoding='utf-8')asfp:whileurl:html=download_page(url)movies,url=parse_html(html)fp.write(u'{movies}\n'.format(movies='\n'.join(movies)))if__name__=='__main__':main()

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容