请求与响应¶
前提条件
- 已阅读入门,知道如何创建并运行一个简单的 Spider。
本页详细介绍 Request 对象:如何创建请求、在回调之间传递数据、控制优先级和去重,以及用 response.follow() 跟随链接。
Request 对象¶
Request 表示一个待获取的 URL。可以直接创建请求,也可以通过 response.follow() 创建:
from scrapling.spiders import Request
# Direct construction
request = Request(
"https://example.com/page",
callback=self.parse_page,
priority=5,
)
# Via response.follow (preferred in callbacks)
request = response.follow("/page", callback=self.parse_page)
Request 支持以下参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
url |
str |
必填 | 要获取的 URL |
sid |
str |
"" |
会话 ID,将请求交给指定会话,详见会话 |
callback |
callable |
None |
处理响应的异步生成器方法,默认为 parse() |
priority |
int |
0 |
值越大,越先处理 |
dont_filter |
bool |
False |
为 True 时跳过去重,允许重复请求 |
meta |
dict |
{} |
传递给响应的任意元数据 |
**kwargs |
传给会话获取方法的其他关键字参数,例如 headers、method、data |
额外的关键字参数会直接传给底层会话。例如,发送 POST 请求:
yield Request(
"https://example.com/api",
method="POST",
data={"key": "value"},
callback=self.parse_result,
)
Response.follow()¶
建议在回调中使用 response.follow() 创建后续请求。相比直接创建 Request 对象,它有以下优点:
- 相对 URL 会自动根据当前页面 URL 解析。
- Referer 请求头默认设为当前页面 URL。
- 会话参数会从原请求继承,包括请求头、代理设置等,但不包括
method、params、data、json、files和multipart。因此,跟随链接时会发送普通 GET 请求。 - 回调、会话 ID 和优先级如果未指定,会从原请求继承。
async def parse(self, response: Response):
# Minimal - inherits callback, sid, priority from current request
yield response.follow("/next-page")
# Override specific fields
yield response.follow(
"/product/123",
callback=self.parse_product,
priority=10,
)
# Pass additional metadata to
yield response.follow(
"/details",
callback=self.parse_details,
meta={"category": "electronics"},
)
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
url |
str |
必填 | 要跟随的 URL,可为绝对或相对 URL |
sid |
str |
"" |
会话 ID,为空时继承原请求的值 |
callback |
callable |
None |
回调方法,为 None 时继承原请求的值 |
priority |
int |
None |
优先级,为 None 时继承原请求的值 |
dont_filter |
bool |
False |
跳过去重 |
meta |
dict |
None |
与响应现有元数据合并的元数据 |
referer_flow |
bool |
True |
将当前 URL 设为 Referer 请求头 |
**kwargs |
与原请求的会话参数合并 |
关闭 Referer 传递¶
默认情况下,response.follow() 会将 Referer 请求头设为当前页面 URL。可以这样关闭:
回调¶
回调是 Spider 中用于处理响应的异步生成器方法。它必须通过 yield 产出以下三种类型之一:
dict:提取的数据项,会加入结果。Request:后续请求,会加入队列。None:静默忽略。
class MySpider(Spider):
name = "my_spider"
start_urls = ["https://example.com"]
async def parse(self, response: Response):
# Yield items (dicts)
yield {"url": response.url, "title": response.css("title::text").get("")}
# Yield follow-up requests
for link in response.css("a::attr(href)").getall():
yield response.follow(link, callback=self.parse_page)
async def parse_page(self, response: Response):
yield {"content": response.css("article::text").get("")}
注意
所有回调方法都必须使用 async def 定义,并使用 yield,不能用 return 代替。即使回调只产出数据项,不创建后续请求,也必须是异步生成器。
请求优先级¶
优先级值较大的请求会先处理。如果某些页面需要优先处理,可以这样设置:
async def parse(self, response: Response):
# High priority - process product pages first
for link in response.css("a.product::attr(href)").getall():
yield response.follow(link, callback=self.parse_product, priority=10)
# Low priority - pagination links processed after products
next_page = response.css("a.next::attr(href)").get()
if next_page:
yield response.follow(next_page, callback=self.parse, priority=0)
使用 response.follow() 时,如果没有指定新的优先级,就会继承原请求的优先级。
去重¶
Spider 会根据 URL、HTTP 方法、请求体和会话 ID 计算指纹,自动去除重复请求。如果两个请求的指纹相同,第二个请求会被静默丢弃。
设置 dont_filter=True 可以允许重复请求,例如登录后再次访问同一页面:
yield Request("https://example.com/dashboard", dont_filter=True, callback=self.parse_dashboard)
# Or with response.follow
yield response.follow("/dashboard", dont_filter=True, callback=self.parse_dashboard)
通过 Spider 的类属性,可以更精确地控制指纹包含的内容:
| 属性 | 默认值 | 作用 |
|---|---|---|
fp_include_kwargs |
False |
将额外的请求参数纳入指纹,也就是传给会话获取方法的参数,例如请求头 |
fp_keep_fragments |
False |
计算指纹时保留 URL 片段(#section) |
fp_include_headers |
False |
将请求头纳入指纹 |
例如,需要将 https://example.com/page#section1 和 https://example.com/page#section2 视为不同 URL 时:
请求元数据¶
meta 字典可以在回调之间传递任意数据。当处理一个页面需要用到另一个页面的信息时,这很有用:
async def parse(self, response: Response):
for product in response.css("div.product"):
category = product.css("span.category::text").get("")
link = product.css("a::attr(href)").get()
if link:
yield response.follow(
link,
callback=self.parse_product,
meta={"category": category},
)
async def parse_product(self, response: Response):
yield {
"name": response.css("h1::text").get(""),
"price": response.css(".price::text").get(""),
# Access meta from the request
"category": response.meta.get("category", ""),
}
使用 response.follow() 时,当前响应的元数据会与新传入的元数据合并,新值优先。
Spider 系统也会自动保存一些元数据。例如,启用代理轮换后,可以通过 response.meta["proxy"] 获取该请求使用的代理。