[toc]
開發環境:Python 3.6.0 版本
(當前最新)Scrapy 1.3.2 版本
(當前最新)
爬蟲是定義如何抓取某個網站(或一組網站)的類,包括如何執行抓取(即關注鏈接)以及如何從其網頁中提取結構化數據(即抓取項目)。換句話說,Spider是您定義用于為特定網站(或在某些情況下,一組網站)抓取和解析網頁的自定義行為的位置。
對于爬蟲,循環經歷這樣的事情:
您首先生成用于抓取第一個URL的初始請求,然后指定要使用從這些請求下載的響應調用的回調函數。
第一個執行的請求通過調用 start_requests()(默認情況下)Request為在start_urls和中指定的URL生成的parse方法獲取, 并且該方法作為請求的回調函數。
在回調函數中,您將解析響應(網頁),并返回帶有提取的數據,Item對象, Request對象或這些對象的可迭代的對象。這些請求還將包含回調(可能是相同的),然后由Scrapy下載,然后由指定的回調處理它們的響應。
在回調函數中,您通常使用選擇器來解析頁面內容 (但您也可以使用BeautifulSoup,lxml或您喜歡的任何機制),并使用解析的數據生成項目。
最后,從爬蟲返回的項目通常將持久存儲到數據庫(在某些項目管道中)或使用Feed導出寫入文件。
即使這個循環(或多或少)適用于任何種類的爬蟲,有不同種類的默認爬蟲捆綁到Scrapy中用于不同的目的。我們將在這里談論這些類型。
class scrapy.spiders.Spider
這是最簡單的爬蟲,每個其他爬蟲必須繼承的爬蟲(包括與Scrapy捆綁在一起的爬蟲,以及你自己寫的爬蟲)。它不提供任何特殊功能。它只是提供了一個默認start_requests()
實現,它從start_urlsspider
屬性發送請求,并parse
為每個結果響應調用spider
的方法。
name
定義此爬蟲名稱的字符串。爬蟲名稱是爬蟲如何由Scrapy定位(和實例化),因此它必須是唯一的。但是,沒有什么能阻止你實例化同一個爬蟲的多個實例。這是最重要的爬蟲屬性,它是必需的。
如果爬蟲抓取單個域名,通常的做法是在域后面命名爬蟲。因此,例如,抓取的爬蟲mywebsite.com通常會被調用 mywebsite。
注意
在Python 2中,這必須是ASCII。
allowed_domains
允許此爬蟲抓取的域的字符串的可選列表,指定一個列表可以抓取,其它就不會抓取了。
start_urls
當沒有指定特定網址時,爬蟲將開始抓取的網址列表。
custom_settings
運行此爬蟲時將從項目寬配置覆蓋的設置字典。它必須定義為類屬性,因為設置在實例化之前更新。
有關可用內置設置的列表,請參閱: 內置設置參考。
crawler
此屬性from_crawler()在初始化類后由類方法設置,并鏈接Crawler到此爬蟲實例綁定到的對象。
Crawlers在項目中封裝了很多組件,用于單個條目訪問(例如擴展,中間件,信號管理器等)。有關詳情,請參閱抓取工具API。
settings
運行此爬蟲的配置。這是一個 Settings實例,有關此主題的詳細介紹,請參閱設置主題。
logger
用Spider創建的Python記錄器name
。您可以使用它通過它發送日志消息,如記錄爬蟲程序中所述。
from_crawler
(crawler, args,* kwargs )
是Scrapy用來創建爬蟲的類方法。
您可能不需要直接覆蓋這一點,因為默認實現充當方法的代理,init()
使用給定的參數args和命名參數kwargs調用它。
盡管如此,此方法 在新實例中設置crawler和settings屬性,以便以后可以在爬蟲程序中訪問它們。
參數:
crawler(Crawlerinstance) - 爬蟲將綁定到的爬蟲
args(list) - 傳遞給init()方法的參數
kwargs(dict) - 傳遞給init()方法的關鍵字參數
start_requests()
此方法必須返回一個可迭代的第一個請求來抓取這個爬蟲。
有了start_requests(),就不寫了start_urls,寫了也沒有用。
默認實現是:start_urls,但是可以復寫的方法start_requests。
例如,如果您需要通過使用POST請求登錄來啟動,您可以:
class MySpider(scrapy.Spider): name = 'myspider' 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): # here you would extract links to follow and return Requests for # each of them, with another callback pass
make_requests_from_url(url)
一種接收URL并返回Request 對象(或Request對象列表)進行抓取的方法。此方法用于在方法中構造初始請求 start_requests(),并且通常用于將URL轉換為請求。
除非重寫,此方法返回具有方法的Requests parse() 作為它們的回調函數,并啟用dont_filter參數(Request有關更多信息,請參閱類)。
parse(response)
這是Scrapy用于處理下載的響應的默認回調,當它們的請求沒有指定回調時。
該parse方法負責處理響應并返回所抓取的數據或更多的URL。其他請求回調具有與Spider類相同的要求。
此方法以及任何其他請求回調必須返回一個可迭代的Request和dicts或Item對象。
參數:
response(Response) - 解析的響應
log(message[, level, component])
包裝器通過爬蟲發送日志消息logger,保持向后兼容性。有關詳細信息,請參閱 從Spider記錄。
closed(reason)
當爬蟲關閉時召喚。此方法為spider_closed信號的signals.connect()提供了一個快捷方式。
讓我們看一個例子:
import scrapy class MySpider(scrapy.Spider): name = 'example.com' allowed_domains = ['example.com'] start_urls = [ 'http://www.example.com/1.html', 'http://www.example.com/2.html', 'http://www.example.com/3.html', ] def parse(self, response): self.logger.info('A response from %s just arrived!', response.url)
從單個回調中返回多個請求和項:
import scrapy class MySpider(scrapy.Spider): name = 'example.com' allowed_domains = ['example.com'] start_urls = [ 'http://www.example.com/1.html', 'http://www.example.com/2.html', 'http://www.example.com/3.html', ] def parse(self, response): for h3 in response.xpath('//h3').extract(): yield {"title": h3} for url in response.xpath('//a/@href').extract(): yield scrapy.Request(url, callback=self.parse)
你可以直接使用start_requests(),而不是start_urls; 項目可以更加方便獲取數據:
import scrapy from myproject.items import MyItem class MySpider(scrapy.Spider): name = 'example.com' allowed_domains = ['example.com'] def start_requests(self): yield scrapy.Request('http://www.example.com/1.html', self.parse) yield scrapy.Request('http://www.example.com/2.html', self.parse) yield scrapy.Request('http://www.example.com/3.html', self.parse) def parse(self, response): for h3 in response.xpath('//h3').extract(): yield MyItem(title=h3) for url in response.xpath('//a/@href').extract(): yield scrapy.Request(url, callback=self.parse)
爬蟲可以接收修改其行為的參數。爬蟲參數的一些常見用法是定義起始URL或將爬網限制到網站的某些部分,但它們可用于配置爬蟲的任何功能。
Spider crawl參數使用該-a選項通過命令 傳遞。例如:
scrapy crawl myspider -a category=electronics
爬蟲可以在他們的init方法中訪問參數:
import scrapy class MySpider(scrapy.Spider): name = 'myspider' def init(self, category=None, *args, **kwargs): super(MySpider, self).init(*args, **kwargs) self.start_urls = ['http://www.example.com/categories/%s' % category] # ...
默認的init方法將獲取任何爬蟲參數,并將它們作為屬性復制到爬蟲。上面的例子也可以寫成如下:
import scrapy class MySpider(scrapy.Spider): name = 'myspider' def start_requests(self): yield scrapy.Request('http://www.example.com/categories/%s' % self.category)
請記住,spider參數只是字符串。爬蟲不會自己做任何解析。如果要從命令行設置start_urls屬性,則必須將它自己解析為列表,使用像 ast.literal_eval 或json.loads之類的屬性 ,然后將其設置為屬性。否則,你會導致迭代一個start_urls字符串(一個非常常見的python陷阱),導致每個字符被看作一個單獨的url。
有效的用例是設置使用的http驗證憑據HttpAuthMiddleware 或用戶代理使用的用戶代理UserAgentMiddleware:scrapy crawl myspider -a http_user=myuser -a http_pass=mypassword -a user_agent=mybot
Spider參數也可以通過Scrapyd schedule.jsonAPI 傳遞。請參閱Scrapyd文檔。
Scrapy附帶一些有用的通用爬蟲,你可以使用它來子類化你的爬蟲。他們的目的是為一些常見的抓取案例提供方便的功能,例如根據某些規則查看網站上的所有鏈接,從站點地圖抓取或解析XML / CSV Feed。
對于在以下爬蟲中使用的示例,我們假設您有一個TestItem
在myproject.items
模塊中聲明的項目:
import scrapy class TestItem(scrapy.Item): id = scrapy.Field() name = scrapy.Field() description = scrapy.Field()
類 scrapy.spiders.CrawlSpider
這是最常用的爬行常規網站的爬蟲,因為它通過定義一組規則為下列鏈接提供了一種方便的機制。它可能不是最適合您的特定網站或項目,但它是足夠通用的幾種情況,所以你可以從它開始,根據需要覆蓋更多的自定義功能,或只是實現自己的爬蟲。
除了從Spider繼承的屬性(你必須指定),這個類支持一個新的屬性:
rules
它是一個(或多個)Rule
對象的列表。每個都Rule
定義了抓取網站的某種行為。規則對象如下所述。如果多個規則匹配相同的鏈接,則將根據它們在此屬性中定義的順序使用第一個。
這個爬蟲還暴露了可覆蓋的方法:
parse_start_url(response)
對于start_urls響應調用此方法。它允許解析初始響應,并且必須返回Item
對象,Request
對象或包含任何對象的迭代器。
class scrapy.spiders.Rule(link_extractor,callback = None,cb_kwargs = None,follow = None,process_links = None,process_request = None )
link_extractor
是一個鏈接提取程序對象,它定義如何從每個爬網頁面提取鏈接。
callback
是一個可調用的或字符串(在這種情況下,將使用具有該名稱的爬蟲對象的方法),以便為使用指定的link_extractor提取的每個鏈接調用。這個回調接收一個響應作為其第一個參數,并且必須返回一個包含Item和 Request對象(或它們的任何子類)的列表。
警告
當編寫爬網爬蟲規則時,避免使用parse
作為回調,因為CrawlSpider
使用parse
方法本身來實現其邏輯。所以如果你重寫的parse
方法,爬行爬蟲將不再工作。
cb_kwargs
是包含要傳遞給回調函數的關鍵字參數的dict。
follow
是一個布爾值,它指定是否應該從使用此規則提取的每個響應中跟蹤鏈接。如果callback
是None follow
默認為True
,否則默認為False
。
process_links
是一個可調用的或一個字符串(在這種情況下,將使用具有該名稱的爬蟲對象的方法),將使用指定從每個響應提取的每個鏈接列表調用該方法link_extractor
。這主要用于過濾目的。
process_request
是一個可調用的或一個字符串(在這種情況下,將使用具有該名稱的爬蟲對象的方法),它將被此規則提取的每個請求調用,并且必須返回一個請求或無(過濾出請求) 。
現在讓我們來看一個CrawlSpider的例子:
import scrapy from scrapy.spiders import CrawlSpider, Rule from scrapy.linkextractors import LinkExtractor class MySpider(CrawlSpider): name = 'example.com' allowed_domains = ['example.com'] start_urls = ['http://www.example.com'] rules = ( # Extract links matching 'category.php' (but not matching 'subsection.php') # and follow links from them (since no callback means follow=True by default). Rule(LinkExtractor(allow=('category.php', ), deny=('subsection.php', ))), # Extract links matching 'item.php' and parse them with the spider's method parse_item Rule(LinkExtractor(allow=('item.php', )), callback='parse_item'), ) def parse_item(self, response): self.logger.info('Hi, this is an item page! %s', response.url) item = scrapy.Item() item['id'] = response.xpath('//td[@id="item_id"]/text()').re(r'ID: (d+)') item['name'] = response.xpath('//td[@id="item_name"]/text()').extract() item['description'] = response.xpath('//td[@id="item_description"]/text()').extract() return item
這個爬蟲會開始抓取example.com的主頁,收集類別鏈接和項鏈接,用parse_item方法解析后者。對于每個項目響應,將使用XPath從HTML中提取一些數據,并將Item使用它填充。
class scrapy.spiders.XMLFeedSpider
XMLFeedSpider設計用于通過以特定節點名稱迭代XML訂閱源來解析XML訂閱源。迭代器可以選自:iternodes,xml和html。iternodes為了性能原因,建議使用迭代器,因為xml和迭代器html一次生成整個DOM為了解析它。但是,html當使用壞標記解析XML時,使用作為迭代器可能很有用。
要設置迭代器和標記名稱,必須定義以下類屬性:
iterator
定義要使用的迭代器的字符串。它可以是:
'iternodes'
- 基于正則表達式的快速迭代器
'html'
- 使用的迭代器Selector。請記住,這使用DOM解析,并且必須加載所有DOM在內存中,這可能是一個大飼料的問題
'xml'
- 使用的迭代器Selector。請記住,這使用DOM解析,并且必須加載所有DOM在內存中,這可能是一個大飼料的問題
它默認為:'iternodes'
。
itertag
一個具有要迭代的節點(或元素)的名稱的字符串。示例:itertag = 'product'
namespaces
定義該文檔中將使用此爬蟲處理的命名空間的元組列表。在 與將用于自動注冊使用的命名空間 的方法。(prefix, uri)prefixuriregister_namespace()
然后,您可以在屬性中指定具有命名空間的itertag 節點。
例:
class YourSpider(XMLFeedSpider): namespaces = [('n', 'http://www.sitemaps.org/schemas/sitemap/0.9')] itertag = 'n:url' # ...
除了這些新的屬性,這個爬蟲也有以下可重寫的方法:
adapt_response(response)
一種在爬蟲開始解析響應之前,在響應從爬蟲中間件到達時立即接收的方法。它可以用于在解析之前修改響應主體。此方法接收響應并返回響應(它可以是相同的或另一個)。
parse_node(response, selector)
對于與提供的標記名稱(itertag)匹配的節點,將調用此方法。接收Selector每個節點的響應和 。覆蓋此方法是必需的。否則,你的爬蟲將不工作。此方法必須返回一個Item對象,一個 Request對象或包含任何對象的迭代器。
process_results(response, results)
對于由爬蟲返回的每個結果(Items or Requests),將調用此方法,并且它將在將結果返回到框架核心之前執行所需的任何最后處理,例如設置項目ID。它接收結果列表和產生那些結果的響應。它必須返回結果列表(Items or Requests)。
這些爬蟲很容易使用,讓我們看一個例子:
from scrapy.spiders import XMLFeedSpider from myproject.items import TestItem class MySpider(XMLFeedSpider): name = 'example.com' allowed_domains = ['example.com'] start_urls = ['http://www.example.com/feed.xml'] iterator = 'iternodes' # This is actually unnecessary, since it's the default value itertag = 'item' def parse_node(self, response, node): self.logger.info('Hi, this is a <%s> node!: %s', self.itertag, ''.join(node.extract())) item = TestItem() item['id'] = node.xpath('@id').extract() item['name'] = node.xpath('name').extract() item['description'] = node.xpath('description').extract() return item
基本上我們做的是創建一個爬蟲,從給定的下載一個start_urls,然后遍歷每個item標簽,打印出來,并存儲一些隨機數據Item。
class scrapy.spiders.CSVF
這個爬蟲非常類似于XMLFeedSpider,除了它迭代行,而不是節點。在每次迭代中調用的方法是parse_row()。
delimiter
CSV文件中每個字段的帶分隔符的字符串默認為','(逗號)。
quotechar
CSV文件中每個字段的包含字符的字符串默認為'"'(引號)。
headers
文件CSV Feed中包含的行的列表,用于從中提取字段。
parse_row(response, row)
使用CSV文件的每個提供(或檢測到)標頭的鍵接收響應和dict(表示每行)。這個爬蟲還給予機會重寫adapt_response和process_results方法的前和后處理的目的。
讓我們看一個類似于前一個例子,但使用 CSVFeedSpider:
from scrapy.spiders import CSVFeedSpider from myproject.items import TestItem class MySpider(CSVFeedSpider): name = 'example.com' allowed_domains = ['example.com'] start_urls = ['http://www.example.com/feed.csv'] delimiter = ';' quotechar = "'" headers = ['id', 'name', 'description'] def parse_row(self, response, row): self.logger.info('Hi, this is a row!: %r', row) item = TestItem() item['id'] = row['id'] item['name'] = row['name'] item['description'] = row['description'] return item
class scrapy.spiders.SitemapSpider
SitemapSpider允許您通過使用Sitemaps發現網址來抓取網站。
它支持嵌套Sitemap和從robots.txt發現Sitemap網址 。
sitemap_urls
指向您要抓取的網址的網站的網址列表。
您還可以指向robots.txt,它會解析為從中提取Sitemap網址。
sitemap_rules
元組列表其中:(regex, callback)
regex是與從Sitemap中提取的網址相匹配的正則表達式。 regex可以是一個str或一個編譯的正則表達式對象。
callback是用于處理與正則表達式匹配的url的回調。callback可以是字符串(指示蜘蛛方法的名稱)或可調用的。
例如:sitemap_rules = [('/product/', 'parse_product')]
規則按順序應用,只有匹配的第一個將被使用。
如果省略此屬性,則會在parse回調中處理在站點地圖中找到的所有網址。
sitemap_follow
應遵循的網站地圖的正則表達式列表。這只適用于使用指向其他Sitemap文件的Sitemap索引文件的網站。
默認情況下,將跟蹤所有網站地圖。
sitemap_alternate_links
指定是否url應遵循一個備用鏈接。這些是在同一個url塊中傳遞的另一種語言的同一網站的鏈接。
例如:
<url> <loc>http://example.com/</loc> <xhtml:link rel="alternate" hreflang="de" href="http://example.com/de"/> </url>
使用sitemap_alternate_linksset
,這將檢索兩個URL。隨著 sitemap_alternate_links
禁用,只有http://example.com/將進行檢索。
默認為sitemap_alternate_links
禁用。
最簡單的示例:使用parse回調處理通過站點地圖發現的所有網址 :
from scrapy.spiders import SitemapSpider class MySpider(SitemapSpider): sitemap_urls = ['http://www.example.com/sitemap.xml'] def parse(self, response): pass # ... scrape item here ...
使用某個回調處理一些網址,并使用不同的回調處理其他網址:
from scrapy.spiders import SitemapSpider class MySpider(SitemapSpider): sitemap_urls = ['http://www.example.com/sitemap.xml'] sitemap_rules = [ ('/product/', 'parse_product'), ('/category/', 'parse_category'), ] def parse_product(self, response): pass # ... scrape product ... def parse_category(self, response): pass # ... scrape category ...
關注robots.txt文件中定義的sitemaps,并且只跟蹤其網址包含/sitemap_shop
以下內容的Sitemap :
from scrapy.spiders import SitemapSpider class MySpider(SitemapSpider): sitemap_urls = ['http://www.example.com/robots.txt'] sitemap_rules = [ ('/shop/', 'parse_shop'), ] sitemap_follow = ['/sitemap_shops'] def parse_shop(self, response): pass # ... scrape shop here ...
將SitemapSpider與其他來源網址結合使用:
from scrapy.spiders import SitemapSpider class MySpider(SitemapSpider): sitemap_urls = ['http://www.example.com/robots.txt'] sitemap_rules = [ ('/shop/', 'parse_shop'), ] other_urls = ['http://www.example.com/about'] def start_requests(self): requests = list(super(MySpider, self).start_requests()) requests += [scrapy.Request(x, self.parse_other) for x in self.other_urls] return requests def parse_shop(self, response): pass # ... scrape shop here ... def parse_other(self, response): pass # ... scrape other here ...
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com