Spiders
Spider类定义了如何爬取某个(或某些)网站。包括了爬取的动作(例如:是否跟进链接)以及如何从网页的内容中提取结构化数据(爬取item)。 换句话说,Spider就是您定义爬取的动作及分析某个网页(或者是有些网页)的地方。
对spider来说,爬取的循环类似下文:
以初始的URL初始化Request,并设置回调函数。 当该request下载完毕并返回时,将生成response,并作为参数传给该回调函数。
spider中初始的request是通过调用
start_requests()
来获取的。start_requests()
读取start_urls
中的URL, 并以parse
为回调函数生成Request
。在回调函数内分析返回的(网页)内容,返回
Item
对象、dict、Request
或者一个包括三者的可迭代容器。 返回的Request对象之后会经过Scrapy处理,下载相应的内容,并调用设置的callback函数(函数可相同)。在回调函数内,您可以使用 选择器(Selectors) (您也可以使用BeautifulSoup, lxml 或者您想用的任何解析器) 来分析网页内容,并根据分析的数据生成item。
最后,由spider返回的item将被存到数据库(由某些 Item Pipeline 处理)或使用 Feed exports 存入到文件中。
虽然该循环对任何类型的spider都(多少)适用,但Scrapy仍然为了不同的需求提供了多种默认spider。 之后将讨论这些spider。
scrapy.Spider
-
class
-
Spider是最简单的spider。每个其他的spider必须继承自该类(包括Scrapy自带的其他spider以及您自己编写的spider)。 Spider并没有提供什么特殊的功能。 其仅仅提供了
start_requests()
的默认实现,读取并请求spider属性中的start_urls
, 并根据返回的结果(resulting responses)调用spider的parse
方法。-
定义spider名字的字符串(string)。spider的名字定义了Scrapy如何定位(并初始化)spider,所以其必须是唯一的。 不过您可以生成多个相同的spider实例(instance),这没有任何限制。 name是spider最重要的属性,而且是必须的。
如果该spider爬取单个网站(single domain),一个常见的做法是以该网站(domain)(加或不加 后缀 )来命名spider。 例如,如果spider爬取
mywebsite.com
,该spider通常会被命名为mywebsite
。
name
-
该设置是一个dict.当启动spider时,该设置将会覆盖项目级的设置. 由于设置必须在初始化(instantiation)前被更新,所以该属性 必须定义为class属性.
请通过 内置设定参考手册 查看支持的设置.
custom_settings
-
该属性在初始化class后,由类方法
from_crawler()
设置, 并且链接了本spider实例对应的Crawler
对象.Crawler包含了很多项目中的组件,作为单一的入口点 (例如插件,中间件,信号管理器等). 请查看 Crawler API 来了解更多.
crawler
-
Configuration on which this spider is been ran. This is a
Settings
instance, see the Settingstopic for a detailed introduction on this subject.
settings
-
Python logger created with the Spider’s
name
. You can use it to send log messages through it as described on Logging from Spiders.
logger
-
This is the class method used by Scrapy to create your spiders.
You probably won’t need to override this directly, since the default implementation acts as a proxy to the
__init__()
method, calling it with the given arguments args and named arguments kwargs.Nonetheless, this method sets the
crawler
andsettings
attributes in the new instance, so they can be accessed later inside the spider’s code.参数:
from_crawler
( crawler, *args, **kwargs )-
该方法必须返回一个可迭代对象(iterable)。该对象包含了spider用于爬取的第一个Request。
当spider启动爬取并且未制定URL时,该方法被调用。 当指定了URL时,
make_requests_from_url()
将被调用来创建Request对象。 该方法仅仅会被Scrapy调用一次,因此您可以将其实现为生成器。该方法的默认实现是使用
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 callback pass
start_requests
( )-
该方法接受一个URL并返回用于爬取的
Request
对象。 该方法在初始化request时被start_requests()
调用,也被用于转化url为request。默认未被复写(overridden)的情况下,该方法返回的Request对象中,
parse()
作为回调函数,dont_filter参数也被设置为开启。 (详情参见Request
).
make_requests_from_url
( url )-
当response没有指定回调函数时,该方法是Scrapy处理下载的response的默认方法。
parse
负责处理response并返回处理的数据以及(/或)跟进的URL。Spider
对其他的Request的回调函数也有相同的要求。该方法及其他的Request回调函数必须返回一个包含
Request
、dict 或Item
的可迭代的对象。参数: response ( Response
) – 用于分析的response
parse
( response )-
使用
scrapy.log.msg()
方法记录(log)message。 log中自动带上该spider的name
属性。 更多数据请参见 Logging 。 封装了通过Spiders的logger
来发送log消息的方法,并且保持了向后兼容性。 更多内容请参考 Logging from Spiders.
log
( message [, level, component ] )-
当spider关闭时,该函数被调用。 该方法提供了一个替代调用signals.connect()来监听
spider_closed
信号的快捷方式。
closed
( reason ) -
scrapy.spiders.
Spider
让我们来看一个例子:
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)
在单个回调函数中返回多个Request以及Item的例子:
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):
sel = scrapy.Selector(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 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)
Spider arguments
Spiders can receive arguments that modify their behaviour. Some common uses for spider arguments are to define the start URLs or to restrict the crawl to certain sections of the site, but they can be used to configure any functionality of the spider.
Spider arguments are passed through the crawl
command using the -a
option. For example:
scrapy crawl myspider -a category=electronics
Spiders receive arguments in their constructors:
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]
# ...
Spider arguments can also be passed through the Scrapyd schedule.json
API. See Scrapyd documentation.
Generic Spiders
Scrapy comes with some useful generic spiders that you can use, to subclass your spiders from. Their aim is to provide convenient functionality for a few common scraping cases, like following all links on a site based on certain rules, crawling from Sitemaps, or parsing a XML/CSV feed.
For the examples used in the following spiders, we’ll assume you have a project with a TestItem
declared in a myproject.items
module:
import scrapy
class TestItem(scrapy.Item):
id = scrapy.Field()
name = scrapy.Field()
description = scrapy.Field()
CrawlSpider
-
class
-
爬取一般网站常用的spider。其定义了一些规则(rule)来提供跟进link的方便的机制。 也许该spider并不是完全适合您的特定网站或项目,但其对很多情况都使用。 因此您可以以其为起点,根据需求修改部分方法。当然您也可以实现自己的spider。
除了从Spider继承过来的(您必须提供的)属性外,其提供了一个新的属性:
-
一个包含一个(或多个)
Rule
对象的集合(list)。 每个Rule
对爬取网站的动作定义了特定表现。 Rule对象在下边会介绍。 如果多个rule匹配了相同的链接,则根据他们在本属性中被定义的顺序,第一个会被使用。
rules
该spider也提供了一个可复写(overrideable)的方法:
-
scrapy.spiders.
CrawlSpider
爬取规则(Crawling rules)
-
class
-
link_extractor
是一个 Link Extractor 对象。 其定义了如何从爬取到的页面提取链接。callback
是一个callable或string(该spider中同名的函数将会被调用)。 从link_extractor中每获取到链接时将会调用该函数。该回调函数接受一个response作为其第一个参数, 并返回一个包含Item
以及(或)Request
对象(或者这两者的子类)的列表(list)。警告
当编写爬虫规则时,请避免使用
parse
作为回调函数。 由于CrawlSpider
使用parse
方法来实现其逻辑,如果 您覆盖了parse
方法,crawl spider 将会运行失败。cb_kwargs
包含传递给回调函数的参数(keyword argument)的字典。follow
是一个布尔(boolean)值,指定了根据该规则从response提取的链接是否需要跟进。 如果callback
为None,follow
默认设置为True
,否则默认为False
。process_links
是一个callable或string(该spider中同名的函数将会被调用)。 从link_extractor中获取到链接列表时将会调用该函数。该方法主要用来过滤。process_request
是一个callable或string(该spider中同名的函数将会被调用)。 该规则提取到每个request时都会调用该函数。该函数必须返回一个request或者None。 (用来过滤request)
scrapy.spiders.
Rule
(
link_extractor,
callback=None,
cb_kwargs=None,
follow=None,
process_links=None,
process_request=None
)
CrawlSpider样例
接下来给出配合rule使用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 = (
# 提取匹配 'category.php' (但不匹配 'subsection.php') 的链接并跟进链接(没有callback意味着follow默认为True)
Rule(LinkExtractor(allow=('category\.php', ), deny=('subsection\.php', ))),
# 提取匹配 'item.php' 的链接并使用spider的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
该spider将从example.com的首页开始爬取,获取category以及item的链接并对后者使用 parse_item
方法。 当item获得返回(response)时,将使用XPath处理HTML并生成一些数据填入 Item
中。
XMLFeedSpider
-
class
-
XMLFeedSpider被设计用于通过迭代各个节点来分析XML源(XML feed)。 迭代器可以从
iternodes
,xml
,html
选择。 鉴于xml
以及html
迭代器需要先读取所有DOM再分析而引起的性能问题, 一般还是推荐使用iternodes
。 不过使用html
作为迭代器能有效应对错误的XML。您必须定义下列类属性来设置迭代器以及标签名(tag name):
-
一个由
(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' # ...
namespaces
除了这些新的属性之外,该spider也有以下可以覆盖(overrideable)的方法:
-
该方法在spider分析response前被调用。您可以在response被分析之前使用该函数来修改内容(body)。 该方法接受一个response并返回一个response(可以相同也可以不同)。
adapt_response
( response ) -
scrapy.spiders.
XMLFeedSpider
XMLFeedSpider例子
该spider十分易用。下边是其中一个例子:
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
简单来说,我们在这里创建了一个spider,从给定的 start_urls
中下载feed, 并迭代feed中每个 item
标签,输出,并在 Item
中存储有些随机数据。
CSVFeedSpider
-
class
-
该spider除了其按行遍历而不是节点之外其他和XMLFeedSpider十分类似。 而其在每次迭代时调用的是
parse_row()
。
scrapy.spiders.
CSVFeedSpider
CSVFeedSpider例子
下面的例子和之前的例子很像,但使用了 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
SitemapSpider
-
class
-
SitemapSpider使您爬取网站时可以通过 Sitemaps 来发现爬取的URL。
其支持嵌套的sitemap,并能从 robots.txt 中获取sitemap的url。
-
包含您要爬取的url的sitemap的url列表(list)。 您也可以指定为一个 robots.txt ,spider会从中分析并提取url。
sitemap_urls
-
一个包含
(regex, callback)
元组的列表(list):regex
是一个用于匹配从sitemap提供的url的正则表达式。regex
可以是一个字符串或者编译的正则对象(compiled regex object)。- callback指定了匹配正则表达式的url的处理函数。
callback
可以是一个字符串(spider中方法的名字)或者是callable。
例如:
sitemap_rules = [('/product/', 'parse_product')]
规则按顺序进行匹配,之后第一个匹配才会被应用。
如果您忽略该属性,sitemap中发现的所有url将会被
parse
函数处理。
sitemap_rules
-
一个用于匹配要跟进的sitemap的正则表达式的列表(list)。其仅仅被应用在 使用 Sitemap index files 来指向其他sitemap文件的站点。
默认情况下所有的sitemap都会被跟进。
sitemap_follow
-
指定当一个
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_alternate_links
-
scrapy.spiders.
SitemapSpider
SitemapSpider样例
简单的例子: 使用 parse
处理通过sitemap发现的所有url:
from scrapy.spiders import SitemapSpider
class MySpider(SitemapSpider):
sitemap_urls = ['http://www.example.com/sitemap.xml']
def parse(self, response):
pass # ... scrape item here ...
用特定的函数处理某些url,其他的使用另外的callback:
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 文件定义的sitemap并只跟进包含有 ..sitemap_shop
的url:
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中使用其他url:
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 ...