Scrapy 是一个强大的网页爬虫框架,广泛用于从网站中抓取数据。在使用 Scrapy 进行数据抓取时,翻页请求是常见的需求。本文将详细介绍 Scrapy 发起翻页请求的原理与实现方式,包括如何通过 Scrapy 框架进行翻页请求、如何自定义请求参数,以及如何处理多页面的数据提取。
要提取所有页面的数据,最直接的方式就是通过翻页请求,访问每一页的 URL 并提取数据。通常在爬取网页时,页面内容会分为多个分页,每一页显示部分数据,用户可以点击 "下一页"(或 "后页")来加载下一部分内容。
requests
模块实现翻页在 requests
模块中,进行翻页的操作大致是这样:
requests.get(url)
,访问下一页的数据。
这种方式简单直观,但它的缺点是每次翻页都需要手动解析并发起请求。
Scrapy 实现翻页请求的思路与 requests
模块类似,但它的实现更加高效和灵活。Scrapy 使用异步请求,可以并发发起多个请求,同时对请求进行调度与管理。其实现逻辑如下:
实现 Scrapy 翻页请求的基本步骤如下:
?page=2
或 &start=25
的查询参数。
scrapy.Request(url, callback)
来创建一个新的请求,将其传递给引擎。callback
用来指定处理响应的函数。
yield scrapy.Request(url, callback)
将翻页请求交给 Scrapy 引擎进行调度和执行。
以爬取某电影 网站Top100 为例,学习如何实现翻页请求。
假设我们已经获取了第一页的数据,接下来就需要实现翻页的逻辑。以下是实现翻页请求的代码示例:
import scrapy
class DoubanSpider(scrapy.Spider):
name = "douban"
start_urls = ['https://xxxxxx']
def parse(self, response):
# 提取当前页面的电影数据
ol_list = response.xpath('//ol[@class="grid_view"]/li')
for ol in ol_list:
item = {}
item['title'] = ol.xpath('.//div[@class="hd"]/a/span[1]/text()').extract_first()
item['rating'] = ol.xpath('.//div[@class="bd"]/div/span[2]/text()').extract_first()
item['quote'] = ol.xpath('.//div[@class="bd"]//p[@class="quote"]/span/text()').extract_first()
yield item
# 提取下一页的链接
next_url = response.xpath("//a[text()='后页>']/@href").extract_first()
if next_url:
next_url = response.urljoin(next_url) # 拼接完整的 URL
yield scrapy.Request(next_url, callback=self.parse)
在这段代码中,parse
方法首先提取当前页面的电影数据,然后查找并拼接下一页的 URL 地址。如果下一页存在,就创建一个新的请求,通过 yield scrapy.Request(next_url, callback=self.parse)
递归地发起翻页请求。
Scrapy 的 Request
对象有多个参数,可以帮助我们定制请求的行为,常用的参数有:
url
:请求的 URL 地址。
callback
:指定响应处理函数。
method
:指定请求的方法,默认为 GET
。
headers
:请求头,通常用来设置 User-Agent 或 Referer 等。
meta
:传递数据给下一个回调函数,用于跨请求传递数据。
dont_filter
:如果设置为 True
,则 Scrapy 不会过滤重复的请求。
例如,我们可以通过 meta
来传递当前页面的数据,或者使用 dont_filter=True
来防止 Scrapy 过滤掉已请求的 URL。
start_requests
方法在上面的示例中,我们使用了 start_urls
来启动爬虫,但如果需要更复杂的翻页逻辑,例如分页的页码是动态生成的,或者 URL 中包含参数,我们可以重写 start_requests
方法,手动生成请求。
例如,假设页面的翻页 URL 是通过 start=25
来表示页码,每 25 条数据一页,我们可以如下编写 start_requests
方法:
def start_requests(self):
for i in range(10): # 假设我们需要爬取 10 页
url = f'https://xxxxxxx/top250?start={i * 25}&filter='
yield scrapy.Request(url, callback=self.parse)
通过这种方式,我们可以自定义分页 URL,避免手动解析 HTML 中的翻页链接。
meta
参数在不同解析函数中传递数据有时候,我们需要将一些数据从一个解析函数传递到另一个解析函数。Scrapy 提供了 meta
参数,可以用来在请求之间传递数据。
例如,在爬取电影详细信息时,我们可能需要从列表页抓取每个电影的基本信息,然后跳转到详情页。我们可以通过 meta
参数传递数据:
def parse(self, response):
for ol in response.xpath('//ol[@class="grid_view"]/li'):
item = {}
item['title'] = ol.xpath('.//div[@class="hd"]/a/span[1]/text()').extract_first()
item['rating'] = ol.xpath('.//div[@class="bd"]/div/span[2]/text()').extract_first()
detail_url = ol.xpath('.//div[@class="hd"]/a/@href').extract_first()
yield scrapy.Request(detail_url, callback=self.parse_detail, meta={'item': item})
def parse_detail(self, response):
item = response.meta['item']
# 进一步抓取详情页的内容
item['director'] = response.xpath('//span[text()="导演:"]/following-sibling::text()').extract_first()
yield item
在上面的代码中,parse
方法将每个电影的基本信息保存在 item
字典中,并将其传递到 parse_detail
函数中,进行进一步的数据提取。
为了避免被网站封禁,我们通常需要设置请求头中的 User-Agent
,以及使用代理 IP。Scrapy 可以通过修改 settings.py
文件中的配置来设置这些信息:
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
ROBOTSTXT_OBEY = False # 如果不需要遵守 robots.txt,可以设置为 False
Scrapy 会默认过滤已访问的 URL 地址,如果希望在某些情况下允许重复请求,可以通过 dont_filter=True
来禁用 URL 过滤。例如,翻页请求通常会重复访问相同的 URL,需要禁用过滤:
yield scrapy.Request(next_url, callback=self.parse, dont_filter=True)
Scrapy 提供了一种高效而灵活的方式来处理翻页请求。通过 scrapy.Request
对象,我们可以轻松地发送请求并处理响应。同时,Scrapy 还提供了强大的数据传递机制(如 meta
参数)和优化手段(如重写 start_requests
方法),使得分页抓取的实现更加灵活和高效。掌握这些技巧,对于编写高效、稳定的爬虫是非常有帮助的。