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

zwye2010 7年前發布 | 32K 次閱讀 Python 網絡爬蟲 Python開發

引言

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

哈哈,其實并沒有 500 行代碼,因為還有注釋和各種空行呢。喜歡爬蟲的童鞋也可以參考下,歡迎給出改進建議或者指出錯誤,謝謝!。

Features

  1. 簡單、易用;
  2. 易于定制的 Spider;
  3. 多線程實現并發下載。

待改進

  1. 更多的測試代碼;
  2. 添加更多的網站爬蟲示例;
  3. 完善爬蟲調度,支持 Request 優先級調度。

xcrawler 介紹

項目結構

├── demo (一個示例 Spider)
│   ├── baidu_news.py
│   └── __init__.py
├── README.md (項目文檔)
├── setup.py (pip 安裝腳本)
├── tests (測試代碼,尚未完成)
└── xcrawler (核心代碼)
    ├── core
    │   ├── crawler.py (Crawler process,負責管理引擎的配置和啟動)
    │   ├── engine.py (Crawler engine,負責調度并完成 URL 請求和調用解析方法)
    │   ├── __init__.py
    ├── __init__.py
    ├── spider
    │   ├── __init__.py
    │   ├── request.py
    │   ├── response.py
    │   └── spider.py (Spider 基類,所有的自定義 Spider 需要從此處繼承)
    └── utils (一些工具函數)
        ├── __init__.py
        └── 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 :請求隊列大小,當隊列已滿時,會阻塞后續的請求。
  • 示例配置:
settings = {
   'download_delay': 0,
   'download_timeout': 6,
   'retry_on_timeout': True,
   'concurrent_requests': 32,
   'queue_size': 512
}

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. 請使用 pip3 setup.py install 安裝即可。

示例

from xcrawler import CrawlerProcess
from xcrawler.spider import BaseSpider, Request
from lxml.html import fromstring
import json

version = '0.0.1' author = 'Chris'

class BaiduNewsSpider(BaseSpider): name = 'baidu_news_spider' start_urls = ['

def spider_started(self): self.file = open('items.jl', 'w')

def spider_stopped(self): self.file.close()

def spider_idle(self):

   # 引擎空閑時,你也可以從數據庫中提取新的 URL 進來
   print('I am in idle mode')
   # self.crawler.crawl(new_request, spider=self)

def make_requests_from_url(self, url): return Request(url, headers=self.default_headers)

def parse(self, response): root = fromstring(response.content, base_url=response.base_url) for element in root.xpath('//a[@target="_blank"]'): title = self._extract_first(element, 'text()') link = self._extract_first(element, '@href').strip() if title: if link.startswith('http://') or link.startswith('https://'): yield {'title': title, 'link': link} yield Request(link, headers=self.default_headers, callback=self.parse_news, meta={'title': title})

def parse_news(self, response): pass

def process_item(self, item): print(item) print(json.dumps(item, ensure_ascii=False), file=self.file)

@staticmethod def _extract_first(element, exp, default=''): r = element.xpath(exp) if len(r): return r[0]

   return default


def main(): settings = { 'download_delay': 1, 'download_timeout': 6, 'retry_on_timeout': True, 'concurrent_requests': 16, 'queue_size': 512 } crawler = CrawlerProcess(settings, 'DEBUG') crawler.crawl(BaiduNewsSpider) crawler.start()

if name == 'main': main()</code></pre>

 

 

來自:http://blog.chriscabin.com/coding-life/web-crawler/xcrawler/1512.html

 

 本文由用戶 zwye2010 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!