發出前兩篇Python實戰的文章之後,有同學和我反映:你的想法很牛逼,可是我就是看不懂你寫的是什麼,我Python不熟悉,看起來有點吃力。我細細一琢磨,這點是個問題。對於熟悉Python的同學,能夠看懂我思路,但是對於那些沒有Python基礎,或者對Python不熟悉的同學,這樣直接扔過來,可能會讓他們失望而歸。所以,這回我弄了一期手把手的實戰教程,同時,在文章中遇到的知識點,還會有提供連結。完全對新手有好。
在前兩篇Python實戰「用程式碼來訪問1024網站」和「用Scrapy編寫“1024網站種子吞噬爬蟲”」收到了大家的一致好評,可能文章寫得比較匆忙,有些術語可能對於Python的初級玩家不是很好理解。所以,我特別準備了一下,用超級詳細的解說,細化到每一步,提供查詢連結等方法,為Python初級玩家,Python小白和對Scrapy框架不熟悉的同學,的製作了這篇手把手Python實戰教程:用Scrapy爬取下載達蓋爾社群的資源。
好了,廢話不多說,學習程式碼就是要學以致用的。不能寫了一遍程式碼就讓程式碼吃灰。下面就跟我一起來搞吧。
小草網站是個好網站,我們這次實戰的結果,是要把“達蓋爾旗幟”裡面的帖子爬取下來,將帖子的圖片儲存到本地,同時將帖子的一些相關資訊,寫入到本地的MongoDB中。這麼乍一聽,感覺我們做的事情好像挺多的,別慌,我帶你慢慢的一步一步來搞起,問題不是很大。
手把手 Step By Stefp
Scrapy可以通過pip來安裝:
$ pip install scrapy
複製程式碼
接下來,我們去事先建好的工程目錄裡面,建立Scrapy的專案。這裡,我們先看一下Scrapy的命令列怎麼用,輸入$ scray -help
出來

看到,建立scrapy的工程的命令是$ scrapy startproject <name>
建立完的結果如下:

OK,這個時候,我們的目錄內容變成了如下結構:

下一步就是建立我們的爬蟲,還是依靠Scrapy本身自帶的命令來建立。輸入Scrapy自帶四種爬蟲模板:basic,crawl,csvfeed和xmlfeed四種。我們這裡選擇basic。
$ scrapy genspider --template=basic superspider bc.ghuws.men
複製程式碼
建立成功,會出現以下提示:

這時候我們的工程目錄就變成了這個樣子:

看到我們的工程裡面多了一個spiders資料夾,裡面有一個superspider.py
檔案,這個就是我們這次程式的主角。我們來看,這個可愛的小蟲子剛生下來是長這個樣子的:

這裡呢,就簡單說一下:
- name - 是我們們的爬蟲名字,這個主要是在執行爬蟲的時候會用到。
- allowed_domains - 是在scrapy自帶的OffsiteMiddleware中用到的。Scrapy預設會開啟OffsiteMiddleware外掛,不在此允許範圍內的域名就會被過濾,而不會進行爬取。
- start_urls - 爬蟲開始爬取的url。
- parse()方法 - 這個就是處理請求結果的。我們具體的爬蟲邏輯大部分就是在這裡寫。
好了,廢話不多說,既然start_urls是用來做爬蟲開始爬取的第一個url,那麼我們就應該把這裡面的數值換成達蓋爾社群的地址,然後我們看一下在parse()
裡面返回的值是什麼。執行方法,就是輸入$ scrapy crawl superspider
指令即可:



我們看到,這個response是一個HtmlResponse類,它裡面的text屬性,裡面的字串就是網頁的html檔案。OK,這一步結束之後,我們下一步就想辦法怎樣能夠解析html網頁了。Scrapy是提供了html物件的解析的,它有一個selector類,可以解析html,同時,裡面還支援xpath語法的查詢和css的查詢。但是這個個人感覺不是很好用,我推薦用BeautifulSoup4庫。安裝方法只需要$ pip install beautifulsoup4
。我們這裡需要用這個來解析html,所以講BeautifulSoup4導進來,在解析,然後我們就會得到一個beasutifulsoup物件。之後,我們就要在這個物件裡面尋找我們需要解析的物件。


目前網頁已經解析好了,下一步就是要在html檔案中,找到每一個帖子的資訊。我們回頭來看html檔案的原始碼,可以看到,每一個帖子其實都是在一個<tr>
tag裡面,其實我們需要的東西,就是下圖紅色框框裡面圈的<a>
tag。

這裡,我們發現,每一個帖子的連結入口,也就是<a>
tag是有兩個特性,一個是有id值,另一個是有href
值。所以,我們要針對soup物件,呼叫find_all()
方法來尋找有特定內容的所有標籤。


我們得到了一個 a_list
結果,這是一個list物件,長度102。在這些資料中,有些結果是我們不要的,比如000到007位置的這幾個資料,他們在網頁中對應的是版規之類的帖子資訊,和我們想要的東西不一樣,所以,在拿到這個a_list
資料,我們需要進行一下篩選。
篩選的過程必不可少,篩選的方法有很多種,我們這裡就做的簡單一點,只選取18年的帖子。為什麼會是18年的帖子啊?少年你看,這一列href的值:

第二個數字“1805”,應該就是“年份+月份”。如果不信,則可以跳到比如論壇100頁,看到的是16年3月份的帖子,這裡面隨便檢查一個連線的href值,是“1603”。這就印證了我們的想法是正確的。好,按照這個篩選18年的帖子的思路,我們來篩選一下a_list
。


看到列印的結果卻是是18年的帖子。但是目前的href並不是帖子真正的url。真正的url應該長這個樣子:
http://bc.ghuws.men/htm_data/16/1805/3126577.html
複製程式碼
所以,我們這裡得進行拼接。對比上面的url,我們目前只有後半部分,前半部分其實是社群網站的root url。那麼我們在settings.py檔案裡面新增一個ROOT_URL
變數,並將這個變數匯入到我們的spider中即可。程式碼就變成了這樣。為了方便,我們們還可以把帖子的id,也就是.html
前面的那個數字也摘出來,方便日後使用。

目前為止,我們拿到了帖子的id和帖子的url。我們的最終目的是要下載圖片,所以,我們得讓爬蟲去按照帖子的url去爬取他們。爬蟲需要進入第二層。這裡,我們需要使用yield
函式,呼叫scrapy.Request
方法,傳入一個callback,在callback中做解析。

現在我們已經進入了每一個帖子的內部,我們現在還沒有拿到的資訊有帖子的標題和帖子的圖片。還是和parse()的步驟一樣,這個時候,我們就該分析帖子的html檔案了。
我們先找標題。看到html檔案中,標題對應的是一個<h4>
標籤。

那這就簡單了,我們只需要找到所有的<h4>
標籤,然後看標題是第幾個就好。接下來是圖片了。每個帖子用的圖床都不一樣,所以圖片部分,我們先來看一下結構:


大概就是這兩種,我們看到,圖片的標籤是<input>
,關鍵點就在type=image
上面,所以我們嘗試著看看能不能根據這個來找到圖片的地址。

我們簡單測試一下,看看執行效果:

完全沒有問題,看著好爽。這時候,我們看結果,會發現,我們抓取到的image,會有一兩個的圖床是不一樣的。

開啟也會看到這個圖片,裡面的內容也和其他的圖片不一樣,並且這個圖片不是我們想要的。所以,這裡我們得做一下過濾。我這裡的方法就是要從找到的image_list
裡面,把少數圖床不一樣的圖片url給過濾掉。一般看來,都是找到的第一個圖片不是我們想要的,所以我們這裡只是判斷一下第一個和第二個是否一樣就可以。

這樣列印出來的結果就沒有問題嘍。
哈哈,現在我們已經拿到了帖子的id,標題,帖子的url地址,還有帖子裡面圖片的url地址。離我們的目標又近了一步。我之前說過,我們的目標是要把每張圖片都儲存在本地,目前我們只是拿到了每張圖片的url。所以,我們需要把圖片都下載下載下來。
其實,當拿到圖片的URL進行訪問的時候,通過http返回的資料,雖然是字串的格式,但是隻要將這些字串儲存成指定的圖片格式,我們在本地就可以按照圖片的解析來開啟。這裡,我們拿到帖子的image_list
,就可以在yield出一層請求,這就是爬蟲的第三層爬取了。

同時,在第三層爬蟲裡面,我們還需要將訪問回來的圖片儲存到本地目錄。那麼程式碼就長這個樣子:

在上面第二次爬取函式的最後,有個地方需要注意一下,就是上圖中紅色框框圈出來的地方。這裡需要加上dont_filter=True
。否則就會被Scrapy給過濾掉。因為圖床的地址,並未在我們剛開始的allow_domain
裡面。加上這個就可以正常訪問了。
這樣執行一遍,我們的本地目錄裡面就會有儲存好的下載照片了。


我們還有個問題,就是我們需要將每個帖子的資訊(id,title,url,和image)都儲存到本地的資料庫中。這個該怎麼做?
別慌,這個其實很簡單。
首先,我們得針對每個帖子,建立一個Scrapy的item。需要在items.py裡面編寫程式碼:

寫好之後,我們需要在爬蟲裡面引入這個類,在第二層解析函式中,構建好item,最後yield出來。這裡,yield出來,會交給Scrapy的pipeline
來處理。

yield出來的item會進入到pipeline中。但是這裡有個前提,就是需要將pipeline在settings.py中設定。

pipeline中我們先列印帖子的id,看看資料能不能夠傳入到這裡

執行:

看到資料是完全可以過來的,而且在Scrapy的log中,會列印出來每一個item裡面的資訊。
我們如果想把資料儲存到MongoDB中,這個操作就應該是在pipeline中完成的。Scrapy之所以簡歷pipeline就是為了針對每個item,如果有特殊的處理,就應該在這裡完成。那麼,我們應該首先匯入pymongo
庫。然後,我們需要在pipeline的__init__()
初始化進行連線資料庫的操作。整體完成之後,pipeline應該長這個樣子:

那麼我們來測試一下資料是否能夠存入到MongoDB中。首先,在terminal中,通過命令$ sudo mongod
來啟動MongoDB。

那麼執行一下,看一下效果:

可以看到,左側,有名為Daguerre
的資料庫,裡面有名為postTable
的表,而且我們的資料成功的寫入了資料庫中。資料的格式如圖所展示,和我們預期的結果是一樣的。
目前為止,我們完成了:從一頁page中,獲得所有帖子的url,然後進入每個帖子,再在帖子中,爬取每個帖子的圖片,下載儲存到本地,同時把帖子的資訊儲存到資料庫中。
但是,這裡你有沒有發現一個問題啊?我們只爬取了第一頁的資料,那如何才能爬取第二頁,第三頁,第N頁的資料呢?
別慌,只需要簡單的加幾行程式碼即可。在我們的spider檔案中的parse()
方法地下,加一個呼叫自己的方法即可,只不過,傳入的url得是下一頁的url,所以,我們這裡得拼湊出下一頁的url,然後再次呼叫parse()
方法即可。這裡為了避免無限迴圈,我們設定一個最大頁數MAX_PAGES
為3,即爬取前三頁的資料。

OK,這樣就完事兒了,這個達蓋爾旗幟的爬蟲就寫好了。我們執行一下瞅瞅效果:

是不是非常的酷炫?再來看看我們的執行結果:



只能說,戰果累累,有圖有真相。
其實,這個程式,可以加入middleware,為http請求提供一些Cookie和User-Agents來防止被網站封。同時,在settings.py檔案中,我們也可以設定一下DOWNLOAD_DELAY
來降低一下單個的訪問速度,和CONCURRENT_REQUESTS
來提升一下訪問速度。


就像之前EpicScrapy1024專案裡面一樣。喜歡的同學,可以去借鑑那個專案程式碼,然後融會貫通,自成一派,爬遍天下網站,無敵是多麼的寂8 寞~~~~
好啦,能看到這裡說明少年你很用心,很辛苦,是一個可塑之才。
廢話不說,看到這裡是有獎勵的。關注“皮克啪的鏟屎官”,回覆“達蓋爾”,即可獲得專案原始碼和說明文件。同時,可以在下面的選單中,找到“Python實戰”按鈕,能夠檢視以往文章,篇篇都非常精彩的哦~
扯扯皮,我覺得學習程式設計最大的動力就是愛好,其實幹什麼事情都是。愛好能夠提供無線的動力,讓人元氣滿滿的往前衝刺。程式碼就是要方便作者,方便大家。寫出來的程式碼要有用處,而且不要吃灰。這樣的程式碼才是好程式碼。歡迎大家關注我的公眾號,“皮克啪的鏟屎官”,之後我會退出Python資料分析的內容,可能會結合量化交易之類的東西。
最後,來貼一張達蓋爾的圖片,紀念一下這位為人類做出傑出貢獻的人。

推薦閱讀:
【Python實戰】用Scrapy編寫“1024網站種子吞噬爬蟲”,送福利
【Python實戰】用程式碼來訪問1024網站,送福利
