Scrapy-spiders(爬虫)
?
Scrapy Spiders(中文版):http://doc.scrapy.org/en/latest/topics/spiders.html
Scrapy Spiders(中文版):https://scrapy-chs.readthedocs.io/zh_CN/latest/topics/spiders.html
Scrapy 1.3 文檔(中文版):https://oner-wv.gitbooks.io/scrapy_zh/content/基本概念/爬蟲.html
?
?
?
爬蟲(Spiders)
?
Spider 類定義了如何爬取某個(或某些)網站。包括了爬取的動作(例如:是否跟進鏈接)以及如何從網頁的內容中提取結構化數據(爬取item)。 換句話說,Spider就是您定義爬取的動作及分析某個網頁(或者是有些網頁)的地方。
對spider來說,爬取的循環類似下文:
以初始的URL初始化Request,并設置回調函數。 當該request下載完畢并返回時,將生成response,并作為參數傳給該回調函數。
spider 中初始的 request 是通過調用?start_requests()?來獲取的。?start_requests()?讀取?start_urls?中的URL, 并以?parse?為回調函數生成?Request?。
在回調函數內分析返回的(網頁)內容,返回?Item?對象或者?Request?或者一個包括二者的可迭代容器。 返回的Request對象之后會經過Scrapy處理,下載相應的內容,并調用設置的callback函數(函數可相同)。
在回調函數內,您可以使用?選擇器(Selectors)?(您也可以使用BeautifulSoup, lxml 或者您想用的任何解析器) 來分析網頁內容,并根據分析的數據生成item。
最后,由spider返回的item將被存到數據庫(由某些?Item Pipeline?處理)或使用?Feed exports?存入到文件中。
雖然該循環對任何類型的spider都(多少)適用,但Scrapy仍然為了不同的需求提供了多種默認spider。 之后將討論這些spider。
?
?
scrapy.Spider
?
class?scrapy.spiders.Spider
這是最簡單的爬蟲,每個其他爬蟲必須繼承該類(包括 Scrapy 自帶的一些爬蟲,以及你自己寫的爬蟲)。它不提供任何特殊功能。它只是提供了一個默認的?start_requests()?實現,它讀取并請求爬蟲的?start_urls?屬性,并為每個結果響應調用爬蟲的?parse?方法。
- name:定義此爬蟲名稱的字符串。爬蟲名稱是爬蟲如何由 Scrapy 定位(和實例化),因此它必須是唯一的。但是,您可以生成多個相同的爬蟲實例(instance),這沒有任何限制。 name是spider最重要的屬性,而且是必須的。如果蜘蛛抓取單個網站(single domain),一個常見的做法是以該網站(domain)(加或不加?后綴?)來命名spider。 例如,如果爬取?mywebsite.com?,該爬蟲通常會被命名為?mywebsite?。
- allowed_domains:可選。包含了此爬蟲允許抓取的域的列表。 Requests for URLs not belonging to the domain names specified in this list won’t be followed if OffsiteMiddleware is enabled.
- start_urls:URL列表。當沒有指定特定 URL 時,爬蟲將從從該列表中開始抓取。因此,爬取的第一個頁面將是這里列出的某個 URL。后續的 URL 將根據包含在起始 URL 中的數據連續生成。
- custom_settings:該設置是一個dict。運行此爬蟲時改設置會覆蓋項目級的設置。因為設置在實例化(instantiation)之前更新,所以它必須定義為類屬性。有關可用內置設置的列表,請參閱:[ 內置設置參考 ]。
crawler:此屬性由初始化類后的類方法?from_crawler()?設置,并鏈接到此爬蟲實例綁定到的?Crawler?對象。Crawlers在項目中封裝了很多組件,作為單一入口訪問(例如擴展,中間件,信號管理器等)。有關詳情,請參閱?Crawler API。
settings:運行此爬蟲的配置。這是一個?Settings?實例,請參?設置?來了解詳細介紹 。
logger:Python Logger 使用 Spider 的?name?創建。您可以使用它通過它發送日志消息,如從記錄日志記錄。
from_crawler:這是 Scrapy 用來創建 spider 的類方法。您可能不需要直接復寫它,因為默認實現充當?__init __()?方法的代理,使用給定的參數 args 和命名參數 kwargs 調用它。
盡管如此,此方法會在新實例中設置?crawler?和?settings?屬性,以便稍后在爬蟲代碼中訪問它們。
參數:
- crawler(Crawler?實例) -spider將綁定到的crawler
- args(list) - 傳遞給?__init __()?方法的參數
- kwargs(dict) - 傳遞給?__init __()?方法的關鍵字參數
start_requests:此方法必須返回一個可迭代對象(iterable),該對象包含了 spider 用于爬取的第一個Request。當 spider 啟動時且未指定特定的 URL 時,Scrapy 會調用該方法。如果指定了特定的 URL,則使用?make_requests_from_url()?來創建 Request 對象。此方法僅被調用一次,因此將其作為生成器實現是安全的。該方法的默認實現是使用?start_urls?的 url 生成 Request。如果您想要修改最初爬取某個網站的Request對象,您可以重寫(override)該方法。例如,如果您需要在啟動時通過使用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 callbackpassmake_requests_from_url(url):該方法接受一個URL并返回用于爬取的?Request?對象。 該方法在初始化request時被?start_requests()?調用,也被用于轉化url為request。默認未被復寫(overridden)的情況下,該方法返回的Request對象中,?parse()?作為回調函數,dont_filter參數也被設置為開啟。 (詳情參見?Request)。
parse(response):當response沒有指定回調函數時,這是Scrapy用來處理下載的response的默認方法。parse?方法負責處理response并返回所抓取的數據以及(/或)跟進的URL。Spider?對其他的Request的回調函數也有相同的要求。此方法以及任何其他Requestd的回調函數必須返回一個可迭代的?Request?或 dict 或 [Item]?對象。參數:response(Response) - 用于分析的response
log(message[, level, component]):通過 Spider 的?logger?發送日志消息,保留向后兼容性。有關詳細信息,請參閱?Logging from Spiders。
closed(reason):當spider關閉時,該函數被調用。 該方法提供了一個替代調用signals.connect()來監聽 [spider_closed]?信號的快捷方式。
?
讓我們看一個例子:
import scrapyclass 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)在單個回調函數中返回多個 Request 以及 Item 的例子:
import scrapyclass 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_urls?,你也可以直接使用?start_requests()?; 您也可以使用?Items?來給予數據更多的結構性(give data more structure):
import scrapy from myproject.items import MyItemclass 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)?
?
Spider 參數
?
Spider 可以通過接受參數來修改其功能。 spider參數一般用來定義初始URL或者指定限制爬取網站的部分。 您也可以使用其來配置spider的任何功能。
在運行?crawl?時添加?-a?可以傳遞Spider參數:?scrapy crawl myspider -a category=electronics
Spider 在構造器(constructor)中獲取參數:
import scrapyclass 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?方法將獲取任何 spider 參數,并將它們作為 spider 的屬性。上面的例子也可以寫成如下:
import scrapyclass MySpider(scrapy.Spider):name = 'myspider'def start_requests(self):yield scrapy.Request('http://www.example.com/categories/%s' % self.category)請記住,蜘蛛參數只能是字符串。蜘蛛自己不會做任何解析。如果要從命令行設置 start_urls 屬性,則必須使用?ast.literal_eval?或?json.loads?之類將它解析為列表,并將它設置為屬性,然后將其設置為屬性。否則,你會導致迭代一個 start_urls 字符串(一個非常常見的python陷阱),導致每個字符被看作一個單獨的url。
有效的用例是通過?HttpAuthMiddleware?設置 http auth 證書或 通過?UserAgentMiddleware?設置 user agent:
scrapy crawl myspider -a http_user=myuser -a http_pass=mypassword -a user_agent=mybotSpider參數也可以通過Scrapyd的?schedule.json?API來傳遞。 參見?Scrapyd documentation.
?
?
Generic Spiders(生成 Spider)
?
Scrapy 自帶一些有用的通用爬蟲,你可以將自己的爬蟲作為它們的子類。他們的目的是為一些常見的抓取案例提供方便的功能,例如根據某些規則跟蹤網站上的所有鏈接,從?Sitemaps?抓取或解析XML / CSV Feed。
對于在下面的爬蟲中使用的示例,我們假設你有一個項目,在?myproject.items?模塊中聲明一個?TestItem:
import scrapyclass TestItem(scrapy.Item):id = scrapy.Field()name = scrapy.Field()description = scrapy.Field()?
CrawlSpider
class?scrapy.spiders.CrawlSpider
Scrapy基礎——CrawlSpider詳解:https://www.jianshu.com/p/0f64297fc912
這是最常用的爬取常規網站的spider,因為它通過定義一組規則為跟進鏈接提供了一個方便的機制。它可能不是完全適合您的特定網站或項目,但它有足夠多的幾種通用情況,因此您可以以此為起點,根據需要覆蓋更多的自定義功能,當然也可以實現自己的spider。
除了從 Spider 繼承的屬性(您必須指定),這個類支持一個新的屬性:
rules
它是一個(或多個)Rule?對象的列表。每個?Rule?定義用于爬取網址的特定行為。Rule 對象如下所述。如果多個規則匹配相同的鏈接,則將根據它們在此屬性中定義的順序使用第一個。
該spider也提供了一個可復寫(overrideable)的方法:
parse_start_url(response)
對于 start_urls 中url所對應的 response 調用此方法。它允許解析初始響應,并且必須返回?Item?對象,Request?對象或包含其中任何對象的iterable。
?
爬取規則(Crawling rules)
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 對象中提取的每個鏈接將會調用該函數。該回調接函數收一個 response 作為它的第一個參數,并且必須返回一個包含?Item?及(或)[Request]?對象(或它們的任何子類)的列表。
警告!
當編寫爬蟲規則時,避免使用 parse 作為回調,因為?CrawlSpider?使用parse方法本身來實現其邏輯。因此,如果您覆蓋 parse 方法,crawl spider 將會運行失敗。
[cb_kwargs] 是一個包含要傳遞給回調函數(keyword argument)的關鍵字參數的dict。
[follow] 是一個布爾值,指定了根據該規則從response提取的鏈接是否需要跟進。如果 [callable] 為 None,則默認為 True,否則默認為 False。
[process_links] 是一個 callable 或 string(在這種情況下,該spider中同名的函數將會被調用),使用 link_extractor 從 Response 對象中提取的每個鏈接列表調用它。這主要用于過濾目的。
[process_request] 是一個 callable 或 string(在這種情況下,該spider中同名的函數將會被調用),它將被此規則提取的每個 request 調用,并且必須返回一個 request 或None(過濾出 request)。
?
CrawlSpider示例
現在讓我們來看看一個帶有 rule 的 CrawlSpider 示例:
import scrapy from scrapy.spiders import CrawlSpider, Rule from scrapy.linkextractors import LinkExtractorclass 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_itemRule(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該spider將從example.com的首頁開始爬取,獲取category以及item的鏈接并對后者使用 parse_item 方法。 當item獲得返回(response)時,將使用XPath處理HTML并生成一些數據填入 Item 中。
?
?
?
?
XMLFeedSpider
class?scrapy.contrib.spiders.XMLFeedSpider
XMLFeedSpider 被設計用于通過迭代各個節點來分析XML源(XML feed)。 迭代器可以從?iternodes?,?xml?,?html?選擇。 鑒于?xml?以及?html?迭代器需要先讀取所有DOM再分析而引起的性能問題, 一般還是推薦使用?iternodes?。 不過使用?html?作為迭代器能有效應對錯誤的XML。
您必須定義下列類屬性來設置迭代器以及標簽名(tag name):
iterator
用于確定使用哪個迭代器的string。可選項有:
- 'iternodes'?- 一個高性能的基于正則表達式的迭代器
- 'html'?- 使用?Selector?的迭代器。 需要注意的是該迭代器使用DOM進行分析,其需要將所有的DOM載入內存, 當數據量大的時候會產生問題。
- 'xml'?- 使用?Selector?的迭代器。 需要注意的是該迭代器使用DOM進行分析,其需要將所有的DOM載入內存, 當數據量大的時候會產生問題。
默認值為?iternodes?。
itertag:一個包含開始迭代的節點名的string。例如:itertag = 'product'
namespaces:一個由?(prefix,?url)?元組(tuple)所組成的list。 其定義了在該文檔中會被spider處理的可用的namespace。?prefix?及?uri?會被自動調用?register_namespace()?生成namespace。您可以通過在?itertag?屬性中制定節點的namespace。例如:
class YourSpider(XMLFeedSpider):namespaces = [('n', 'http://www.sitemaps.org/schemas/sitemap/0.9')]itertag = 'n:url'# ...除了這些新的屬性之外,該spider也有以下可以覆蓋(overrideable)的方法:
adapt_response(response):該方法在spider分析response前被調用。您可以在response被分析之前使用該函數來修改內容(body)。 該方法接受一個response并返回一個response(可以相同也可以不同)。
parse_node(response,?selector):當節點符合提供的標簽名時(itertag)該方法被調用。 接收到的response以及相應的?Selector?作為參數傳遞給該方法。 該方法返回一個?Item?對象或者?Request?對象 或者一個包含二者的可迭代對象(iterable)。
process_results(response,?results):當spider返回結果(item或request)時該方法被調用。 設定該方法的目的是在結果返回給框架核心(framework core)之前做最后的處理, 例如設定item的ID。其接受一個結果的列表(list of results)及對應的response。 其結果必須返回一個結果的列表(list of results)(包含Item或者Request對象)。
?
XMLFeedSpider例子
該spider十分易用。下邊是其中一個例子:
from scrapy.spiders import XMLFeedSpider from myproject.items import TestItemclass 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 valueitertag = 'item'def parse_node(self, response, node):self.logger.info('Hi, this is a <%s> node!: %s', self.itertag, ''.join(node.getall()))item = TestItem()item['id'] = node.xpath('@id').get()item['name'] = node.xpath('name').get()item['description'] = node.xpath('description').get()return item簡單來說,我們在這里創建了一個spider,從給定的?start_urls?中下載feed, 并迭代feed中每個?item?標簽,輸出,并在?Item?中存儲有些隨機數據。
?
?
CSVFeedSpider
class?scrapy.contrib.spiders.CSVFeedSpider
該spider除了其按行遍歷而不是節點之外其他和XMLFeedSpider十分類似。 而其在每次迭代時調用的是?parse_row()?。
delimiter:在CSV文件中用于區分字段的分隔符。類型為string。 默認為?','?(逗號)。
quotechar:A string with the enclosure character for each field in the CSV file Defaults to?'"'(quotation mark).
headers:在CSV文件中包含的用來提取字段的行的列表。參考下邊的例子。
parse_row(response,?row):該方法接收一個response對象及一個以提供或檢測出來的header為鍵的字典(代表每行)。 該spider中,您也可以覆蓋?adapt_response?及?process_results?方法來進行預處理(pre-processing)及后(post-processing)處理。
?
CSVFeedSpider例子
下面的例子和之前的例子很像,但使用了?CSVFeedSpider:
from scrapy.spiders import CSVFeedSpider from myproject.items import TestItemclass 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?
?
SitemapSpider
class?scrapy.contrib.spiders.SitemapSpider
SitemapSpider 使您爬取網站時可以通過?Sitemaps?來發現爬取的URL。
其支持嵌套的 sitemap,并能從?robots.txt?中獲取sitemap的url。
sitemap_urls:包含您要爬取的url的sitemap的url列表(list)。 您也可以指定為一個?robots.txt?,spider會從中分析并提取url。
sitemap_rules:一個包含?(regex,?callback)?元組的列表(list):
- regex?是一個用于匹配從sitemap提供的url的正則表達式。?regex?可以是一個字符串或者編譯的正則對象(compiled regex object)。
- callback指定了匹配正則表達式的url的處理函數。?callback?可以是一個字符串(spider中方法的名字)或者是callable。
例如:sitemap_rules = [('/product/', 'parse_product')] 。規則按順序進行匹配,之后第一個匹配才會被應用。如果您忽略該屬性,sitemap中發現的所有url將會被?parse?函數處理。
sitemap_follow:一個用于匹配要跟進的sitemap的正則表達式的列表(list)。其僅僅被應用在 使用?Sitemap index files?來指向其他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_links?設置時,兩個URL都會被獲取。 當?sitemap_alternate_links?關閉時,有?http://example.com/?會被獲取。默認?sitemap_alternate_links?關閉。
?
sitemap_filter(entries):This is a filter funtion that could be overridden to select sitemap entries based on their attributes.
例如:
<url><loc>http://example.com/</loc><lastmod>2005-01-01</lastmod> </url>We can define a?sitemap_filter?function to filter?entries?by date:
from datetime import datetime from scrapy.spiders import SitemapSpiderclass FilteredSitemapSpider(SitemapSpider):name = 'filtered_sitemap_spider'allowed_domains = ['example.com']sitemap_urls = ['http://example.com/sitemap.xml']def sitemap_filter(self, entries):for entry in entries:date_time = datetime.strptime(entry['lastmod'], '%Y-%m-%d')if date_time.year >= 2005:yield entryThis would retrieve only?entries?modified on 2005 and the following years.
Entries are dict objects extracted from the sitemap document. Usually, the key is the tag name and the value is the text inside it.
It’s important to notice that:
- as the loc attribute is required, entries without this tag are discarded
- alternate links are stored in a list with the key?alternate?(see?sitemap_alternate_links)
- namespaces are removed, so lxml tags named as?{namespace}tagname?become only?tagname
If you omit this method, all entries found in sitemaps will be processed, observing other attributes and their settings.
?
SitemapSpider 樣例
簡單的例子: 使用?parse?處理通過sitemap發現的所有url:
from scrapy.spiders import SitemapSpiderclass MySpider(SitemapSpider):sitemap_urls = ['http://www.example.com/sitemap.xml']def parse(self, response):pass # ... scrape item here ...用特定的函數處理某些url,其他的使用另外的callback:
from scrapy.spiders import SitemapSpiderclass 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?文件定義的sitemap并只跟進包含有?..sitemap_shop?的url:
from scrapy.spiders import SitemapSpiderclass 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 中使用其他 url:
from scrapy.spiders import SitemapSpiderclass 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 requestsdef parse_shop(self, response):pass # ... scrape shop here ...def parse_other(self, response):pass # ... scrape other here ...?
?
?
總結
以上是生活随笔為你收集整理的Scrapy-spiders(爬虫)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: adb(Android debug br
- 下一篇: 数据链路层(学习笔记)