根本原因是Scrapy默认请求缺乏真实浏览器特征,且未处理JS渲染、Referer校验、Cookie会话等关键环节;电商页商品参数、价格、SKU多由AJAX动态加载,纯HTML抓取必然缺失核心字段。

Scrapy抓取电商详情页时,为什么总是被封或返回空数据?
根本原因不是反爬强度高,而是默认的scrapy.Spider发起请求时缺少真实浏览器特征,且未处理动态渲染、Referer校验、Cookie会话延续等关键环节。电商页普遍依赖前端 JS 渲染商品参数、价格、SKU,纯 HTML 抓取大概率拿不到price、stock等字段。
实操建议:
- 优先在
start_requests()中手动设置headers,至少包含User-Agent和Referer(Referer必须是该商品列表页URL) - 对含 JS 渲染的字段(如“已售”“评价数”),不要硬等
response.css(),改用SplashRequest或scrapy-playwright;若用后者,需在settings.py中启用DOWNLOAD_HANDLERS并配置playwright启动参数 - 禁用
ROBOTSTXT_OBEY = True,否则多数电商站的/robots.txt会直接屏蔽/item/路径 - 别在
parse()里直接调用time.sleep()——Scrapy 是异步框架,sleep 会阻塞整个引擎;应改用DOWNLOAD_DELAY或AUTOTHROTTLE_ENABLED
Scrapy分布式部署时,Redis队列里的URL重复入队怎么破?
Scrapy-Redis 默认只去重request_fingerprint,但电商详情页URL常带跟踪参数(如?spm=xxx&utm_source=xxx),导致同一商品被多次抓取。更隐蔽的问题是:不同爬虫实例从Redis读取URL后,若没及时标记“正在处理”,可能被其他节点重复消费。
实操建议:
- 重写
dupefilter类,在request_fingerprint()前先用urllib.parse.urlparse()提取path,再用urlunparse()重建纯净URL,丢弃所有query参数 - 在
Spider的make_requests_from_url()中,为每个URL附加唯一meta['task_id'],并在pipeline中将成功入库的task_id写入Redis的SET结构做二次去重 - 避免用
redis.lpush()直接推URL——改用redis.sadd()推入SET,天然去重;消费端用spop而非rpop,防止多个worker抢到同一任务
如何让Scrapy稳定提取电商页的多规格SKU与实时价格?
电商详情页的SKU数据几乎从不放在HTML源码里,而是通过AJAX请求/api/item/sku?itemId=123或WebSocket推送。直接解析DOM只会拿到占位符或静态默认值。价格还常受登录态、地域、会员等级影响,返回结果非幂等。
实操建议:
- 用浏览器开发者工具过滤
XHR,找到真实SKU接口;其Referer、Cookie、X-Requested-With头必须完整复现,尤其注意Cookie中track_id或uuid这类设备标识字段需跨请求复用 - SKU接口返回JSON时,字段名极不规范:有的叫
skuList,有的叫props,有的嵌套在data.result下——务必在parse_sku()中加try/except KeyError并记录原始响应体,否则爬虫静默失败 - 价格字段常为字符串(如
"¥299.00")或对象(如{"price":"299.00","promoPrice":"249.00"}),提取后统一转成float并存为price和promo_price两个字段,避免后续分析时类型错误
Scrapy+Redis分布式下,如何避免商品数据写入MySQL时主键冲突或丢失?
多个Scrapy worker并发写入同一张表时,若靠INSERT IGNORE或ON DUPLICATE KEY UPDATE兜底,容易掩盖逻辑问题:比如A worker写入stock=10,B worker几秒后写入stock=8却因时间差被忽略,最终库存状态错乱。
实操建议:
- MySQL表必须设复合唯一索引:
UNIQUE KEY `uk_shop_item` (`shop_id`, `item_id`),其中shop_id来自URL域名解析(如jd.com→1),不能只依赖item_id - 在Pipeline中用
REPLACE INTO替代INSERT,确保每次写入都是全量覆盖;但要注意REPLACE会触发DELETE+INSERT,自增ID会跳变,若业务依赖连续ID则必须改用INSERT ... ON DUPLICATE KEY UPDATE - 对价格、库存等高频更新字段,加
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,后续可查出“最后更新时间早于10分钟”的脏数据单独重抓
分布式爬虫最难的从来不是并发数量,而是各节点间状态感知缺失——URL去重、数据写入、异常重试,每个环节都得在无共享内存的前提下靠Redis或数据库做协调。稍有疏忽,就会出现“看着都在跑,数据却漏一半”的情况。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











