Scrapy 是 Python 生态中最强大的爬虫框架之一,其核心组件
Request
和Response
承担着数据抓取与处理的关键任务。本文深入解析 Scrapy 2.13.0 中Request
和Response
的高级用法,涵盖参数配置、回调函数、错误处理、子类扩展等,并结合 综合实战案例 展示如何在实际项目中高效运用这些组件。通过本文,读者不仅能掌握 Scrapy 的底层机制,还能学会如何优化爬虫性能、处理复杂场景,并提升代码可维护性。
1. Request 对象详解
Request
是 Scrapy 中表示 HTTP 请求的核心类,通常由 Spider 生成,并由 Downloader 执行后返回 Response
。
1.1 核心参数解析
参数 | 类型 | 说明 | 示例 |
---|---|---|---|
url |
str |
请求的目标 URL | "https://example.com" |
callback |
callable |
处理响应的回调函数 | self.parse_page |
method |
str |
HTTP 方法(GET/POST) | "POST" |
meta |
dict |
请求元数据(可跨请求传递) | {"user_id": 123} |
body |
bytes/str |
请求体(POST 数据) | {"key": "value"} |
headers |
dict |
自定义请求头 | {"User-Agent": "Scrapy"} |
cookies |
dict/list |
请求携带的 Cookie | {"session": "abc123"} |
priority |
int |
请求优先级(数值越大越优先) | 10 |
errback |
callable |
错误回调函数 | self.handle_error |
1.2 高级用法
(1) 动态参数传递(cb_kwargs
)
Scrapy 1.7+ 推荐使用 cb_kwargs
替代 meta
传递回调参数,避免与中间件冲突。
def parse(self, response):
yield scrapy.Request(
url="https://example.com/detail",
callback=self.parse_detail,
cb_kwargs={"item_id": 123}, # 传递给回调函数的参数
)
def parse_detail(self, response, item_id):
print(f"Processing item {item_id}") # 输出: Processing item 123
(2) 错误处理(errback
)
捕获请求异常(如 404、超时),并记录日志或重试。
import scrapy
from scrapy.spidermiddlewares.httperror import HttpError
from twisted.internet.error import DNSLookupError, TimeoutError
class ErrorHandlingSpider(scrapy.Spider):
name = "error_handler"
def start_requests(self):
yield scrapy.Request(
url="https://example.com/nonexistent",
callback=self.parse,
errback=self.handle_error, # 错误回调
)
def handle_error(self, failure):
if failure.check(HttpError):
response = failure.value.response
self.logger.error(f"HTTP Error {response.status}: {response.url}")
elif failure.check(DNSLookupError):
self.logger.error(f"DNS Lookup Failed: {failure.request.url}")
elif failure.check(TimeoutError):
self.logger.error(f"Request Timeout: {failure.request.url}")
(3) 动态生成请求(FormRequest
& JsonRequest
)
- 表单提交:
FormRequest
自动处理 HTML 表单数据。 - JSON 请求:
JsonRequest
直接发送 JSON 数据。
# 表单提交示例
yield scrapy.FormRequest(
url="https://example.com/login",
formdata={"username": "user", "password": "pass"},
callback=self.after_login,
)
# JSON 请求示例
yield scrapy.JsonRequest(
url="https://api.example.com/data",
data={"query": "scrapy"},
callback=self.parse_json,
)
2. Response 对象详解
Response
是 Downloader 返回的结果,包含 HTTP 响应的所有信息。
2.1 核心属性
属性 | 类型 | 说明 | 示例 |
---|---|---|---|
url |
str |
响应的 URL | "https://example.com" |
status |
int |
HTTP 状态码 | 200 |
headers |
dict |
响应头 | {"Content-Type": "text/html"} |
body |
bytes |
响应体(原始字节) | b"<html>..." |
text |
str |
解码后的文本(需继承 TextResponse ) |
"<html>..." |
request |
Request |
生成此响应的请求对象 | <GET https://example.com> |
2.2 高级用法
(1) 动态跟进链接(follow
& follow_all
)
# 跟进单个链接
yield response.follow(
url="https://example.com/next",
callback=self.parse_next,
)
# 跟进多个链接(TextResponse 特有)
for link in response.css("a::attr(href)").getall():
yield response.follow(link, callback=self.parse_link)
(2) 提取数据并传递到下个请求
通过 meta
或 cb_kwargs
跨请求传递数据。
def parse(self, response):
item_id = response.css("div.item::attr(id)").get()
yield scrapy.Request(
url=f"https://example.com/item/{item_id}",
callback=self.parse_item,
cb_kwargs={"item_id": item_id}, # 传递给回调函数
meta={"item_id": item_id}, # 传递给中间件或后续请求
)
def parse_item(self, response, item_id):
print(f"Processing item {item_id}") # 输出: Processing item 123
3. 综合实战案例:电商网站商品爬取
场景需求
爬取某电商网站的商品列表页,并跟进详情页提取价格、库存等信息,同时处理反爬机制(如动态 Cookie、请求限速)。
实现步骤
(1) 定义 Spider
import scrapy
from scrapy.http import JsonRequest
class EcommerceSpider(scrapy.Spider):
name = "ecommerce"
start_urls = ["https://example.com/products"]
def parse(self, response):
# 提取商品列表页中的商品链接
for product_link in response.css("a.product-link::attr(href)").getall():
yield response.follow(
product_link,
callback=self.parse_product,
cb_kwargs={"category": response.url.split("/")[-1]}, # 传递分类信息
)
# 翻页逻辑
next_page = response.css("a.next-page::attr(href)").get()
if next_page:
yield response.follow(next_page, callback=self.parse)
def parse_product(self, response, category):
# 提取商品详情
yield {
"name": response.css("h1::text").get(),
"price": response.css("span.price::text").get(),
"stock": response.css("span.stock::text").get(),
"category": category, # 从回调参数传递
}
def start_requests(self):
# 使用 JsonRequest 处理 API 接口(如果网站有 JSON API)
yield JsonRequest(
url="https://api.example.com/products",
callback=self.parse_api,
)
def parse_api(self, response):
data = response.json()
for product in data["products"]:
yield scrapy.Request(
url=product["detail_url"],
callback=self.parse_product,
cb_kwargs={"category": "api"}, # 标记为 API 来源
)
(2) 反爬策略优化
- 动态 Cookie:通过
cookies
参数传递登录态。 - 请求间隔:在
settings.py
中设置DOWNLOAD_DELAY
。 - User-Agent 轮换:使用
scrapy.downloadermiddlewares.useragent.UserAgentMiddleware
。
# settings.py 示例
DOWNLOAD_DELAY = 2 # 请求间隔 2 秒
USER_AGENT_LIST = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15",
]
4. 总结与启发
Request
的核心价值:灵活控制 HTTP 请求,支持动态参数、错误处理和表单提交。Response
的进阶用法:跨请求数据传递、动态跟进链接、高效数据提取。
实战经验
:
- 优先使用
cb_kwargs
替代meta
传递回调参数。 - 结合
FormRequest
和JsonRequest
处理复杂交互。 - 在实战中结合反爬策略提升爬虫稳定性。
通过本文的解析与案例,读者可以掌握 Scrapy 的底层机制,并在实际项目中灵活应用,构建高效、稳定的爬虫系统! 🚀