scrapy學習筆記

發表於2016-10-12

scrapy是python最有名的爬蟲框架之一,可以很方便的進行web抓取,並且提供了很強的定製型,這裡記錄簡單學習的過程和在實際應用中會遇到的一些常見問題

一、安裝

在安裝scrapy之前有一些依賴需要安裝,否則可能會安裝失敗,scrapy的選擇器依賴於lxml,還有Twisted網路引擎,下面是ubuntu下安裝的過程

1. linux下安裝

http://lxml.de/installation.html

2. Mac下安裝

mac下安裝有時候會失敗,建議使用virtualenv安裝在獨立的環境下,可以減少一些問題,因為mac系統自帶python,例如一些依賴庫依賴的一些新的版本,而升級新版本會把舊版本解除安裝掉,解除安裝可能會有許可權的問題

二、基本使用

1. 初始化scrapy專案

我們可以使用命令列初始化一個專案

這裡可以檢視scrapy更多其他的命令

初始化完成後,我們得到下面目錄結構

我們先看一下scrapy的處理流程
scrapy學習筆記

scrapy由下面幾個部分組成

  • spiders:爬蟲模組,負責配置需要爬取的資料和爬取規則,以及解析結構化資料
  • items:定義我們需要的結構化資料,使用相當於dict
  • pipelines:管道模組,處理spider模組分析好的結構化資料,如儲存入庫等
  • middlewares:中介軟體,相當於鉤子,可以對爬取前後做預處理,如修改請求header,url過濾等

我們先來看一個例子,在spiders目錄下新建一個模組DmozSpider.py

開啟終端進入根目錄,執行下面命令

爬蟲開始爬取start_urls定義的url,並輸出到檔案中,最後輸出爬去報告,會輸出爬取得統計結果

這裡我們完成了簡單的爬取和儲存的操作,會在根目錄生成兩個檔案ResourcesBooks

2. 通過程式碼執行爬蟲

每次進入控制檯執行爬蟲還是比較麻煩的,而且不好除錯,我們可以通過CrawlerProcess通過程式碼執行爬蟲,新建一個模組run.py

參考:http://doc.scrapy.org/en/latest/topics/practices.html#run-scrapy-from-a-script

三、Scrapy類

如上面的DmozSpider類,爬蟲類繼承自scrapy.Spider,用於構造Request物件給Scheduler

1. 常用屬性與方法

屬性

  • name:爬蟲的名字,必須唯一(如果在控制檯使用的話,必須配置)
  • start_urls:爬蟲初始爬取的連結列表
  • parse:response結果處理函式
  • custom_settings:自定義配置,覆蓋settings.py中的預設配置

方法

  • start_requests:啟動爬蟲的時候呼叫,預設是呼叫make_requests_from_url方法爬取start_urls的連結,可以在這個方法裡面定製,如果重寫了該方法,start_urls預設將不會被使用,可以在這個方法裡面定製一些自定義的url,如登入,從資料庫讀取url等,本方法返回Request物件
  • make_requests_from_url:預設由start_requests呼叫,可以配置Request物件,返回Request物件
  • parse:response到達spider的時候預設呼叫,如果在Request物件配置了callback函式,則不會呼叫,parse方法可以迭代返回ItemRequest物件,如果返回Request物件,則會進行增量爬取

2. Request與Response物件

每個請求都是一個Request物件,Request物件定義了請求的相關資訊(url, method, headers, body, cookie, priority)和回撥的相關資訊(meta, callback, dont_filter, errback),通常由spider迭代返回

其中meta相當於附加變數,可以在請求完成後通過response.meta訪問

請求完成後,會通過Response物件傳送給spider處理,常用屬性有(url, status, headers, body, request, meta, )

詳細介紹參考官網

看下面這個例子

四、Selector

上面我們只是爬取了網頁的html文字,對於爬蟲,我們需要明確我們需要爬取的結構化資料,需要對原文字進行解析,解析的方法通常有下面這些

  • 普通文字操作
  • 正規表示式:re
  • Dom樹操作:BeautifulSoup
  • XPath選擇器:lxml

scrapy預設支援選擇器的功能,自帶的選擇器構建與lxml之上,並對其進行了改進,使用起來更為簡潔明瞭

1. XPath選擇器

XPpath是標準的XML文件查詢語言,可以用於查詢XML文件中的節點和內容,關於XPath語法,可以參見這裡

先看一個例子,通過html或xml構造Selector物件,然後通過xpath查詢節點,並解析出節點的內容

Selector相當於節點,通過xpath去到子節點集合(SelectorList),可以繼續搜尋,通過extract方法可以取出節點的值,extract方法也可以作用於SelectorList,對於SelectorList可以通過extract_first取出第一個節點的值

  • 通過text()取出節點的內容
  • 通過@href去除節點屬性值(這裡是取出href屬性的值)
  • 直接對節點取值,則是輸出節點的字串

2. CSS選擇器

除了XPath選擇器,scrapy還支援css選擇器

關於css選擇器更多的規則,可以見w3c官網

五、Item類

上面我們只是爬取了網頁的html文字,對於爬蟲,我們需要明確我們需要爬取的結構化資料,我們定義一個item儲存分類資訊,scrapy的item繼承自scrapy.Item

scrapy.Item的用法與python中的字典用法基本一樣,只是做了一些安全限制,屬性定義使用Field,這裡只是進行了宣告,而不是真正的屬性,使用的時候通過鍵值對操作,不支援屬性訪問

what, 好坑爹,這意味著所有的屬性賦值都得用字串了,這裡有解釋(還是沒太明白)

修改DmozSpider的parse方法

六、Pipeline

spider負責爬蟲的配置,item負責宣告結構化資料,而對於資料的處理,在scrapy中使用管道的方式進行處理,只要註冊過的管道都可以處理item資料(處理,過濾,儲存)

下面看看管道的宣告方式,這裡定義一個預處理管道PretreatmentPipeline.py,如果item的title為None,則設定為空字串

再定義一個過濾重複資料的管道DuplicatesPipeline.py,當link重複,則丟棄

最後可以定義一個儲存資料的管道,可以把資料儲存到資料庫中

定義好管道之後我們需要配置到爬蟲上,我們在settings.py模組中配置,後面的數字表示管道的順序

我們也可以為spider配置單獨的pipeline

除了process_item方法外,pipeline還有open_spiderspider_closed兩個方法,在爬蟲啟動和關閉的時候呼叫

七、Rule

爬蟲的通常需要在一個網頁裡面爬去其他的連結,然後一層一層往下爬,scrapy提供了LinkExtractor類用於對網頁連結的提取,使用LinkExtractor需要使用CrawlSpider爬蟲類中,CrawlSpiderSpider相比主要是多了rules,可以新增一些規則,先看下面這個例子,爬取鏈家網的連結

1. Rule物件

Role物件有下面引數

  • link_extractor:連結提取規則
  • callback:link_extractor提取的連結的請求結果的回撥
  • cb_kwargs:附加引數,可以在回撥函式中獲取到
  • follow:表示提取的連結請求完成後是否還要應用當前規則(boolean),如果為False則不會對提取出來的網頁進行進一步提取,預設為False
  • process_links:處理所有的連結的回撥,用於處理從response提取的links,通常用於過濾(引數為link列表)
  • process_request:連結請求預處理(新增header或cookie等)

2. LinkExtractor

LinkExtractor常用的引數有:

  • allow:提取滿足正規表示式的連結
  • deny:排除正規表示式匹配的連結(優先順序高於allow
  • allow_domains:允許的域名(可以是strlist
  • deny_domains:排除的域名(可以是strlist
  • restrict_xpaths:提取滿足XPath選擇條件的連結(可以是strlist
  • restrict_css:提取滿足css選擇條件的連結(可以是strlist
  • tags:提取指定標籤下的連結,預設從aarea中提取(可以是strlist
  • attrs:提取滿足擁有屬性的連結,預設為href(型別為list
  • unique:連結是否去重(型別為boolean
  • process_value:值處理函式(優先順序大於allow

關於LinkExtractor的詳細引數介紹見官網

注意:如果使用rules規則,請不要覆蓋或重寫CrawlSpiderparse方法,否則規則會失效,可以使用parse_start_urls方法

八、Middleware

從最開始的流程圖可以看到,爬去一個資源連結的流程,首先我們配置spider相關的爬取資訊,在啟動爬取例項後,scrapy_engine從Spider取出Request(經過SpiderMiddleware),然後丟給Scheduler(經過SchedulerMiddleware),Scheduler接著把請求丟給Downloader(經過DownloadMiddlware),Downloader把請求結果丟還給Spider,然後Spider把分析好的結構化資料丟給Pipeline,Pipeline進行分析儲存或丟棄,這裡面有4個角色

scrapy有下面三種middlewares

  • SpiderMiddleware:通常用於配置爬蟲相關的屬性,引用連結設定,Url長度限制,成功狀態碼設定,爬取深度設定,爬去優先順序設定等
  • DownloadMiddlware:通常用於處理下載之前的預處理,如請求Header(Cookie,User-Agent),登入驗證處理,重定向處理,代理伺服器處理,超時處理,重試處理等
  • SchedulerMiddleware(已經廢棄):為了簡化框架,排程器中介軟體已經被廢棄,使用另外兩個中介軟體已經夠用了

1. SpiderMiddleware

爬蟲中介軟體有下面幾個方法

  • process_spider_input:當response通過spider的時候被呼叫,返回None(繼續給其他中介軟體處理)或丟擲異常(不會給其他中介軟體處理,當成異常處理)
  • process_spider_output:當spider有item或Request輸出的時候調動
  • process_spider_exception:處理出現異常時呼叫
  • process_start_requests:spider當開始請求Request的時候呼叫

下面是scrapy自帶的一些中介軟體(在scrapy.spidermiddlewares名稱空間下)

  • UrlLengthMiddleware
  • RefererMiddleware
  • OffsiteMiddleware
  • HttpErrorMiddleware
  • DepthMiddleware

我們自己實現一個SpiderMiddleware

參考連結:http://doc.scrapy.org/en/latest/topics/spider-middleware.html

2. DownloaderMiddleware

下載中介軟體有下面幾個方法

  • process_request:請求通過下載器的時候呼叫
  • process_response:請求完成後呼叫
  • process_exception:請求發生異常時呼叫
  • from_crawler:從crawler構造的時候呼叫
  • from_settings:從settings構造的時候呼叫

更多詳細的引數解釋見這裡

在爬取網頁的時候,使用不同的User-Agent可以提高請求的隨機性,定義一個隨機設定User-Agent的中介軟體RandomUserAgentMiddleware

settings.py設定USER_AGENTS引數

配置爬蟲中介軟體的方式與pipeline類似,第二個參數列示優先順序

九、快取

scrapy預設已經自帶了快取的功能,通常我們只需要配置即可,開啟settings.py

更多引數參見這裡

十、多執行緒

scrapy網路請求是基於Twisted,而Twisted預設支援多執行緒,而且scrapy預設也是通過多執行緒請求的,並且支援多核CPU的併發,通常只需要配置一些引數即可

更多引數參見這裡

十一、常見問題

1. 專案名稱問題

在使用的時候遇到過一個問題,在初始化scrapy startproject tutorial的時候,如果使用了一些特殊的名字,如:test, fang等單詞的話,通過get_project_settings方法獲取配置的時候會出錯,改成tutorial或一些複雜的名字的時候不會

這是一個bug,在github上有提到:https://github.com/scrapy/scrapy/issues/428,但貌似沒有完全修復,修改一下名字就好了(當然scrapy.cfgsettings.py裡面也需要修改)

2. 為每個pipeline配置spider

上面我們是在settings.py裡面配置pipeline,這裡的配置的pipeline會作用於所有的spider,我們可以為每一個spider配置不同的pipeline,設定Spidercustom_settings物件

3. 獲取提取連結的節點資訊

通過LinkExtractor提取的scrapy.Link預設不帶節點資訊,有時候我們需要節點的其他attribute屬性,scrapy.Link有個text屬性儲存從節點提取的text值,我們可以通過修改lxmlhtml._collect_string_content變數為etree.tostring,這樣可以在提取節點值就變味渲染節點scrapy.Link.text,然後根據scrapy.Link.text屬性拿到節點的html,最後提取出我們需要的值

4. 從資料庫中讀取urls

有時候我們已經把urls下載到資料庫了,而不是在start_urls裡配置,這時候可以過載spider的start_requests方法

我們還可以在Request新增後設資料,然後在response中訪問

5. 如何進行迴圈爬取

有時候我們需要爬取的一些經常更新的頁面,例如:間隔時間為2s,爬去一個列表前10頁的資料,從第一頁開始爬,爬完成後重新回到第一頁

目前的思路是,通過parse方法迭代返回Request進行增量爬取,由於scrapy預設由快取機制,需要修改

6. 關於去重

scrapy預設有自己的去重機制,預設使用scrapy.dupefilters.RFPDupeFilter類進行去重,主要邏輯如下

預設的去重指紋是sha1(method + url + body + header),這種方式並不能過濾很多,例如有一些請求會加上時間戳的,基本每次都會不同,這時候我們需要自定義過濾規則

配置setting

7. 如何在Pipeline中處理不同的Item

scrapy所有的迭代出來的的Item都會經過所有的Pipeline,如果需要處理不同的Item,只能通過isinstance()方法進行型別判斷,然後分別進行處理,暫時沒有更好的方案

8. url按順序執行

我們可以通過Request的priority控制url的請求的執行順序,但由於網路請求的不確定性,不能保證返回也是按照順序進行的,如果需要進行逐個url請求的話,吧url列表放在meta物件裡面,在response的時候迭代返回下一個Request物件到排程器,達到順序執行的目的,暫時沒有更好的方案

十二、總結

scrapy雖然是最有名的python爬蟲框架,但是還是有很多不足,例如,item不能單獨配置給制定的pipeline,每一個爬取的所有item都會走遍所有的管道,需要在管道里面去判斷不同型別的item,如果在pipelines和items比較多的專案,將會讓專案變得非常臃腫

如有問題歡迎到我的部落格留言

十三、參考連結

相關文章