當前位置:
首頁 > 知識 > 500 行 Python 代碼構建一個輕量級爬蟲框架

500 行 Python 代碼構建一個輕量級爬蟲框架

點擊上方

Python開發

」,選擇「置頂公眾號」


關鍵時刻,第一時間送達!



引言


玩 Python 爬蟲有段時間了,但是目前還是處於入門級別。xcrawler 則是利用周末時間構建的一個輕量級的爬蟲框架,其中一些設計思想借鑒了著名的爬蟲框架 Scrapy 。既然已經有像 Scrapy 這樣優秀的爬蟲框架,為何還要造輪子呢?嗯,其實最主要的還是想要將學習到 Python 知識綜合起來,提高一下自己。

Features




  1. 簡單、易用;



  2. 易於定製的 Spider ;



  3. 多線程實現並發下載。


待改進




  1. 更多的測試代碼;



  2. 添加更多的網站爬蟲示例;



  3. 完善爬蟲調度,支持 Request 優先順序調度。


xcrawler 介紹


項目結構

  1. ├──

    demo

    (一個示例

    Spider

  2.  

    ├──

    baidu_news

    .

    py

  3.  

    └──

    __init__

    .

    py

  4. ├──

    README

    .

    md

    (項目文檔)

  5. ├──

    setup

    .

    py

    pip

    安裝腳本)

  6. ├──

    tests

    (測試代碼,尚未完成)

  7. └──

    xcrawler

    (核心代碼)

  8.    

    ├──

    core

  9.    

     

    ├──

    crawler

    .

    py

    Crawler

    process

    ,負責管理引擎的配置和啟動)

  10.    

     

    ├──

    engine

    .

    py

    Crawler

    engine

    ,負責調度並完成

    URL

    請求和調用解析方法)

  11.    

     

    ├──

    __init__

    .

    py

  12.    

    ├──

    __init__

    .

    py

  13.    

    ├──

    spider

  14.    

     

    ├──

    __init__

    .

    py

  15.    

     

    ├──

    request

    .

    py

  16.    

     

    ├──

    response

    .

    py

  17.    

     

    └──

    spider

    .

    py

    Spider

    基類,所有的自定義

    Spider

    需要從此處繼承)

  18.    

    └──

    utils

    (一些工具函數)

  19.        

    ├──

    __init__

    .

    py

  20.        

    └──

    url

    .

    py


Crawler engine (生產者+消費者模型)




  1. 引擎啟動時會啟動一個後台線程池,後台線程池負責下載由調度器提供給它的所有 URL (Request),並將響應(Response)結果存放到隊列中;



  2. 引擎的前台解析線程會不斷消費處理隊列中的響應(Response),並調用相應 Spider 的解析函數處理這些相應;



  3. 引擎負責處頁面理解析回來的對象,所有的 

    Request

     對象都會被放到隊列中(遞歸抓取時)等待處理,所有的字典對象(item)送給 Spider 的 

    process_item

     方法處理。


配置介紹




  • 配置項目




    1. download_delay

       : 每批次之間的下載延遲(單位為秒),默認為 0;



    2. download_timeout

       :下載等待延遲,默認為 6 秒;



    3. retry_on_timeout

       :即當下載超時後,對應的請求是否應該重試;



    4. concurrent_requests

       :並發下載數;



    5. queue_size

       :請求隊列大小,當隊列已滿時,會阻塞後續的請求。



  • 示例配置:

  1. settings

    =

    {

  2.    

    "download_delay"

    :

    0

    ,

  3.    

    "download_timeout"

    :

    6

    ,

  4.    

    "retry_on_timeout"

    :

    True

    ,

  5.    

    "concurrent_requests"

    :

    32

    ,

  6.    

    "queue_size"

    :

    512

  7. }


Spider 基類關鍵方法介紹




  1. spider_started

     :該方法會在引擎啟動時被觸發調用,你可以通過繼承該方法進行一些初始化工作,比如配置 pipeline 輸出文件或者資料庫連接等等;



  2. spider_idle

     :該方法會在引擎處理空閑狀態(即沒有任何 requests 在隊列)時被觸發調用,你可以通過繼承該方法給引擎添加新的請求等(使用 

    self

    .

    crawler 

    .

     crawl 

    (

     new_request 

    ,

     spider 

    =

     

    self

     

    )

    即可);



  3. spider_stopped

     :該方法會在引擎關閉時觸發調用,你可以通過繼承該方法並在 Spider 結束工作前做一些清理工作,如關閉文件管道、關閉資料庫連接等;



  4. start_requests

     :該方法會為引擎提該 Spider 的對應種子請求;



  5. make_requests_from_url

     :該方法會為你的 URL 創建一個 Request 對象;



  6. parse

     :該方法為請求的默認解析函數回調,當然你可以可以在創建 Request 時指定其它的回調函數;



  7. process_request

     :每當引擎處理一個 Spider 對應的請求時,該方法會被觸發調用,你可以通過繼承該方法對 request 做些設置,比如更換隨機的 User - Agent ,替換 Cookies 或者代理等;當然,你可以將 request 設置為 None 從而忽略該請求;



  8. proccess_response

     :每當引擎處理一個 Spider 對應的響應時,該方法會被觸發調用;



  9. process_item

     :每當引擎處理一個 Spider 對應的 item 時,該方法會被觸發調用,你可以通過繼承該方法將抓取並解析到的 item 存儲到資料庫或者本地文件中。


注意




  1. 你可以在一個 Crawler 進程中裝入不同的 Spider class ,但需要保證不同的 Spider 的名稱也要不同,否則會被引擎拒絕;



  2. 需要根據情況調整下載延遲和並發數大小;下載延遲盡量不要太大,否則每批請求可能會等待較長時間才會處理完成,從而影響爬蟲性能;



  3. Windows 下的測試還沒做,我用的是 Ubuntu ,所以如果您有什麼問題,歡迎反饋哈!


安裝




  1. 請移步項目主頁 xcrawler (https://github.com/chrisleegit/xcrawler) 下載源碼;



  2. 請保證你的安裝環境為 

    Python

     

    3.4

    +

     ;



  3. 請使用 

    pip 

    3

     setup

    .

    py install

     安裝即可。


示例

  1. from

    xcrawler

    import

    CrawlerProcess

  2. from

    xcrawler

    .

    spider

    import

    BaseSpider

    ,

    Request

  3. from

    lxml

    .

    html

    import

    fromstring

  4. import

    json

  5. __version__

    =

    "0.0.1"

  6. __author__

    =

    "Chris"

  7. class

    BaiduNewsSpider

    (

    BaseSpider

    ):

  8.    name

    =

    "baidu_news_spider"

  9.    start_urls

    =

    [

    "http://news.baidu.com/"

    ]

  10.    default_headers

    =

    {

  11.        

    "User-Agent"

    :

    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) "

  12.                      

    "Chrome/50.0.2661.102 Safari/537.36"

  13.    

    }

  14.    

    def

    spider_started

    (

    self

    ):

  15.        self

    .

    file

    =

    open

    (

    "items.jl"

    ,

    "w"

    )

  16.    

    def

    spider_stopped

    (

    self

    ):

  17.        self

    .

    file

    .

    close

    ()

  18.    

    def

    spider_idle

    (

    self

    ):

  19.        

    # 引擎空閑時,你也可以從資料庫中提取新的 URL 進來

  20.        

    print

    (

    "I am in idle mode"

    )

  21.        

    # self.crawler.crawl(new_request, spider=self)

  22.    

    def

    make_requests_from_url

    (

    self

    ,

    url

    ):

  23.        

    return

    Request

    (

    url

    ,

    headers

    =

    self

    .

    default_headers

    )

  24.    

    def

    parse

    (

    self

    ,

    response

    ):

  25.        root

    =

    fromstring

    (

    response

    .

    content

    ,

  26.                          base_url

    =

    response

    .

    base_url

    )

  27.        

    for

    element

    in

    root

    .

    xpath

    (

    "//a[@target="_blank"]"

    ):

  28.            title

    =

    self

    .

    _extract_first

    (

    element

    ,

    "text()"

    )

  29.            link

    =

    self

    .

    _extract_first

    (

    element

    ,

    "@href"

    ).

    strip

    ()

  30.            

    if

    title

    :

  31.                

    if

    link

    .

    startswith

    (

    "http://"

    )

    or

    link

    .

    startswith

    (

    "https://"

    ):

  32.                    

    yield

    {

    "title"

    :

    title

    ,

    "link"

    :

    link

    }

  33.                    

    yield

    Request

    (

    link

    ,

    headers

    =

    self

    .

    default_headers

    ,

    callback

    =

    self

    .

    parse_news

    ,

  34.                                  meta

    ={

    "title"

    :

    title

    })

  35.    

    def

    parse_news

    (

    self

    ,

    response

    ):

  36.        

    pass

  37.    

    def

    process_item

    (

    self

    ,

    item

    ):

  38.        

    print

    (

    item

    )

  39.        

    print

    (

    json

    .

    dumps

    (

    item

    ,

    ensure_ascii

    =

    False

    ),

    file

    =

    self

    .

    file

    )

  40.    

    @staticmethod

  41.    

    def

    _extract_first

    (

    element

    ,

    exp

    ,

    default

    =

    ""

    ):

  42.        r

    =

    element

    .

    xpath

    (

    exp

    )

  43.        

    if

    len

    (

    r

    ):

  44.            

    return

    r

    [

    0

    ]

  45.        

    return

    default

  46. def

    main

    ():

  47.    settings

    =

    {

  48.        

    "download_delay"

    :

    1

    ,

  49.        

    "download_timeout"

    :

    6

    ,

  50.        

    "retry_on_timeout"

    :

    True

    ,

  51.        

    "concurrent_requests"

    :

    16

    ,

  52.        

    "queue_size"

    :

    512

  53.    

    }

  54.    crawler

    =

    CrawlerProcess

    (

    settings

    ,

    "DEBUG"

    )

  55.    crawler

    .

    crawl

    (

    BaiduNewsSpider

    )

  56.    crawler

    .

    start

    ()

  57. if

    __name__

    ==

    "__main__"

    :

  58.    main

    ()




版權聲明




  • 本文由 Christopher L 發表,採用 知識共享署名——非商業性使用——相同方式共享 4.0 國際許可協議 進行許可。請確保你已了解許可協議,並在轉載 時聲明。



  • 本文固定鏈接: http://blog.chriscabin.com/?p=1512。





  • 作者:chris@克里斯的小屋



  • 原文:http://blog.chriscabin.com/coding-life/webcrawler/xcrawler/1512.html



  • Python開發整理髮布,轉載請聯繫作者獲得授權


【點擊成為Java大神】

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發 的精彩文章:

Python爬蟲之天氣預報
詳解Python爬取房天下的推薦新樓盤

TAG:Python開發 |