pin_drop当前位置:知识文库 ❯ 图文
Scrapy Spider编写 - 掌握爬虫核心采集逻辑教程
一、Spider概述
Spider是Scrapy中定义采集逻辑的核心类,继承自scrapy.Spider。Spider定义了如何爬取某个网站,包括起始URL、页面解析逻辑、跟进链接等。通过编写parse()方法处理响应,使用CSS选择器或XPath提取数据,yield Item或Request实现数据采集和链接跟进。
Spider是整个Scrapy框架中最贴近业务逻辑的部分。你可以通过Scrapy内置的多种Spider类型来适配不同场景,如基础的Spider、支持规则匹配的CrawlSpider、以及支持站点地图的SitemapSpider。更多详情可参考 Scrapy Spider官方文档。
二、Spider基本语法
以下是一个最基本的Spider类结构:
代码示例
import scrapy
class MySpider(scrapy.Spider):
name = 'my_spider'
allowed_domains = ['example.com']
start_urls = ['https://example.com']
def parse(self, response):
# 提取数据
yield {...}
# 跟进链接
yield scrapy.Request(url, callback=self.parse_detail)
这个结构包含了Spider最基本的三个属性:name(爬虫名称)、allowed_domains(允许爬取的域名)和start_urls(起始URL列表),以及一个必须实现的parse()方法。
三、参数详解
Spider核心属性
Spider核心方法
Request参数
四、返回值说明
parse()方法通过yield返回两种类型的数据:
-
Item字典:包含提取到的结构化数据,如
yield {'title': '标题', 'url': '链接'},将传递给Pipeline处理。 -
scrapy.Request对象:包含新的请求URL和回调函数,用于跟进链接实现多级爬取。
同一个parse()方法中既可以yield Item也可以yield Request,Scrapy会自动区分处理。这是Scrapy实现"列表页提取+跟进详情页"模式的核心机制。
五、代码示例
示例1:基本Spider
以下是一个爬取 quotes.toscrape.com 网站名言的基本Spider:
代码示例
import scrapy
class QuotesSpider(scrapy.Spider):
name = 'quotes'
allowed_domains = ['quotes.toscrape.com']
start_urls = ['https://quotes.toscrape.com/']
def parse(self, response):
# 使用CSS选择器提取数据
quotes = response.css('div.quote')
for quote in quotes:
yield {
'text': quote.css('span.text::text').get(),
'author': quote.css('small.author::text').get(),
'tags': quote.css('div.tags a.tag::text').getall(),
}运行命令:
代码示例
scrapy crawl quotes -o quotes.json
说明:response.css('div.quote') 选取所有名言容器,::text 提取文本内容,.get() 获取第一个匹配,.getall() 获取所有匹配列表。
示例2:自定义start_requests
当需要自定义初始请求(如添加特殊Header、传递meta数据)时,可以重写start_requests()方法:
代码示例
import scrapy
class LoginSpider(scrapy.Spider):
name = 'login_spider'
def start_requests(self):
"""自定义初始请求"""
urls = [
'https://example.com/page1',
'https://example.com/page2',
]
for url in urls:
yield scrapy.Request(
url=url,
callback=self.parse,
headers={'Custom-Header': 'value'},
meta={'page': url.split('/')[-1]},
)
def parse(self, response):
page = response.meta.get('page')
self.logger.info(f'解析页面: {page}')
yield {
'page': page,
'title': response.css('title::text').get(),
}
通过meta字典可以在请求之间传递数据,在回调函数中通过response.meta获取。这是多级爬取时保持上下文的关键方式。
示例3:多级页面爬取
以下Spider实现了从列表页提取数据,并跟进到作者详情页获取更多信息的多级爬取:
代码示例
import scrapy
class MultiLevelSpider(scrapy.Spider):
name = 'multi_level'
start_urls = ['https://quotes.toscrape.com/']
def parse(self, response):
"""第一级:列表页"""
quotes = response.css('div.quote')
for quote in quotes:
author_url = quote.css('small.author + a::attr(href)').get()
if author_url:
yield scrapy.Request(
url=response.urljoin(author_url),
callback=self.parse_author,
meta={'quote_text': quote.css('span.text::text').get()},
)
# 跟进下一页
next_page = response.css('li.next a::attr(href)').get()
if next_page:
yield scrapy.Request(
url=response.urljoin(next_page),
callback=self.parse,
)
def parse_author(self, response):
"""第二级:详情页"""
yield {
'quote': response.meta['quote_text'],
'author': response.css('h3.author-title::text').get(),
'born': response.css('span.author-born-date::text').get(),
}
关键技术点:response.urljoin() 将相对URL转换为绝对URL;callback=self.parse_author 指定新的回调函数处理详情页;meta 将列表页提取的名言文本传递到详情页回调函数中。
小贴士
对于更复杂的多页爬取场景,Scrapy提供了CrawlSpider类,配合Rule和LinkExtractor可以自动跟进链接,无需手动编写跟进逻辑。适合规则明确的整站爬取场景。
六、实际应用场景
-
列表页+详情页的多级爬取:电商网站商品列表页提取商品链接,跟进到详情页获取价格、描述、评价等完整信息。
-
需要登录认证的网站爬取:通过start_requests()发送POST请求登录,使用FormRequest处理表单提交,保持Session状态。
-
多入口URL的批量采集:从多个起始URL同时开始采集,如多个分类页面、多个城市的站点等。
七、注意事项
name唯一性:name属性必须唯一,同一项目中不能有重名爬虫。Scrapy通过name来识别和运行特定爬虫。
allowed_domains过滤:allowed_domains会过滤非目标域名的请求,设置不当可能导致请求被忽略。如果爬虫需要跨域请求,请确保将相关域名都添加到列表中。
URL去重:Scrapy默认会过滤重复URL,设置dont_filter=True可禁用此行为。适用于需要重复访问同一URL的场景(如实时监控)。
yield vs return:parse()方法必须使用yield而非return返回数据。yield是生成器语法,允许函数在运行过程中逐步产出数据,这对于异步框架的流量控制至关重要。
八、数据提取方式对比
Scrapy支持多种数据提取方式,各有优缺点:
实际开发中,CSS选择器是最常用的方式,语法简洁且性能良好。XPath在需要复杂定位(如查找父元素、兄弟元素)时更有优势。正则表达式主要用于从文本内容中提取特定模式的数据。
九、小结与练习题
本节小结
-
基本结构:Spider继承scrapy.Spider,必须定义name属性和parse()方法。
-
请求定制:start_urls定义起始URL,也可重写start_requests()自定义初始请求。
-
数据返回:通过yield返回Item字典或Request对象,实现数据采集和链接跟进。
-
数据传递:使用meta字典在请求间传递数据,是多级爬取的核心技术。
练习题
练习1
编写一个Spider,爬取quotes.toscrape.com的名言列表,提取名言文本、作者和标签,并保存到JSON文件。
练习2
编写一个Spider,重写start_requests()方法,使用POST请求提交表单数据到测试网站,并获取响应结果。
常见问题
CSS选择器和XPath应该选择哪个?
两者在Scrapy中性能基本一致。CSS选择器语法更简洁,适合大多数常规提取场景;XPath功能更强大,支持向上查找父元素、复杂的轴定位等。建议优先使用CSS选择器,在CSS无法表达的复杂场景下再使用XPath。
为什么parse()方法必须用yield而不是return?
yield是Python生成器语法,允许函数在运行过程中逐步产出数据而不终止。Scrapy利用这一特性实现异步流量控制:当框架繁忙时会暂停生成器,空闲时继续产出。如果使用return,只能返回一次数据,无法实现这种流量控制机制。
如何在Spider中使用自定义配置覆盖全局设置?
在Spider类中定义custom_settings字典即可。例如:custom_settings = {'DOWNLOAD_DELAY': 2, 'CONCURRENT_REQUESTS': 8}。这些设置只对该Spider生效,不会影响项目中的其他爬虫。
如何实现翻页爬取?
在parse()方法中提取下一页链接,使用response.urljoin()转换为绝对URL,然后yield一个scrapy.Request对象,callback指向parse自身。参考示例3中的翻页逻辑:next_page = response.css('li.next a::attr(href)').get(),然后yield scrapy.Request(url=response.urljoin(next_page), callback=self.parse)。
Spider中的self.logger是什么?
self.logger是Scrapy自动为Spider创建的日志记录器。可以使用self.logger.info()、self.logger.warning()、self.logger.error()等方法记录日志。日志输出会被Scrapy的日志系统统一管理,可以配置输出到文件或控制台。
本文涉及AI创作
内容由AI创作,请仔细甄别