本文轉載自以下網站: 從 Class 類到 Scrapy https://www.makcyun.top/web_scraping_withpython12.html
普通函數爬蟲: https://www.cnblogs.com/sanduzxcvbnm/p/10271493.html
函數類爬蟲:https://www.makcyun.top/web_scraping_withpython7.html
Scrapy框架爬蟲: https://www.cnblogs.com/sanduzxcvbnm/p/10276729.html
對於 Python 初學者來說,習慣使用函數寫代碼后,開始學 Scrapy 會感到很復雜,不知如何下手寫代碼,本文通過實際案例,對比普通函數(類)和 Scrapy 中代碼的寫法,助你快速入門 Scrapy。
摘要:通過實際爬蟲案例,分別用普通函數(類)和 Scrapy 進行實現,通過代碼,助你快速入門 Scrapy。
上一篇文章,我們通過 3 個實際爬蟲案例,分別用函數(def)和 類(Class) 兩種方法進行了實現,相信能夠幫助你加深對類(Class)概念和用法的理解。在該文的第 3 個例子中,我們從類的寫法延伸到了 Pyspider 中類代碼的寫法,本文進一步補充,通過實際爬蟲案例分別用普通類的寫法和 Scrapy 中類代碼的寫法進行實現。
Scrapy 爬蟲框架非常強大,但是初學起來會覺得有點復雜,因為完整的一段代碼需要拆分放在不同的模塊下,比如寫一個爬蟲,原先我們只需要用函數或者類從頭寫到尾即可,一目了然,但是在 Scrapy 中則不同,我們首先要在 items.py 中定義爬取的字段內容,在主程序模塊中編寫爬蟲主程序,在 pipeline.py模塊中實現數據處理、存儲,在 middlewares.py 模塊中定義代理 IP、UA 等。
總之代碼的寫法會發生一些變化,我在沒適應用 Scrapy 之前,習慣在 Sublime 中完整地用函數實現一遍,然后再遷移到 Scrapy 框架中,雖然慢,但是寫多幾次后就適應了Scrapy 的寫法,這比一上來就直接在 Scrapy 中寫過渡地要順利一些。
好,下面我們就以之前一篇爬取酷安 App 的文章為例進行說明,這篇文章用了 Scrapy 來實現,下面再用普通的函數寫法實現一遍,並對關鍵的地方進行一下對比。
Scrapy 爬取並分析酷安 6000 款 App,找到良心佳軟
▌爬取思路分析
在上面這篇文章里,我面已經對 目標網站 進行了分析,這里簡單回顧一下,便於把握后續的抓取思路。
首先,網頁請求是 GET 形式,URL 只有一個頁數遞增參數,構造翻頁非常簡單。每頁顯示了 10 條 App 信息,通過點擊尾頁,發現一共有 610 頁,也就是說一共有 6100 款左右的 App 。
接下來,我們需要進入每一個 App 的主頁,抓取 App 相關字段信息,確定了 8 個關鍵字段,分別是:App 名稱、下載量、評分、評分人數、評論數、關注人數、體積、App 分類標簽。
然后,打開網頁后台,利用正則表達式、CSS分別提取每個字段的信息即可。
如果你還不熟悉正則、CSS、Xpath 這幾種網頁內容提取方法,可以參考我早先總結的這篇文章:
通過上述分析,就可以確定爬取思路了:首先可以通過兩種方式構造分頁循環,一種是利用 for 循環直接構造 610 頁 URL 鏈接,另外一種是獲取下一頁的節點,不斷遞增直到最后一頁。第一種方式簡單但只適合總頁數確定的形式,第二種方式稍微復雜一點,但不管知不知道總頁數都可以循環。
接着,每頁抓取 10 款 App URL,進入 App 詳情頁后,利用 CSS 語法抓取每個 App 的 8 個字段信息,最后保存到 MongoDB中,結果形式如下:
下面我們就來實操對比一下。
▌獲取網頁 Response
首先,遍歷每頁的 URL 請求獲得響應 Response,提取每款 App 主頁的 URL 請求,以便下一步解析提取字段內容。
def 寫法:
兩次 for 循環,提取所有的 URL 鏈接,供下一步解析內容。
headers = { |
Scrapy 寫法:
class KuspiderSpider(scrapy.Spider): |
這里有幾點不同的地方,簡單進行說明:
-
循環構造方式不同
普通函數用兩個 for 循環就可以,Scrapy 中是構造最外層的循環,實現方法是先構造一個空列表,存放 page,URL 構造好之后通過 scrapy.Request () 方法進行請求,獲得響應 response ,傳遞給 callback 參數指定的 parse() 方法,再進一步進行第二個 for 循環。
-
內容提取形式不同
以 CSS 語法提取為例,普通函數和 Scrapy 中內容提取的方法稍有不同, 下面以提取提取單個節點文本、提取屬性、提取多個節點,這三種最為常見的提取形式為例,將普通函數和 Scrapy 的寫法進行對比:
-
#提取單個節點文本
name = item('.list_app_title').text()
name = item.css('.detail_app_title::text').extract_first()
#提取屬性
url = item('.app_left_list>a').attr('href')
url = item.css('::attr("href")').extract_first()
#提取多個節點
content = pq(response)('.app_left_list>a').items()
contents = response.css('.app_left_list>a')
這里順便再說一下 Scrapy 遍歷分頁的第二種方式。
如果不通過構造 for 循環的方式遍歷,可以先請求第一頁獲得 response 進行解析,然后再獲取下一頁 url 重復調用解析方法,直到解析完最后一頁為止,這種方法 start_requests 構造就很簡單,直接傳遞 url 到下一個 parse() 方法即可。
def start_requests(self): |
▌解析網頁提取字段
接下來,我們就要提取App 名稱、下載量、評分這些字段信息了。
def 寫法:
def parse_content(urls): |
這里,值得注意一點:
pyquery 提取文本的時候,默認會提取節點內所有的文本內容,如果你只想要其中某個節點的,那么最好先刪除掉不需要的節點,再提取文本。
比如這里,我們在提取 app 名稱的時候,如果直接用:
name = doc('.detail_app_title')text() |
提取出來的則是「酷安 8.8.3」,如果只想要「酷安」,不想要下面的版本信息:8.8.3,需要刪除子節點 span 后再提取:
name = doc('.detail_app_title').remove('span').text() |
Scrapy 寫法:
獲取字段信息,我們需要現在 settings.py 中設置,然后才能提取。
class KuanItem(scrapy.Item): |
回到主程序中,通過 item = Kuan2Item() 來調用上面定義的字段信息。
def parse(self, response): |
▌存儲到 MongoDB
提取完信息以后,我們便可以選擇將數據存儲到 MongoDB 中。
通過上面的方法,我們提取出了字段內容 data,然后轉換為了 DataFrame,DataFrame 存儲到 MongoDB 非常簡單,幾行代碼就能搞定。
def 寫法:
client = pymongo.MongoClient('localhost',27017) |
這里用了 inset_many () 方法來插入數據,但其實不太建議,因為一旦出現爬蟲中斷,我們再接着爬的時候,它會插入重復數據,雖然我們可以再后續處理時去除重復數據,但有更好的方法,那就是用 update_one() 方法,該方法能夠保證直插入新數據,重復數據不插入,下面我們在 Scrapy 中使用:
Scrapy 寫法:
class MongoPipeline(object): |
簡單說明幾點:
from crawler() 是一個類方法,用 @class method 標識,這個方法的作用主要是用來獲取我們在 settings.py 中設置的這幾項參數:
MONGO_URL = 'localhost' |
open_spider() 方法主要進行一些初始化操作 ,在 Spider 開啟時,這個方法就會被調用 。
process_item() 方法是最重要的方法,實現插入數據到 MongoDB 中。
Scrapy 字段提取后,通過 yield 返回的是生成器,內容是單個字典信息,此時,我們可以下面這句代碼,實現只插入新數據,忽略重復數據。
self.db[name].update_one(item, {'$set': item}, upsert=True) |
以上,我們從獲取網頁 Response、解析內容、MongoDB 存儲三個方面,對比了普通函數和 Scrapy 代碼的寫法,這三部分內容是多數爬蟲的主要部分。當然,還有其他的內容比如:下載圖片、反爬措施等,我們留在后續的 Scrapy 文章中繼續介紹。




