500 行 Python 代碼構建一個輕量級爬蟲框架
點擊上方
「
Python開發
」,選擇「置頂公眾號」
關鍵時刻,第一時間送達!
引言
玩 Python 爬蟲有段時間了,但是目前還是處於入門級別。xcrawler 則是利用周末時間構建的一個輕量級的爬蟲框架,其中一些設計思想借鑒了著名的爬蟲框架 Scrapy 。既然已經有像 Scrapy 這樣優秀的爬蟲框架,為何還要造輪子呢?嗯,其實最主要的還是想要將學習到 Python 知識綜合起來,提高一下自己。
Features
簡單、易用;
易於定製的 Spider ;
多線程實現並發下載。
待改進
更多的測試代碼;
添加更多的網站爬蟲示例;
完善爬蟲調度,支持 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 (生產者+消費者模型)
引擎啟動時會啟動一個後台線程池,後台線程池負責下載由調度器提供給它的所有 URL (Request),並將響應(Response)結果存放到隊列中;
引擎的前台解析線程會不斷消費處理隊列中的響應(Response),並調用相應 Spider 的解析函數處理這些相應;
引擎負責處頁面理解析回來的對象,所有的
對象都會被放到隊列中(遞歸抓取時)等待處理,所有的字典對象(item)送給 Spider 的Request
方法處理。process_item
配置介紹
配置項目
: 每批次之間的下載延遲(單位為秒),默認為 0;download_delay
:下載等待延遲,默認為 6 秒;download_timeout
:即當下載超時後,對應的請求是否應該重試;retry_on_timeout
:並發下載數;concurrent_requests
:請求隊列大小,當隊列已滿時,會阻塞後續的請求。queue_size
示例配置:
settings
=
{
"download_delay"
:
0
,
"download_timeout"
:
6
,
"retry_on_timeout"
:
True
,
"concurrent_requests"
:
32
,
"queue_size"
:
512
}
Spider 基類關鍵方法介紹
:該方法會在引擎啟動時被觸發調用,你可以通過繼承該方法進行一些初始化工作,比如配置 pipeline 輸出文件或者資料庫連接等等;spider_started
:該方法會在引擎處理空閑狀態(即沒有任何 requests 在隊列)時被觸發調用,你可以通過繼承該方法給引擎添加新的請求等(使用spider_idle
即可);self
.
crawler
.
crawl
(
new_request
,
spider
=
self
)
:該方法會在引擎關閉時觸發調用,你可以通過繼承該方法並在 Spider 結束工作前做一些清理工作,如關閉文件管道、關閉資料庫連接等;spider_stopped
:該方法會為引擎提該 Spider 的對應種子請求;start_requests
:該方法會為你的 URL 創建一個 Request 對象;make_requests_from_url
:該方法為請求的默認解析函數回調,當然你可以可以在創建 Request 時指定其它的回調函數;parse
:每當引擎處理一個 Spider 對應的請求時,該方法會被觸發調用,你可以通過繼承該方法對 request 做些設置,比如更換隨機的 User - Agent ,替換 Cookies 或者代理等;當然,你可以將 request 設置為 None 從而忽略該請求;process_request
:每當引擎處理一個 Spider 對應的響應時,該方法會被觸發調用;proccess_response
:每當引擎處理一個 Spider 對應的 item 時,該方法會被觸發調用,你可以通過繼承該方法將抓取並解析到的 item 存儲到資料庫或者本地文件中。process_item
注意
你可以在一個 Crawler 進程中裝入不同的 Spider class ,但需要保證不同的 Spider 的名稱也要不同,否則會被引擎拒絕;
需要根據情況調整下載延遲和並發數大小;下載延遲盡量不要太大,否則每批請求可能會等待較長時間才會處理完成,從而影響爬蟲性能;
Windows 下的測試還沒做,我用的是 Ubuntu ,所以如果您有什麼問題,歡迎反饋哈!
安裝
請移步項目主頁 xcrawler (https://github.com/chrisleegit/xcrawler) 下載源碼;
請保證你的安裝環境為
;Python
3.4
+
請使用
安裝即可。pip
3
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
=
[
"http://news.baidu.com/"
]
default_headers
=
{
"User-Agent"
:
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/50.0.2661.102 Safari/537.36"
}
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
()
版權聲明
本文由 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爬取房天下的推薦新樓盤
TAG:Python開發 |