Spiders
Spider類定義了如何爬取某個網站。包括了爬取的動作(例如:是否跟進鏈接)以及如何從網頁的內容中提取結構化數據(爬取item)。簡而言之,Spider就是你定義爬取的動作及分析某個網頁(或者是有些網頁)的地方。
對spider來說,爬取的循環類似如下:
-
以初始的URL初始化Request,並設置回調函數。當該request下載完畢並返回時,將生成response,並作為參數傳給該回調函數。spider中初始的request是通過調用start_requests() 來獲取。start_requests() 讀取start_urls中的URL,並以parse為回調函數生成 Request。
-
在回調函數內分析返回的(網頁)內容,返回 Item 對象、dict、 Request 或者一個包括三者的可迭代容器。 返回的Request對象之后會經過Scrapy處理,下載相應的內容,並調用設置的callback函數(函數可相同)。
-
在回調函數內,您可以使用 選擇器(Selectors) (您也可以使用BeautifulSoup, lxml 或者您想用的任何解析器) 來分析網頁內容,並根據分析的數據生成item。
-
最后,由spider返回的item將被存到數據庫(由某些 Item Pipeline 處理)或使用 Feed exports 存入到文件中。
scrapy為我們提供了如下的一些spider類來應對不同的爬取需求。
- scrapy.spiders.Spider
Spider是最簡單的spider。每個其他的spider必須繼承自該類(包括Scrapy自帶其他spider以及你自己編寫的spider)。Spider僅僅提供了 start_requests()的默認實現,讀取並請求spider屬性中的 start_urls, 並根據返回的結果(resulting responses)調用spider的 parse 方法。 - scrapy.spiders.CrawlSpider
爬取一般網站常用的spider。其定義了一些規則(rule)來提供跟進link的方便的機制。比如一些網站的url通常是這樣的http://www.example.com/123.html
,http://www.example.com/456.html
博客類的網站通常會這樣,我們可以直接使用這個類提供的Rule來進行網址匹配。當然我們也可以實現自己的spider。 - scrapy.spiders.XMLFeedSpider
XMLFeedSpider被設計用於通過迭代各個節點來分析XML源(XML feed)。 迭代器可以從 iternodes , xml , html 選擇。 鑒於 xml 以及 html 迭代器需要先讀取所有DOM再分析而引起的性能問題, 一般還是推薦使用 iternodes 。 不過使用 html 作為迭代器能有效應對錯誤的XML。 - scrapy.spiders.CSVFeedSpider
該spider除了其按行遍歷而不是節點之外其他和XMLFeedSpider十分類似。 而其在每次迭代時調用的是 parse_row() 。 - scrapy.spiders.SitemapSpider
SitemapSpider使您爬取網站時可以通過 Sitemaps 來發現爬取的URL。其支持嵌套的sitemap,並能從 robots.txt 中獲取sitemap的url。
這里我們着重學習前面兩種,也是非常常用的兩種。
scrapy.Spider
class scrapy.spiders.Spider
在這之前,我們寫的爬蟲中,其實已經用到了這個類,我們自定義的spider就是繼承自該類,現在我們詳細的了解該類的一些常用的屬性和方法,具體的詳盡的信息可以參照官方文檔。
-
name
定義spider名字的字符串(string)。spider的名字定義了Scrapy如何定位(並初始化)spider,所以其必須是唯一的。name是spider最重要的屬性,而且必須。
一般就是以網站的URL去掉前后綴來命名,如www.baidu.com
我們的name就可以為baidu
,簡單明了。 -
allowed_domains
可選。包含了spider允許爬取的域名(domain)列表(list)。 當 OffsiteMiddleware 啟用時, 域名不在列表中的URL不會被跟進。 -
start_urls
URL列表。當沒有制定特定的URL時,spider將從該列表中開始進行爬取。 因此,第一個被獲取到的頁面的URL將是該列表之一。 后續的URL將會從獲取到的數據中提取。 -
start_requests()
該方法必須返回一個可迭代對象(iterable)。該對象包含了spider用於爬取的第一個Request。該方法僅僅會被Scrapy調用一次,可以將其實現為生成器。
該方法的默認實現是使用 start_urls 的url生成Request。我們可以重寫該方法來實現定制。比如我們想要一開始就實現一個post請求,通過默認的方法可定是不行的。因此我們重寫該方法如下。class MySpider(scrapy.Spider): name = 'myname' def start_requests(self): return [scrapy.FormRequest("http://www.example.com/login", formdata={'user': 'john', 'pass': 'secret'}, callback=self.logged_in)] def logged_in(self, response): print('登陸成功') pass
-
parse(response)
當response沒有指定回調函數時,該方法是Scrapy處理下載的response的默認方法。parse 負責處理response並返回處理的數據以及(/或)跟進的URL。
該方法及其他的Request回調函數必須返回一個包含 Request、dict 或 Item 的可迭代的對象。簡單的來說,所謂的回調函數,其實就是告訴spider,在拿到了網站的response以后,交給誰來進行處理后面的頁面的解析工作。 -
closed(reason)
當spider關閉時,該函數被調用。
常用的Spider的屬性和方法就是這些,下面是一個綜合的例子。
import scrapy
from myproject.items import MyItem
class MySpider(scrapy.Spider):
name = 'cnblog'
allowed_domains = ['cnblogs.com']
start_urls = ['http://www.cnblogs.com.com/123.html',
'http://www.cnblogs.com.com/234.html',
'http://www.cnblogs.com.com/345.html'
]
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(url=url, callback=self.parse)
def parse(self, response):
for h3 in response.xpath('//h3').extract():
item = MyItem()
item['title'] = h3
yield item
for url in response.xpath('//a/@href').extract():
yield scrapy.Request(url, callback=self.parse)
請注意看,我們可以在start_requests()
方法里面請求多個URL,這會形成一個請求隊列,並且可以使用同樣的解析方法對response進行解析,parse()
方法的返回結果可以也僅可以有兩種,官方文檔上面說明是三種,其實item和字典我們算做一種,兩種返回值的例子都包含在上面,一種是item或者說是字典,scrapy會將item交給item pipeline去進行后續的處理,包括數據的清洗,存儲;另一種是Request,此時scrapy會將這個請求放入調度器請求隊列,后續會對其進行請求解析。scrapy的引擎也是通過返回的兩種類型來區別是交給pipeline還是scheduler進行后續的處理。
scrapy.CrawlSpider
class scrapy.spiders.CrawlSpider
在爬取一些特殊類型的網站時,比如一些博客類網站,其網頁的鏈接都會有一些特殊的形式,簡單的例子,博客園的博客內容,http://www.cnblogs.com/cnkai/p/7397421.html
,http://www.cnblogs.com/cnkai/p/7396835.html
比如我們想爬取我的博客內容,會發現,除了最后的幾個數字不同,其他的內容是相同的,因此,我們就可以通過這個類來進行自動抓取相似的鏈接,而無需我們自己定義。
CrawlSpider類定義了如下的屬性和方法。
- rules
一個包含一個(或多個) Rule 對象的集合(list)。 每個 Rule 對爬取網站的動作定義了特定表現。 Rule對象在下邊會介紹。 如果多個rule匹配了相同的鏈接,則根據他們在本屬性中被定義的順序,第一個會被使用。 - parse_start_url(response)
當start_url的請求返回時,該方法被調用。 該方法分析最初的返回值並必須返回一個 Item 對象或者 一個 Request 對象或者 一個可迭代的包含二者對象。
CrawlSpider需要配合scrapy.spiders.Rule類來實現定義規則
下面介紹scrapy.spiders.Rule
類
class scrapy.spiders.Rule(link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)
-
link_extractor
這是一個 Link Extractor 對象。 其定義了如何從爬取到的頁面提取鏈接。 -
callback
這是一個callable或string(該spider中同名的函數將會被調用)。 從link_extractor中每獲取到鏈接時將會調用該函數。該回調函數接受一個response作為其第一個參數, 並返回一個包含 Item 以及(或) Request 對象(或者這兩者的子類)的列表(list) -
cb_kwargs
包含傳遞給回調函數的參數(keyword argument)的字典。 -
follow
是一個布爾(boolean)值,指定了根據該規則從response提取的鏈接是否需要跟進。 如果 callback 為None, follow 默認設置為 True ,否則默認為 False 。 -
process_links
是一個callable或string(該spider中同名的函數將會被調用)。 從link_extractor中獲取到鏈接列表時將會調用該函數。該方法主要用來過濾。 -
process_request
是一個callable或string(該spider中同名的函數將會被調用)。 該規則提取到每個request時都會調用該函數。該函數必須返回一個request或者None。 (用來過濾request)
下面是一個詳細的樣例
import scrapy
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
class MySpider(CrawlSpider):
name = 'cnblog'
allowed_domains = []
start_urls = ['http://www.cnblogs.com']
rules = (
Rule(LinkExtractor(allow=('http://www.cnblogs.com/\w+/p/\d+.html', )), callback='parse_item', follow=True),
)
def parse_item(self, response):
print(response.url)
簡單的解釋:
rules()中的allow=('http://www.cnblogs.com/\w+/p/\d+.html', )
表示我們需要匹配的格式,其中包含了正則表達式,\w+
表示匹配任意的字母數字下划線漢字至少一次,\d+
表示匹配任意的數字至少一次,callback
設置回調函數,follow=True
表示跟進response里面的鏈接,最終的結果就是提取出所有的滿足要求的連接,並打印出來,這個數量會很龐大,如果你的爬蟲沒有被ban的話,這里我簡單的運行一下,可以看到下面的鏈接被打印了出來。且這些鏈接是滿足我們要求的格式。
總結
這一節主要是學習了scrapy的Spiders類,着重學習了scrapy.Spider
和scrapy.CrawlSpider
兩個類,這兩個也是我們用的頻度比較高的兩個類,這兩個需要我們重點的掌握,其他的幾個這里並沒有詳細的解釋,有興趣的可以去查閱scrapy官方文檔,或者在以后需要的時候去查閱也可以。