Scrapy spider
info
spider是定义一个特定站点(或一组站点)如何被抓取的类,包括如何执行抓取(即跟踪链接)以及如何从页面中提取结构化数据(即抓取项)。换言之,spider是为特定站点(或者在某些情况下,一组站点)定义爬行和解析页面的自定义行为的地方。
对于蜘蛛来说,抓取周期是这样的:
首先生成对第一个URL进行爬网的初始请求,然后指定一个回调函数,该函数使用从这些请求下载的响应进行调用。
要执行的第一个请求是通过调用start_requests()
方法,该方法(默认情况下)生成Request
中指定的URL的start_urls
以及parse
方法作为请求的回调函数。在回调函数中,解析响应(网页)并返回 item objects ,
Request
对象,或这些对象的可迭代。这些请求还将包含一个回调(可能相同),然后由Scrapy下载,然后由指定的回调处理它们的响应。在回调函数中,解析页面内容,通常使用 选择器 (但您也可以使用beautifulsoup、lxml或任何您喜欢的机制)并使用解析的数据生成项。
最后,从spider返回的项目通常被持久化到数据库(在某些 Item Pipeline )或者使用 Feed 导出 .
尽管这个循环(或多或少)适用于任何类型的蜘蛛,但是为了不同的目的,有不同类型的默认蜘蛛被捆绑成 Scrapy 。我们将在这里讨论这些类型。
scrapy.Spider
这是最简单的蜘蛛,也是每个蜘蛛都必须继承的蜘蛛(包括与碎屑捆绑在一起的蜘蛛,还有你自己写的蜘蛛)。它不提供任何特殊功能。它只是提供了一个默认值 start_requests()
从发送请求的实现 start_urls
spider属性并调用spider的方法 parse
对于每个结果响应。
name
定义此蜘蛛名称的字符串。spider名称是scrappy定位(和实例化)spider的方式,因此它必须是唯一的。但是,没有什么可以阻止您实例化同一个蜘蛛的多个实例。这是最重要的蜘蛛属性,也是必需的。如果蜘蛛 爬取 一个域,通常的做法是在域后命名蜘蛛,无论有没有 TLD . 例如,一只爬行的蜘蛛mywebsite.com
经常被称为mywebsite
.allowed_domains
包含允许此蜘蛛爬行的域的字符串的可选列表。对于不属于此列表(或其子域)中指定的域名的URL请求,如果OffsiteMiddleware
启用。假设您的目标URL是https://www.example.com/1.html
然后添加'example.com'
列在名单上。start_urls
未指定特定URL时,爬行器将从其开始爬行的URL列表。因此,下载的第一个页面将是此处列出的页面。后续的Request
将从起始URL中包含的数据连续生成。custom_settings
运行此spider时,将从项目范围配置中重写的设置字典。它必须被定义为类属性,因为在实例化之前更新了设置。有关可用内置设置的列表,请参阅: 内置设置参考 .crawler
此属性由from_crawler()
初始化类后的类方法,并链接到Crawler
此蜘蛛实例绑定到的对象。Crawler封装了项目中的许多组件,用于它们的单入口访问(例如扩展、中间件、信号管理器等)。见 爬虫API 了解更多。fromcrawler(_crawler, args, kwargs)
这是Scrapy用来创建蜘蛛的类方法。您可能不需要直接重写它,因为默认实现充当__init__()
方法,使用给定参数调用它args
和命名参数kwargs
.尽管如此,此方法设置了crawler
和settings
新实例中的属性,以便稍后在蜘蛛代码中访问它们。参数crawler (Crawler
instance) — 蜘蛛将被绑到的爬行器args (list) — 传递给的参数__init__()
方法*kwargs (dict) — 传递给的关键字参数__init__()
方法start_requests()
此方法必须返回一个iterable,其中包含对此spider进行爬网的第一个请求。当蜘蛛被打开爬取的时候,它被称为 Scrapy。Scrapy只调用一次,因此可以安全地实现start_requests()
作为发电机。默认实现生成Request(url, dont_filter=True)
对于每个URLstart_urls
.如果要更改用于开始抓取域的请求,这是要重写的方法。例如,如果您需要从使用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
parse(response)
这是Scrapy在请求未指定回调时用来处理下载响应的默认回调。这个parse
方法负责处理响应,并返回 爬取 的数据和/或更多的URL。其他请求回调与Spider
班级。此方法以及任何其他请求回调都必须返回Request
和/或 item objects 。参数response (Response
) — 解析的响应log(message[, level, component])
通过Spider的logger
,保持向后兼容性。有关详细信息,请参阅 从蜘蛛记录 .closed(reason)
蜘蛛关闭时调用。此方法为spider_closed
信号
我们来看一个例子:
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)
从单个回调返回多个请求和项目:
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):
for h3 in response.xpath('//h3').getall():
yield {"title": h3}
for href in response.xpath('//a/@href').getall():
yield scrapy.Request(response.urljoin(href), self.parse)
而不是 start_urls
您可以使用 start_requests()
直接;为数据提供更多的结构,您可以使用 Item
对象::
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').getall():
yield MyItem(title=h3)
for href in response.xpath('//a/@href').getall():
yield scrapy.Request(response.urljoin(href), self.parse)
scrapy settings
蜘蛛可以接受改变其行为的论据。spider参数的一些常见用途是定义起始URL或将爬行限制在站点的某些部分,但它们可以用于配置spider的任何功能。
蜘蛛参数通过 crawl
命令使用 -a
选项。例如::
scrapy crawl myspider -a category=electronics
蜘蛛可以在它们的 init 方法::
import scrapy
class MySpider(scrapy.Spider):
name = 'myspider'
def __init__(self, category=None, *args, **kwargs):
super(MySpider, self).__init__(*args, **kwargs)
self.start_urls = [f'http://www.example.com/categories/{category}']
# ...
默认值 init 方法将获取任何spider参数,并将其作为属性复制到spider。上面的例子也可以写如下:
import scrapy
class MySpider(scrapy.Spider):
name = 'myspider'
def start_requests(self):
yield scrapy.Request(f'http://www.example.com/categories/{self.category}')
如果你是 running Scrapy from a script ,则可以在调用时指定爬行器参数。 CrawlerProcess.crawl
或 CrawlerRunner.crawl
::
process = CrawlerProcess()
process.crawl(MySpider, category="electronics")
请记住,spider参数只是字符串。蜘蛛本身不会进行任何解析。如果你要设置 start_urls
属性来自命令行,您必须使用类似的 ast.literal_eval()
或 json.loads()
然后将其设置为属性。否则,您将在 start_urls
字符串(一个非常常见的Python陷阱),导致每个字符被视为一个单独的URL。
有效的用例是设置 HttpAuthMiddleware
或用户代理 UserAgentMiddleware
::
scrapy crawl myspider -a http_user=myuser -a http_pass=mypassword -a user_agent=mybot
蜘蛛参数也可以通过scrapyD传递 schedule.json
应用程序编程接口。见 Scrapyd documentation .
class Scrapy
Scrapy附带了一些有用的通用蜘蛛,您可以使用它们来对蜘蛛进行子类化。他们的目标是为一些常见的抓取案例提供方便的功能,比如根据特定规则跟踪站点上的所有链接,从 Sitemaps 或分析XML/CSV源。
对于以下蜘蛛中使用的示例,我们假设您有一个项目 TestItem
宣布为 myproject.items
模块:
import scrapy
class TestItem(scrapy.Item):
id = scrapy.Field()
name = scrapy.Field()
description = scrapy.Field()
CrawlSpider
这是最常用的爬行常规网站的蜘蛛,因为它通过定义一组规则为跟踪链接提供了一种方便的机制。它可能不是最适合您的特定网站或项目的,但它对于某些情况来说已经足够通用了,因此您可以从它开始,并根据需要覆盖它以获得更多的自定义功能,或者只实现您自己的蜘蛛。
除了从spider继承的属性(必须指定),这个类还支持一个新的属性:
这个蜘蛛还公开了一个可重写的方法:
- parsestart_url(_response, **kwargs)[源代码]
对于为爬行器的URL生成的每个响应,都会调用此方法start_urls
属性。它允许解析初始响应,并且必须返回 item object ,aRequest
对象,或包含其中任何一个的迭代数。
爬行规则
- classscrapy.spiders.Rule(link_extractor=None, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None, errback=None)
link_extractor
是一种 Link Extractor 对象,该对象定义如何从每个爬网页面提取链接。每个生成的链接将用于生成Request
对象,该对象将在其meta
词典(在link_text
密钥)。如果省略,将使用不带参数创建的默认链接提取器,从而提取所有链接。callback
是要为使用指定链接提取器提取的每个链接调用的可调用或字符串(在这种情况下,将使用来自具有该名称的爬行器对象的方法)。此回调接收一个Response
作为其第一个参数,并且必须返回单个实例或 item objects 和/或Request
对象(或其任何子类)。如上所述,收到的Response
对象将包含生成Request
在ITS中meta
词典(在link_text
密钥)cb_kwargs
是包含要传递给回调函数的关键字参数的dict。follow
是一个布尔值,用于指定是否从使用此规则提取的每个响应中遵循链接。如果callback
没有follow
默认为True
,否则默认为False
.process_links
是一个可调用的,或一个字符串(在这种情况下,将使用具有该名称的蜘蛛对象中的方法),对于使用指定的link_extractor
. 这主要用于过滤目的。process_request
是一个可调用的(或字符串,在这种情况下,将使用来自具有该名称的爬行器对象的方法),它将在Request
按此规则提取。此可调用对象应将上述请求作为第一个参数,并且Response
作为第二个参数从其发出请求。它必须返回一个Request
对象或None
(用过滤发出请求)。errback
在处理规则生成的请求时引发任何异常时要调用的可调用或字符串(在这种情况下,将使用来自spider对象的具有该名称的方法)。它收到一个Twisted Failure
实例作为第一个参数。警告由于其内部实现,在编写时必须显式设置新请求的回调CrawlSpider
-基于蜘蛛;否则会发生意外行为。2.0 新版功能: 这个 错误 参数。
爬行蜘蛛示例
现在让我们来看一个例子,爬行蜘蛛的规则是:
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 = (
# 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_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()').get()
item['description'] = response.xpath('//td[@id="item_description"]/text()').get()
item['link_text'] = response.meta['link_text']
url = response.xpath('//td[@id="additional_data"]/@href').get()
return response.follow(url, self.parse_additional_page, cb_kwargs=dict(item=item))
def parse_additional_page(self, response, item):
item['additional_data'] = response.xpath('//p[@id="additional_data"]/text()').get()
return item
此蜘蛛将开始爬行example.com的主页,收集类别链接和项目链接,并使用 parse_item
方法。对于每个项目响应,将使用XPath从HTML中提取一些数据,并使用 Item
都会装满它。