pin_drop当前位置:知识文库 ❯ 图文
Scrapy分页爬取详解 - 从简单分页到多级分页完整指南
一、分页爬取概述
分页爬取是网络爬虫中最常见也是最重要的需求之一。在现实场景中,网站通常将大量数据分成多页展示,比如电商商品列表、搜索结果、论坛帖子等。Scrapy通过强大的请求调度机制,可以轻松实现分页数据的自动跟进采集。
Scrapy分页爬取的核心原理是:在 parse() 方法中提取当前页数据后,识别下一页的URL,然后通过 yield 创建新的请求对象。Scrapy的调度器会自动管理请求队列,实现高效的分页采集。Scrapy内置的URL去重机制(RFPDupeFilter)会自动过滤已请求过的URL,避免重复爬取。
二、分页爬取核心语法
最基本的分页爬取模式如下:提取当前页数据,找到下一页链接,发起新请求并指定回调函数为自身,形成循环:
代码示例
def parse(self, response):
# 提取当前页数据
for item in response.css('div.item'):
yield {
'name': item.css('h3::text').get(),
'price': item.css('.price::text').get(),
}
# 查找下一页链接
next_page = response.css('a.next::attr(href)').get()
if next_page:
# 使用urljoin将相对URL转为绝对URL
yield scrapy.Request(
url=response.urljoin(next_page),
callback=self.parse, # 回调自身实现循环
)
三、分页相关方法与Request参数
分页常用方法
Request常用参数
四、完整代码示例
示例1:简单分页爬取(response.follow方式)
使用 response.follow() 是最推荐的分页方式,它自动将相对URL转换为绝对URL,代码更简洁:
代码示例
import scrapy
class SimplePaginationSpider(scrapy.Spider):
name = 'simple_pagination'
start_urls = ['https://quotes.toscrape.com/']
def parse(self, response):
# 提取当前页的名言数据
for quote in response.css('div.quote'):
yield {
'text': quote.css('span.text::text').get(),
'author': quote.css('small.author::text').get(),
'tags': quote.css('div.tags a.tag::text').getall(),
}
# 查找并跟进下一页
next_page = response.css('li.next a::attr(href)').get()
if next_page:
yield response.follow(next_page, callback=self.parse)
示例2:URL模式分页(有规律的分页URL)
当分页URL有固定规律时(如 ?page=1、?page=2),可以直接生成URL列表批量请求,无需解析页面中的下一页链接:
代码示例
import scrapy
class UrlPatternSpider(scrapy.Spider):
name = 'url_pattern'
allowed_domains = ['example.com']
def start_requests(self):
base_url = 'https://example.com/products'
# 生成第1-10页的URL并批量请求
for page in range(1, 11):
url = f'{base_url}?page={page}'
yield scrapy.Request(
url,
callback=self.parse,
meta={'page': page}, # 传递页码信息
)
def parse(self, response):
page = response.meta['page']
self.logger.info(f'正在爬取第{page}页')
for product in response.css('div.product'):
yield {
'name': product.css('h3::text').get(),
'price': product.css('.price::text').get(),
'page': page,
}
示例3:多级分页(列表页 + 详情页)
这是最实用的场景:先从列表页提取基本信息,然后跟进每个商品的详情页获取完整信息。通过 meta 字典在请求之间传递数据:
代码示例
import scrapy
class MultiLevelPaginationSpider(scrapy.Spider):
name = 'multi_pagination'
start_urls = ['https://quotes.toscrape.com/']
def parse(self, response):
"""列表页:提取数据并跟进详情页和下一页"""
for quote in response.css('div.quote'):
# 提取基本信息到Item
item = {
'text': quote.css('span.text::text').get(),
'author_name': quote.css('small.author::text').get(),
}
# 跟进作者详情页获取更多信息
author_url = quote.css('small.author + a::attr(href)').get()
if author_url:
yield response.follow(
author_url,
callback=self.parse_author,
meta={'item': item}, # 传递基础数据
)
else:
yield item
# 跟进列表的下一页
next_page = response.css('li.next a::attr(href)').get()
if next_page:
yield response.follow(next_page, callback=self.parse)
def parse_author(self, response):
"""作者详情页:补充作者详细信息"""
item = response.meta['item'] # 获取传递过来的基础数据
item['author_born_date'] = response.css(
'span.author-born-date::text'
).get()
item['author_born_location'] = response.css(
'span.author-born-location::text'
).get()
item['author_description'] = response.css(
'span.author-description::text'
).get()
yield item
示例4:带分页终止条件的爬取
实际项目中,通常需要设置分页终止条件,如最大页数限制或采集数量限制,避免无限循环:
代码示例
import scrapy
class LimitedPaginationSpider(scrapy.Spider):
name = 'limited_pagination'
start_urls = ['https://quotes.toscrape.com/']
max_pages = 5 # 最大爬取页数
items_count = 0 # 已采集数量计数器
def parse(self, response):
for quote in response.css('div.quote'):
self.items_count += 1
yield {
'text': quote.css('span.text::text').get(),
'author': quote.css('small.author::text').get(),
}
# 分页终止条件:达到最大页数或采集数量上限
current_page = response.meta.get('page', 1)
if current_page < self.max_pages:
next_page = response.css('li.next a::attr(href)').get()
if next_page:
yield response.follow(
next_page,
callback=self.parse,
meta={'page': current_page + 1},
)
五、分页方式对比
六、实际应用场景
-
场景1 - 电商商品列表采集:爬取淘宝、京东等电商网站的商品列表页,逐页采集商品名称、价格、销量等信息,然后跟进每个商品的详情页获取评价和详细描述。
-
场景2 - 论坛帖子分页浏览:采集知乎、贴吧等论坛的帖子列表,逐页跟进。每个帖子可能有多个回复页面,需要实现多级分页采集所有回复内容。
-
场景3 - 搜索结果分页采集:爬取搜索引擎或站内搜索的结果页,处理翻页URL中的
pn、offset等参数,实现批量搜索结果采集。
小贴士
response.follow() 除了支持URL字符串外,还可以直接接收 Selector 对象,例如 response.follow(response.css('a.next'), callback=self.parse),它会自动提取 href 属性。另外,建议在 settings.py 中设置 DOWNLOAD_DELAY = 1 控制爬取间隔,避免对目标服务器造成过大压力。
七、注意事项与最佳实践
注意1:务必添加分页终止条件(最大页数、采集数量等),否则可能陷入无限循环,浪费资源甚至被封禁IP。
注意2:
response.follow()自动处理相对URL,比手动使用response.urljoin()更方便,推荐优先使用。
注意3:Scrapy默认启用URL去重(RFPDupeFilter),不同分页URL不会被重复请求。如果需要请求相同URL多次,设置
dont_filter=True。
注意4:设置
DOWNLOAD_DELAY控制采集速度,建议设置为1-3秒。同时可以设置CONCURRENT_REQUESTS_PER_DOMAIN限制每个域名的并发请求数。
八、练习题
练习1
编写一个Spider,爬取 https://quotes.toscrape.com/ 的所有页面,使用 close_spider() 方法在爬虫结束时统计并输出总共采集了多少条名言。
练习2
编写一个Spider,使用URL模式分页,爬取某网站第1-20页的数据(URL格式为 https://example.com/data?page=N),并在每页数据中标记页码。要求设置最大并发请求数为3,下载延迟为2秒。
九、常见问题FAQ
常见问题
response.follow()和scrapy.Request()有什么区别?
response.follow() 自动将相对URL转换为绝对URL,还支持直接传入Selector对象;scrapy.Request() 需要完整的绝对URL。对于分页场景,推荐优先使用 response.follow()。
如何在分页之间传递数据?
使用 meta 字典。在创建Request时通过 meta={'key': value} 传递数据,在回调函数中通过 response.meta['key'] 获取。这在多级分页中非常常用,可以逐级累积数据。
分页爬取时如何控制爬取速度?
在 settings.py 中设置 DOWNLOAD_DELAY = 2(秒),设置 CONCURRENT_REQUESTS_PER_DOMAIN = 2 限制并发数。也可以启用 AUTOTHROTTLE_ENABLED = True 让Scrapy自动调整爬取速度。
页面没有"下一页"按钮,如何判断分页结束?
有多种方法:(1) 检查当前页数据量,如果为空或少于预期则停止;(2) 设置最大页数限制;(3) 通过URL模式,检查是否超过最后一页;(4) 检查页面中是否有"最后一页"或"无更多数据"的标识元素。建议组合使用多种判断方式。
如何处理JavaScript动态加载的分页?
Scrapy默认不能执行JavaScript。解决方法:(1) 分析页面的API请求,直接请求JSON API获取分页数据;(2) 使用 scrapy-playwright 或 scrapy-splash 中间件渲染页面;(3) 使用Selenium获取渲染后的HTML再交给Scrapy处理。
本文涉及AI创作
内容由AI创作,请仔细甄别