本文地址:https://www.jianshu.com/p/b69d1303336f
在Scrapy
中,Spider
负责的是网页的抓取逻辑,以及数据的解析逻辑。所以Spider
是Scrapy
爬虫中相对核心的部分。
Spider用法
命令
在上一章的简介中,我们提到,一般来说我们需要使用Scrapy
的命令行生成一个Spider
模板。命令的语法是这样的:
scrapy genspider [options] <name> <domain>
这里有两个必写的参数,name
指的是这个Spider
的名称,而且这个名称必须是唯一的,不能有其他的Spider
重名。而domain
指的是这个Spider
爬取的域名。
除此之外,这个命令还有以下可选的options:
Options
=======
--help, -h show this help message and exit
--list, -l List available templates
--edit, -e Edit spider after creating it
--dump=TEMPLATE, -d TEMPLATE
Dump template to standard output
--template=TEMPLATE, -t TEMPLATE
Uses a custom template.
--force If the spider already exists, overwrite it with the
template
如果使用的是最基础的模板,将会创建出以下格式的模板:
# -*- coding: utf-8 -*-
import scrapy
class SpidernameSpider(scrapy.Spider):
name = 'spidername'
allowed_domains = ['mydomain.com']
start_urls = ['http://mydomain.com/']
def parse(self, response):
pass
可以看到,这个模版的主体为一个SpidernameSpider
类,继承自scrapy.Spider
,这个类的名称与文件名基本保持一致。
scrapy.Spider类
这个类是Spider
最基础的类,里面只定义两三个默认的方法,非常的简单。其他的种类的Spider
都是继承自这个Spider
的。而我们的Spider
也都需要继承这个类。
这个爬虫的抓取规则与数据提取规则都将定义在这个类中。
运行流程
首先,在Scrapy
中,只要我们按照规则定义好了相应的代码,那么很多时候Scrapy
会自动的帮我们调度运行程序。
生成的模板中有一个
start_urls
参数,这是这个Spider
的运行入口,Scrapy
会自动的将这个参数中的url发送到Downloader
进行下载,并且自动的调用parse
方法来处理获得的response
。如果希望自定义爬虫的开始方式,那么删除这个参数,并自定义一个
start_urls
方法即可。在
parse
方法处理response
的过程中,我们一般会有可能获取两种对象,一个是最终我们从网页上提取的数据,这种数据我们会将其保存为item
对象。另一种是我们获取的接下来要访问的url,这一种我们会将其生成为一个Request
对象。这两种获得的对象,我们都会使用
yield
将其返回出去。Scrapy
会自动检测对象的类型,如果是item
,则会将其发送到item pipeline
进行存储等处理,如果是Request
对象,则会再次往Downloader
发送进行访问。每一个
Request
对象,都会在生成的时候绑定一个回调函数,用来处理这个请求返回的响应结果。
通过以上这几个步骤一直循环往复,则可以将我们想要抓取的数据抓取完毕。
属性与方法
name
一个定义了
Spider
名称的字符串,Scrapy
会通过这个属性来定位Spider
,所以这个name
必须是唯一的。如果
Spider
抓取的是单个域名的内容,那么通常的做法是使用域名来对Spider
命名。例如,一个抓取mywebsite.com
的Spider
应该命名为mywebsite
。allowed_domains
一个可选的列表,定义了允许访问的域名。不包含在此域名列表中的URL将不会被跟进爬取。
start_urls
一个爬虫开始爬取的起始URL列表,也是一个可选项。爬虫启动后,将首先从这些URL开始爬取。
custom_settings
配置
Spider
自定义设置的字典,会在运行这个Spider
时覆盖项目级别的设置。这个属性需要被设置为一个类属性,因为它需要在实例化之前被定义好。crawler
这个属性将会在类实例化之后,由
from_crawler()
设置,并且连接到绑定的Crawler
对象。Crawler
对象在项目中封装了许多组件,供单独对应访问。settings
运行此
Spider
的配置,是一个Settings
对象。logger
这是一个由
Spider
的名称创建的Python logger
对象,可以使用它来输出信息。start_requests()
这个方法会返回一个可迭代对象,里面包含着
Spider
启动时首先抓取的Request
。Scrapy
会在这个爬虫启动时默认调用此方法一次,默认的实现是将start_urls
中的每一个url生成一个Request
。如果你希望改变
Spider
抓取起始url的逻辑,那么你需要重写此方法。parse(response)
当
Request
没有指定回调函数时,会Scrapy
默认的调用这个方法来处理response。这个方法的通常作用是从response中提取出需要的数据,或者是生成更多接下来要爬取的URL。这个方法跟其他的
Request
回调函数一样,需要至少返回Request
、dict
、item
中的一种。log(message, [, level, component])
通过
Spider
的logger
输出信息。closed(reason)
Spider
关闭时调用此方法。*from_crawler(crawler, args, **kwargs)
这是
Scrapy
用来创建你的Spiders
的方法。一般来说你不需要直接的重写这个方法,因为它扮演着
__init__
方法的代理。这个方法会在新实例中设置crawler
和settings
,这样你才能在Spider
中访问这两个属性。
例子
以下是一些简单使用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):
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_requests()
替代start_urls
:
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)
CrawlSpider
Scrapy
提供了一些有用的通用爬虫,可以用来实现某些特定功能,例如CrawlSpider
,这些通用爬虫都是继承自Spider
的子类。
CrawlSpider
的主要用处是通过一条或者多条固定的规则(rules
),来抓取页面上所有的连接。这常常被用来做整站爬取。
CrawlSpider类
class scrapy.spiders.CrawlSpider
这种通用爬虫主要用来抓取常见的网站,对于一些特定的网站可能不是非常适合,但是更具有通用性。它可以通过定义规则来跟踪连接。
除了那些继承自Spider
的属性和方法,这个类支持以下的新属性和方法:
rules
这是由一个或者多个
Rule
对象组成的列表。每一个Rule
对象定义了一种抓取网页的行为。如果多个Rule
匹配到了同一个连接,那么在列表中位置靠前的将会被使用。parse_start_url(response)
这个方法是用来处理
start_urls
返回的响应。这个方法需要至少返回Item
对象,Request
对象,或者一个包含前两种对象的可迭代对象。
Rule
class scrapy.spiders.Rule(link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)
参数:
link_exractor
– 一个定义了如何从页面上提取连接的Link Extractor
对象。callback
– 用来处理返回的Response
的回调函数。这个回调函数需要接收Response
作为第一个参数,并且需要返回一个包含Item
或者Request
的列表注意:不能使用
parse()
方法作为回调函数,CrawlSpider
是通过parse()
方法来实现逻辑的,所以如果重写了parse()
方法,CrawlSpider
将无法运行。cb_kwargs
– 包含关键字参数的字典,可以传递给回调函数。follow
– 是否跟踪访问那些使用Rule
提取出来的连接。当callback
为None时,follow
默认为True。除此以外,follow
默认为False。也就是说,在没有指定
callback
时,CrawlSpider
会默认继续访问从response中使用Rule
提取的连接。如果指定了callback
,那么response将会教给回调函数处理。process_links
– 用来处理从response中提取出的连接的函数。主要的作用是用来筛选目标。process_request
– 用来处理从response中提取出来的每一个request。需要返回一个Request
对象,或者None(用来筛选request)。
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 = (
# allow表示将会访问的连接,deny表示不会访问的连接。
# 没有指定callback,表明会继续跟踪访问下去
Rule(LinkExtractor(allow=('category\.php', ), deny=('subsection\.php', ))),
# 提取匹配'item.php'的连接,并将返回的response交给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
系列文章: