
本文详解如何使用 scrapy 无需 splash 即可完整抓取 anaconda 商品页面中所有颜色与尺寸组合对应的标题、原价、促销价等结构化信息,并提供可直接运行的健壮爬虫实现。
本文详解如何使用 scrapy 无需 splash 即可完整抓取 anaconda 商品页面中所有颜色与尺寸组合对应的标题、原价、促销价等结构化信息,并提供可直接运行的健壮爬虫实现。
在电商网站如 Anaconda(anacondastores.com)中,商品常以“颜色 × 尺寸”形式呈现多个变体(variants),例如 Mantis Green × 6lb、Spool Yellow × 30lb 等。这些变体通常不全部渲染在初始 HTML 中,而是通过前端 JS 动态加载或按需请求 JSON 接口。但关键在于:该站实际将变体元数据内嵌于页面 DOM 属性中,且每个变体均有唯一可构造的 URL —— 因此无需 Splash 渲染,纯 Scrapy 即可高效采集。
✅ 核心策略:两级链接发现 + 属性驱动解析
不同于依赖 JavaScript 执行的方案,本方法基于对网页源码的深度分析,提取两类关键属性:
- data-url:位于 .js-variant-style-picker 元素上,对应不同颜色变体的独立详情页 URL(如 /.../BP90140299-mantis-green);
- data-variant-size-code:位于 .size-variant a 链接中,代表尺寸编码(如 6lb),拼接后构成尺寸变体请求地址。
? 技巧提示:打开浏览器开发者工具 → Network 面板 → 切换颜色/尺寸 → 观察 XHR 请求,即可快速定位真实变体接口格式(此处为 /{sku}-{color}?version=7 或 /{size-code}?version=7)。
? 完整可运行爬虫代码
import scrapy
class AnacondaSpider(scrapy.Spider):
name = "anaconda"
allowed_domains = ["www.anacondastores.com"]
# 起始页建议使用基础 SKU 页面(含所有颜色入口)
start_urls = [
"https://www.anacondastores.com/fishing/fishing-line/braid-line/shimano-kairiki-8-braid-line-150-metre-spool/BP90140299"
]
def parse(self, response):
# Step 1: 提取所有颜色变体 URL 并递归跟进
color_urls = response.css(".js-variant-style-picker::attr(data-url)").getall()
for url in color_urls:
if url:
yield response.follow(url, callback=self.parse_color)
# Step 2: 若当前页已含尺寸选择器(如进入某颜色页后),直接提取尺寸编码
size_codes = response.css(".size-variant a::attr(data-variant-size-code)").getall()
for code in size_codes:
if code:
# 构造尺寸变体请求 URL(注意:部分页面需加 ?version=7 参数才能返回完整数据)
variant_url = response.urljoin(f"{code}?version=7")
yield scrapy.Request(variant_url, callback=self.parse_variant)
def parse_color(self, response):
"""处理颜色详情页:提取尺寸选项并生成尺寸变体请求"""
size_codes = response.css(".size-variant a::attr(data-variant-size-code)").getall()
for code in size_codes:
if code:
variant_url = response.urljoin(f"{code}?version=7")
yield scrapy.Request(variant_url, callback=self.parse_variant)
def parse_variant(self, response):
"""统一解析变体页面:从 data-* 属性中提取结构化字段"""
item = {
"url": response.url,
"title": response.css("#productContentWrapper > div::attr(data-product-name)").get(),
"price": response.css("#productContentWrapper > div::attr(data-product-metric1)").get(), # 原价(含货币符号)
"sale_price": response.css("#productContentWrapper > div::attr(data-product-price)").get(), # 促销价
"size": response.css("#productContentWrapper > div::attr(data-product-dimension13)").get(), # 尺寸标识(如 '6lb')
"color": response.css("#productContentWrapper > div::attr(data-product-dimension12)").get(), # 颜色标识(如 'Mantis Green')
}
# ✅ 关键增强:自动补全缺失字段(提升鲁棒性)
if not item["color"]:
# 从 URL 路径反推颜色(如 /.../BP90140299-mantis-green → 'Mantis Green')
import re
color_match = re.search(r"-([a-zA-Z0-9-]+)$", response.url)
item["color"] = color_match.group(1).replace("-", " ").title() if color_match else None
yield item⚠️ 注意事项与最佳实践
- 避免重复采集:response.follow() 自动去重,但建议在 settings.py 中启用 DUPEFILTER_CLASS = 'scrapy.dupefilters.RFPDupeFilter'(默认已启用);
- 反爬应对:添加合理 DOWNLOAD_DELAY = 1 和 RANDOMIZE_DOWNLOAD_DELAY = True,必要时配置 USER_AGENT;
- 字段容错:get() 方法返回 None,生产环境应增加空值校验或默认值(如 item.get("price") or "N/A");
- URL 构造安全:始终使用 response.urljoin() 而非字符串拼接,确保相对路径正确解析;
- 扩展性设计:若后续需支持更多维度(如材质、包装),只需新增对应 CSS 选择器和 data-* 属性提取逻辑。
✅ 预期输出示例(JSON Lines 格式)
{"url": "https://www.anacondastores.com/.../6lb?version=7", "title": "Shimano Kairiki 8 Braid Line", "price": "$29.99", "sale_price": "$24.99", "size": "6lb", "color": "Mantis Green"}
{"url": "https://www.anacondastores.com/.../8lb?version=7", "title": "Shimano Kairiki 8 Braid Line", "price": "$29.99", "sale_price": "$24.99", "size": "8lb", "color": "Mantis Green"}
{"url": "https://www.anacondastores.com/.../6lb?version=7", "title": "Shimano Kairiki 8 Braid Line", "price": "$29.99", "sale_price": "$24.99", "size": "6lb", "color": "Spool Yellow"}
...该方案兼顾简洁性与可维护性,适用于 Anaconda 全站变体结构一致的商品页,是 Scrapy 工程化采集的典型范式。

















