Spider設置:
在該目錄運行下面命令
E:\py\Quotetutorial\Quotetutorial>scrapy3 genspider zhihu www.zhihu.com 生成zhihu.py,並且域名爲www.zhihu.com
E:\py\Quotetutorial\Quotetutorial>scrapy3 crawl zhihu 運行zhihu.py
start_requests
()該方法必須返回一個可迭代對象(iterable)。該對象包含了spider用於爬取的第一個Request。
當spider啓動爬取並且未制定URL時,該方法被調用。 當指定了URL時,make_requests_from_url()
將被調用來創建Request對象。 該方法僅僅會被Scrapy調用一次,因此您可以將其實現爲生成器。
該方法的默認實現是使用 start_urls
的url生成Request。
如果您想要修改最初爬取某個網站的Request對象,您可以重寫(override)該方法。 例如,如果您需要在啓動時以POST登錄某個網站,你可以這麼寫:
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爲request。
默認未被複寫(overridden)的情況下,該方法返回的Request對象中, parse()
作爲回調函數,dont_filter參數也被設置爲開啓。 (詳情參見 Request
).
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。
Spider參數
Spider可以通過接受參數來修改其功能。 spider參數一般用來定義初始URL或者指定限制爬取網站的部分。 您也可以使用其來配置spider的任何功能。
在運行 crawl
時添加 -a
可以傳遞Spider參數:
scrapy crawl myspider -a category=electronics
Spider在構造器(constructor)中獲取參數:
import scrapy
class MySpider(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]
# ...
Spider參數也可以通過Scrapyd的 schedule.json
API來傳遞。 參見 Scrapyd documentation.
編寫你自己的item pipeline
編寫你自己的item pipeline很簡單,每個item pipeline組件是一個獨立的Python類,同時必須實現以下方法:
process_item
(self, item, spider)每個item pipeline組件都需要調用該方法,這個方法必須返回一個
Item
(或任何繼承類)對象, 或是拋出DropItem
異常,被丟棄的item將不會被之後的pipeline組件所處理。參數:
此外,他們也可以實現以下方法:
open_spider
(self, spider)當spider被開啓時,這個方法被調用。
參數: spider ( Spider
對象) – 被開啓的spider
close_spider
(spider)當spider被關閉時,這個方法被調用
參數: spider ( Spider
對象) – 被關閉的spider
from_crawler
(cls, crawler)If present, this classmethod is called to create a pipeline instance from a
Crawler
. It must return a new instance of the pipeline. Crawler object provides access to all Scrapy core components like settings and signals; it is a way for pipeline to access them and hook its functionality into Scrapy.參數: crawler ( Crawler
object) – crawler that uses this pipeline
Item pipeline 樣例
驗證價格,同時丟棄沒有價格的item
讓我們來看一下以下這個假設的pipeline,它爲那些不含稅(price_excludes_vat
屬性)的item調整了 price
屬性,同時丟棄了那些沒有價格的item:
from scrapy.exceptions import DropItem
class PricePipeline(object):
vat_factor = 1.15
def process_item(self, item, spider):
if item['price']:
if item['price_excludes_vat']:
item['price'] = item['price'] * self.vat_factor
return item
else:
raise DropItem("Missing price in %s" % item)
將item寫入JSON文件
以下pipeline將所有(從所有spider中)爬取到的item,存儲到一個獨立地 items.jl
文件,每行包含一個序列化爲JSON格式的item:
import json
class JsonWriterPipeline(object):
def __init__(self):
self.file = open('items.jl', 'wb')
def process_item(self, item, spider):
line = json.dumps(dict(item)) + "\n"
self.file.write(line)
return item
註解
JsonWriterPipeline的目的只是爲了介紹怎樣編寫item pipeline,如果你想要將所有爬取的item都保存到同一個JSON文件, 你需要使用 Feed exports 。
Write items to MongoDB
In this example we’ll write items to MongoDB using pymongo. MongoDB address and database name are specified in Scrapy settings; MongoDB collection is named after item class.
The main point of this example is to show how to use from_crawler()
method and how to clean up the resources properly.
註解
Previous example (JsonWriterPipeline) doesn’t clean up resources properly. Fixing it is left as an exercise for the reader.
import pymongo
class MongoPipeline(object):
def __init__(self, mongo_uri, mongo_db):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db
@classmethod
def from_crawler(cls, crawler):
return cls(
mongo_uri=crawler.settings.get('MONGO_URI'),
mongo_db=crawler.settings.get('MONGO_DATABASE', 'items')
)
def open_spider(self, spider):
self.client = pymongo.MongoClient(self.mongo_uri)
self.db = self.client[self.mongo_db]
def close_spider(self, spider):
self.client.close()
def process_item(self, item, spider):
collection_name = item.__class__.__name__
self.db[collection_name].insert(dict(item))
return item
去重
一個用於去重的過濾器,丟棄那些已經被處理過的item。讓我們假設我們的item有一個唯一的id,但是我們spider返回的多個item中包含有相同的id:
from scrapy.exceptions import DropItem
class DuplicatesPipeline(object):
def __init__(self):
self.ids_seen = set()
def process_item(self, item, spider):
if item['id'] in self.ids_seen:
raise DropItem("Duplicate item found: %s" % item)
else:
self.ids_seen.add(item['id'])
return item
啓用一個Item Pipeline組件
爲了啓用一個Item Pipeline組件,你必須將它的類添加到 ITEM_PIPELINES
配置,就像下面這個例子:
ITEM_PIPELINES = {
'myproject.pipelines.PricePipeline': 300,
'myproject.pipelines.JsonWriterPipeline': 800,
}
分配給每個類的整型值,確定了他們運行的順序,item按數字從低到高的順序,通過pipeline,通常將這些數字定義在0-1000範圍內。
下載器中間件(Downloader Middleware)
下載器中間件是介於Scrapy的request/response處理的鉤子框架。 是用於全局修改Scrapy request和response的一個輕量、底層的系統。
編寫您自己的下載器中間件
編寫下載器中間件十分簡單。每個中間件組件是一個定義了以下一個或多個方法的Python類:
- class
scrapy.contrib.downloadermiddleware.
DownloaderMiddleware
process_request
(request, spider)當每個request通過下載中間件時,該方法被調用。
process_request()
必須返回其中之一: 返回None
、返回一個Response
對象、返回一個Request
對象或raiseIgnoreRequest
。如果其返回
None
,Scrapy將繼續處理該request,執行其他的中間件的相應方法,直到合適的下載器處理函數(download handler)被調用, 該request被執行(其response被下載)。如果其返回
Response
對象,Scrapy將不會調用 任何 其他的process_request()
或process_exception()
方法,或相應地下載函數; 其將返回該response。 已安裝的中間件的process_response()
方法則會在每個response返回時被調用。如果其返回
Request
對象,Scrapy則停止調用 process_request方法並重新調度返回的request。當新返回的request被執行後, 相應地中間件鏈將會根據下載的response被調用。如果其raise一個
IgnoreRequest
異常,則安裝的下載中間件的process_exception()
方法會被調用。如果沒有任何一個方法處理該異常, 則request的errback(Request.errback
)方法會被調用。如果沒有代碼處理拋出的異常, 則該異常被忽略且不記錄(不同於其他異常那樣)。參數:
process_response
(request, response, spider)process_request()
必須返回以下之一: 返回一個Response
對象、 返回一個Request
對象或raise一個IgnoreRequest
異常。如果其返回一個
Response
(可以與傳入的response相同,也可以是全新的對象), 該response會被在鏈中的其他中間件的process_response()
方法處理。如果其返回一個
Request
對象,則中間件鏈停止, 返回的request會被重新調度下載。處理類似於process_request()
返回request所做的那樣。如果其拋出一個
IgnoreRequest
異常,則調用request的errback(Request.errback
)。 如果沒有代碼處理拋出的異常,則該異常被忽略且不記錄(不同於其他異常那樣)。參數:
process_exception
(request, exception, spider)當下載處理器(download handler)或
process_request()
(下載中間件)拋出異常(包括IgnoreRequest
異常)時, Scrapy調用process_exception()
。process_exception()
應該返回以下之一: 返回None
、 一個Response
對象、或者一個Request
對象。如果其返回
None
,Scrapy將會繼續處理該異常,接着調用已安裝的其他中間件的process_exception()
方法,直到所有中間件都被調用完畢,則調用默認的異常處理。如果其返回一個
Response
對象,則已安裝的中間件鏈的process_response()
方法被調用。Scrapy將不會調用任何其他中間件的process_exception()
方法。如果其返回一個
Request
對象, 則返回的request將會被重新調用下載。這將停止中間件的process_exception()
方法執行,就如返回一個response的那樣。參數:
參考:
http://scrapy-chs.readthedocs.io/zh_CN/latest/topics/spiders.html