在Scrapy爬虫开发中,经常会遇到需要先请求列表页获取基础信息,再进入详情页补充更多数据的场景,此时就需要把列表页解析函数中获取到的变量传递到详情页的解析函数中。Scrapy提供了专门的中间传递机制,不需要额外引入复杂的存储方案就能实现这个需求。

核心传递方式:meta参数
Scrapy的Request对象提供了一个meta属性,这是一个字典类型的参数,我们可以在构造请求时把需要传递的变量放到这个字典里,后续的解析函数可以通过response.meta获取到之前存入的值。
基础使用示例
下面通过一个爬取文章列表的场景来演示具体用法,首先列表页的解析函数获取文章标题,然后把标题传递到详情页的解析函数中,再补充获取文章的正文内容。
import scrapy
class ArticleSpider(scrapy.Spider):
name = "article_spider"
start_urls = ["https://ipipp.com/article/list"]
def parse(self, response):
# 获取所有文章列表项
article_list = response.css("div.article-item")
for article in article_list:
# 提取列表页的文章标题
title = article.css("h2::text").get()
# 提取详情页链接
detail_url = article.css("a::attr(href)").get()
# 构造详情页请求,把标题放入meta字典
yield scrapy.Request(
url=detail_url,
callback=self.parse_detail,
meta={"article_title": title}
)
def parse_detail(self, response):
# 从response的meta中获取之前传递过来的标题
title = response.meta.get("article_title")
# 提取详情页的正文内容
content = response.css("div.article-content::text").get()
# 输出最终的结果
yield {
"title": title,
"content": content
}
传递多个变量的方式
如果需要在多个解析函数之间传递多个变量,只需要在meta字典中添加多个键值对即可,不需要额外做其他处理。
def parse(self, response):
item_list = response.css("div.goods-item")
for item in item_list:
goods_name = item.css("p.name::text").get()
goods_price = item.css("span.price::text").get()
goods_link = item.css("a::attr(href)").get()
# 把多个变量都存入meta字典
yield scrapy.Request(
url=goods_link,
callback=self.parse_goods_detail,
meta={
"goods_name": goods_name,
"goods_price": goods_price
}
)
def parse_goods_detail(self, response):
# 分别获取多个传递过来的变量
name = response.meta.get("goods_name")
price = response.meta.get("goods_price")
detail = response.css("div.detail::text").get()
yield {
"name": name,
"price": price,
"detail": detail
}
注意事项
- meta字典中的值会被Scrapy自动进行序列化处理,因此传递的变量需要是可以被序列化的类型,比如字符串、数字、列表、字典等,不要传递不可序列化的对象比如Scrapy的Response对象。
- 如果请求过程中发生了重定向,meta字典的内容会默认保留,不需要额外做特殊处理。
- 获取meta中的值时建议使用
get方法,避免键不存在时抛出 KeyError 异常,比如response.meta.get("key", "默认值")可以在键不存在时返回指定的默认值。 - 不要往meta中存入过大的数据,避免占用过多的内存资源,影响爬虫的运行效率。
其他特殊场景的传递方式
如果是需要在整个爬虫运行过程中共享的全局变量,也可以把变量存到Spider类的实例属性中,不过这种方式不适合需要跟随单个请求传递的场景,只适合所有请求都需要访问的全局配置类数据。
class MySpider(scrapy.Spider):
name = "my_spider"
# 定义全局共享的变量
global_config = "全局配置值"
def parse(self, response):
# 所有解析函数都可以直接访问global_config
print(self.global_config)
# 后续逻辑...