1. Spider
Spider類定義瞭如何爬取某個(或某些)網站。包括了爬取的動作(例如:是否跟進連結)以及如何從網頁的內容中提取結構化資料(爬取item)。 換句話說,Spider就是您定義爬取的動作及分析某個網頁(或者是有些網頁)的地方。
class scrapy.Spider是最基本的類,所有編寫的爬蟲必須繼承這個類。
主要用到的函式及呼叫順序為:
-
init() : 初始化爬蟲名字和start_urls列表
-
start_requests() 呼叫make_requests_from url():生成Requests物件交給Scrapy下載並返回response
-
parse() : 解析response,並返回Item或Requests(需指定回撥函式)。Item傳給Item pipline持久化 , 而Requests交由Scrapy下載,並由指定的回撥函式處理(預設parse()),一直進行迴圈,直到處理完所有的資料為止。
2.Scrapy原始碼
#所有爬蟲的基類,使用者定義的爬蟲必須從這個類繼承
class Spider(object_ref):
#定義spider名字的字串(string)。spider的名字定義了Scrapy如何定位(並初始化)spider,所以其必須是唯一的。
#name是spider最重要的屬性,而且是必須的。
#一般做法是以該網站(domain)(加或不加 字尾 )來命名spider。 例如,如果spider爬取 mywebsite.com ,該spider通常會被命名為 mywebsite
name = None
#初始化,提取爬蟲名字,start_ruls
def __init__(self, name=None, **kwargs):
if name is not None:
self.name = name
# 如果爬蟲沒有名字,中斷後續操作則報錯
elif not getattr(self, `name`, None):
raise ValueError("%s must have a name" % type(self).__name__)
# python 物件或型別通過內建成員__dict__來儲存成員資訊
self.__dict__.update(kwargs)
#URL列表。當沒有指定的URL時,spider將從該列表中開始進行爬取。 因此,第一個被獲取到的頁面的URL將是該列表之一。 後續的URL將會從獲取到的資料中提取。
if not hasattr(self, `start_urls`):
self.start_urls = []
# 列印Scrapy執行後的log資訊
def log(self, message, level=log.DEBUG, **kw):
log.msg(message, spider=self, level=level, **kw)
# 判斷物件object的屬性是否存在,不存在做斷言處理
def set_crawler(self, crawler):
assert not hasattr(self, `_crawler`), "Spider already bounded to %s" % crawler
self._crawler = crawler
@property
def crawler(self):
assert hasattr(self, `_crawler`), "Spider not bounded to any crawler"
return self._crawler
@property
def settings(self):
return self.crawler.settings
#該方法將讀取start_urls內的地址,併為每一個地址生成一個Request物件,交給Scrapy下載並返回Response
#該方法僅呼叫一次
def start_requests(self):
for url in self.start_urls:
yield self.make_requests_from_url(url)
#start_requests()中呼叫,實際生成Request的函式。
#Request物件預設的回撥函式為parse(),提交的方式為get
def make_requests_from_url(self, url):
return Request(url, dont_filter=True)
#預設的Request物件回撥函式,處理返回的response。
#生成Item或者Request物件。使用者必須實現這個類
def parse(self, response):
raise NotImplementedError
@classmethod
def handles_request(cls, request):
return url_is_from_spider(request.url, cls)
def __str__(self):
return "<%s %r at 0x%0x>" % (type(self).__name__, self.name, id(self))
__repr__ = __str__
2.1. Scrapy主要屬性和方法
- name
定義spider名字的字串。
例如,如果spider爬取 mywebsite.com ,該spider通常會被命名為 mywebsite
- allowed_domains
包含了spider允許爬取的域名(domain)的列表,可選。
- start_urls
初始URL元祖/列表。當沒有制定特定的URL時,spider將從該列表中開始進行爬取。
- start_requests(self)
該方法必須返回一個可迭代物件(iterable)。該物件包含了spider用於爬取(預設實現是>使用 start_urls 的url)的第一個Request。
當spider啟動爬取並且未指定start_urls時,該方法被呼叫。
- parse(self, response)
當請求url返回網頁沒有指定回撥函式時,預設的Request物件回撥函式。用來處理網頁返回的response,以及生成Item或者Request物件。
- log(self, message[, level, component])
使用 scrapy.log.msg() 方法記錄(log)message。 更多資料請參見 logging
3.parse()方法的工作機制
1. 因為使用的yield,而不是return。parse函式將會被當做一個生成器使用。scrapy會逐一獲取parse方法中生成的結果,並判斷該結果是一個什麼樣的型別;
2. 如果是request則加入爬取佇列,如果是item型別則使用pipeline處理,其他型別則返回錯誤資訊。
3. scrapy取到第一部分的request不會立馬就去傳送這個request,只是把這個request放到佇列裡,然後接著從生成器裡獲取;
4. 取盡第一部分的request,然後再獲取第二部分的item,取到item了,就會放到對應的pipeline裡處理;
5. parse()方法作為回撥函式(callback)賦值給了Request,指定parse()方法來處理這些請求 scrapy.Request(url, callback=self.parse)
6. Request物件經過排程,執行生成 scrapy.http.response()的響應物件,並送回給parse()方法,直到排程器中沒有Request(遞迴的思路)
7. 取盡之後,parse()工作結束,引擎再根據佇列和pipelines中的內容去執行相應的操作;
8. 程式在取得各個頁面的items前,會先處理完之前所有的request佇列裡的請求,然後再提取items。
7. 這一切的一切,Scrapy引擎和排程器將負責到底。