scrapy框架中Spider原始碼解析
scrapy框架中Spider原始碼解析
一、scrapy架構
在講解spider類之前,我們先來了解下scrapy這個框架的整體架構
請看下面scrapy工作流程圖
1.scrapy引擎(Scrapy Engine)
引擎負責控制資料流在系統中所有元件中流動,並在相應動作發生時觸發事件。
2.排程器(Scheduler)
排程器從引擎接受request並將他們入隊,以便之後引擎請求他們時提供給引擎
3.下載器(Downloader)
下載器負責獲取頁面資料並提供給引擎,而後提供給spider。
4.蜘蛛(Spiders)
Spider是Scrapy使用者編寫用於分析response並提取item(即獲取到的item)或額外跟進的URL的類。 每個spider負責處理一個特定(或一些)網站。
5.管道(Item Pipeline)
Item Pipeline負責處理被spider提取出來的item。典型的處理有清理、 驗證及持久化(例如存取到資料庫中)
6.下載器中介軟體(Downloader Middlewares)
下載器中介軟體是在引擎及下載器之間的特定鉤子(specific hook),處理Downloader傳遞給引擎的response(也包括引擎傳遞給下載器的Request)。 其提供了一個簡便的機制,通過插入自定義程式碼來擴充套件Scrapy功能。
7.spider中介軟體(Spider Middlewares)
Spider中介軟體是在引擎及Spider之間的特定鉤子(specific hook),處理spider的輸入(response)和輸出(items及requests)。 其提供了一個簡便的機制,通過插入自定義程式碼來擴充套件Scrapy功能。
scrapy架構工作流程(資料流向)
Scrapy中的資料流由執行引擎控制,其過程如下:
1.引擎開啟一個網站(open a domain),找到處理該網站的Spider並向該spider請求第一個要爬取的URL(s)。
2. 引擎從Spider中獲取到第一個要爬取的URL並在排程器(Scheduler)以Request排程。
3. 引擎向排程器請求下一個要爬取的URL。
4. 排程器返回下一個要爬取的URL給引擎,引擎將URL通過下載中介軟體(請求(request)方向)轉發給下載器(Downloader)。
5. 一旦頁面下載完畢,下載器生成一個該頁面的Response,並將其通過下載中介軟體(返回(response)方向)傳送給引擎。
6. 引擎從下載器中接收到Response並通過Spider中介軟體(輸入方向)傳送給Spider處理。
7. Spider處理Response並返回爬取到的Item及(跟進的)新的Request給引擎。
8. 引擎將(Spider返回的)爬取到的Item給Item Pipeline,將(Spider返回的)Request給排程器。
(從第二步)重複直到排程器中沒有更多地request,引擎關閉該網站。
二、spider原始碼解析
Spider是最基本的類,所有爬蟲必須繼承這個類。
Spider類主要用到的函式及呼叫順序為:
(1)init()方法: 初始化爬蟲名字和start_urls列表。
注:這裡爬蟲名稱是必須的,而且必須是唯一的
def __init__(self, name=None, **kwargs):
if name is not None:
self.name = name
elif not g`在這裡插入程式碼片`etattr(self, 'name', None):
raise ValueError("%s must have a name" % type(self).__name__)
self.__dict__.update(kwargs)
if not hasattr(self, 'start_urls'):
self.start_urls = []
(2)start_requests()方法:spider發起請求時會呼叫make_requests_from_url()生成Requests物件交給Scrapy下載並返回Response物件交給解析函式處理。
注:start_requests()方法只調用一次
def start_requests(self):
cls = self.__class__
if method_is_overridden(cls, Spider, 'make_requests_from_url'):
warnings.warn(
"Spider.make_requests_from_url method is deprecated; it "
"won't be called in future Scrapy releases. Please "
"override Spider.start_requests method instead (see %s.%s)." % (
cls.__module__, cls.__name__
),
)
for url in self.start_urls:
yield self.make_requests_from_url(url)
else:
for url in self.start_urls:
yield Request(url, dont_filter=True)
def make_requests_from_url(self, url):
""" This method is deprecated. """
return Request(url, dont_filter=True)
(3)parse()方法:解析下載器返回的response,並返回Item或Requests(需指定回撥函式)。Item傳給Item pipline進行資料的持久化儲存,Requests交由Scrapy下載,並由指定的回撥函式處理,一直進行迴圈,直到處理完所有的資料為止。
重點:這個內容需要我們自己去寫。parse()是預設的Request物件回撥函式,解析返回的response物件, 注意回撥函式的寫法,是函式地址(callback=parse或者callback=None)。
def parse(self, response):
raise NotImplementedError
三、spider全部原始碼展示及詳情解析
"""
Base class for Scrapy spiders
See documentation in docs/topics/spiders.rst
"""
import logging
import warnings
from scrapy import signals
from scrapy.http import Request
from scrapy.utils.trackref import object_ref
from scrapy.utils.url import url_is_from_spider
from scrapy.utils.deprecate import create_deprecated_class
from scrapy.exceptions import ScrapyDeprecationWarning
from scrapy.utils.deprecate import method_is_overridden
#所有爬蟲的基類,使用者定義的爬蟲必須從這個類繼承
class Spider(object_ref):
"""Base class for scrapy spiders. All spiders must inherit from this
class.
"""
#1、定義spider名字的字串。spider的名字定義了Scrapy如何定位(並初始化)spider,所以其必須是唯一的。
#2、name是spider最重要的屬性,而且是必須的。一般做法是以該網站的域名來命名spider。例如我們在爬取豆瓣讀書爬蟲時使用‘name = "douban_book_spider"’
name = None
custom_settings = None
#初始化爬蟲名字和start_urls列表。上面已經提到。
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__)
self.__dict__.update(kwargs)
#初始化start_urls列表,當沒有指定的URL時,spider將從該列表中開始進行爬取。 因此,第一個被獲取到的頁面的URL將是該列表之一,後續的URL將會從獲取到的資料中提取。
if not hasattr(self, 'start_urls'):
self.start_urls = []
@property
def logger(self):
logger = logging.getLogger(self.name)
return logging.LoggerAdapter(logger, {'spider': self})
def log(self, message, level=logging.DEBUG, **kw):
"""Log the given message at the given log level
This helper wraps a log call to the logger within the spider, but you
can use it directly (e.g. Spider.logger.info('msg')) or use any other
Python logger too.
"""
self.logger.log(level, message, **kw)
@classmethod
def from_crawler(cls, crawler, *args, **kwargs):
spider = cls(*args, **kwargs)
spider._set_crawler(crawler)
return spider
def set_crawler(self, crawler):
warnings.warn("set_crawler is deprecated, instantiate and bound the "
"spider to this crawler with from_crawler method "
"instead.",
category=ScrapyDeprecationWarning, stacklevel=2)
assert not hasattr(self, 'crawler'), "Spider already bounded to a " \
"crawler"
self._set_crawler(crawler)
def _set_crawler(self, crawler):
self.crawler = crawler
self.settings = crawler.settings
crawler.signals.connect(self.close, signals.spider_closed)
#該方法將讀取start_urls列表內的地址,為每一個地址生成一個Request物件,並返回這些物件的迭代器。
#注意:該方法只會呼叫一次。
def start_requests(self):
cls = self.__class__
if method_is_overridden(cls, Spider, 'make_requests_from_url'):
warnings.warn(
"Spider.make_requests_from_url method is deprecated; it "
"won't be called in future Scrapy releases. Please "
"override Spider.start_requests method instead (see %s.%s)." % (
cls.__module__, cls.__name__
),
)
for url in self.start_urls:
yield self.make_requests_from_url(url)
else:
for url in self.start_urls:
yield Request(url, dont_filter=True)
#1、start_requests()中呼叫,實際生成Request的函式。
#2、Request物件預設的回撥函式為parse(),提交的方式為get。
def make_requests_from_url(self, url):
""" This method is deprecated. """
return Request(url, dont_filter=True)
#預設的Request物件回撥函式,處理返回的response。
#生成Item或者Request物件。這個類需要我們自己去實現。
def parse(self, response):
raise NotImplementedError
@classmethod
def update_settings(cls, settings):
settings.setdict(cls.custom_settings or {}, priority='spider')
@classmethod
def handles_request(cls, request):
return url_is_from_spider(request.url, cls)
@staticmethod
def close(spider, reason):
closed = getattr(spider, 'closed', None)
if callable(closed):
return closed(reason)
def __str__(self):
return "<%s %r at 0x%0x>" % (type(self).__name__, self.name, id(self))
__repr__ = __str__
BaseSpider = create_deprecated_class('BaseSpider', Spider)
class ObsoleteClass(object):
def __init__(self, message):
self.message = message
def __getattr__(self, name):
raise AttributeError(self.message)
spiders = ObsoleteClass(
'"from scrapy.spider import spiders" no longer works - use '
'"from scrapy.spiderloader import SpiderLoader" and instantiate '
'it with your project settings"'
)
# Top-level imports
from scrapy.spiders.crawl import CrawlSpider, Rule
from scrapy.spiders.feed import XMLFeedSpider, CSVFeedSpider
from scrapy.spiders.sitemap import SitemapSpider
作者:小怪聊職場
連結:https://www.jianshu.com/p/d492adf17312
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。
注:某些內容參考自csdn部分大佬部落格