Scrapy爬取亚马逊畅销书时XPATH正确却偶尔返回None,如何排查解决?
亚马逊Scrapy爬虫偶发返回None问题排查与解决方案
根因定位
首先可以排除pipelines的问题,问题全部出在spider逻辑与反爬应对层面,核心原因如下:
- 亚马逊反爬策略触发:偶发返回验证码页面、403/503错误页、跳转登录页,返回内容不是正常商品页,XPATH无法匹配到对应节点,返回None
- Item对象复用BUG:
parse_category方法中,Item实例化写在了遍历商品的循环外,Scrapy是异步框架,循环生成的多个请求还没执行完,Item的字段就被后续循环修改,导致字段错乱、偶发为空 - 页面结构适配不全:亚马逊不同品类、不同版本的商品详情页结构存在差异,比如部分老版详情页的商品信息放在
#productDetailsTable节点中,而非你用的#detailBullets_feature_div,仅写一套XPATH必然会出现匹配失败的情况 - 请求头配置缺陷:默认Scrapy请求头特征明显,UA固定或者没有携带正确的Referer,容易被亚马逊识别为爬虫,返回异常内容
对应解决方法
- 修复Item复用BUG
修改parse_category方法,将Item实例化放到循环内部,确保每个商品对应独立的Item对象:
def parse_category(self, response): book_containers = response.xpath('//ol[@id="zg-ordered-list" and @class="a-ordered-list a-vertical"]/li') for book_dir in book_containers: # 每次循环新建独立Item,避免异步复用冲突 item = AmzbestsellerItem() # 目录、分类字段赋值逻辑移到循环内 item['dir_level_1'] = response.xpath('normalize-space(//ul[@id="zg_browseRoot"]/ul/li[@class = "zg_browseUp"]/a/text())').get(default='') item['dir_level_2'] = response.xpath('normalize-space(//ul[@id="zg_browseRoot"]/ul/ul/li[@class = "zg_browseUp"]/a/text())').get(default='') item['dir_level_3'] = response.xpath('normalize-space(//ul[@id="zg_browseRoot"]/ul/ul/ul/li[@class = "zg_browseUp"]/a/text())').get(default='') item['dir_level_4'] = response.xpath('normalize-space(//ul[@id="zg_browseRoot"]/ul/ul/ul/ul/li[@class = "zg_browseUp"]/a/text())').get(default='') item['dir_level_5'] = response.xpath('normalize-space(//ul[@id="zg_browseRoot"]/ul/ul/ul/ul/ul/li[@class = "zg_browseUp"]/a/text())').get(default='') item['category_name'] = response.xpath('normalize-space(//span[@class="zg_selected"]/text())').get(default='') item['category_url'] = response.url.split('/ref')[0] # 原有商品链接提取逻辑不变 book_dir = book_dir.xpath('./span[@class="a-list-item"]/div[@class="a-section a-spacing-none aok-relative"]/span[@class="aok-inline-block zg-item"]/a[@class="a-link-normal"]/@href').get() book_url = response.urljoin(book_dir) item['book_url'] = book_url.split('/ref')[0] yield Request(book_url, callback=self.parse_book, meta={'item': item}, dont_filter=True)
- 增加反爬应对配置
- 在
settings.py中开启随机User-Agent,可直接使用scrapy-fake-useragent中间件 - 配置重试中间件,将403、503、500等错误状态码加入重试列表,设置重试次数≥3
- 高频率爬取需配置住宅代理池,避免IP被封
- 开启自动限速
AUTOTHROTTLE_ENABLED = True,降低请求触发反爬的概率
- 优化XPATH兼容逻辑
给所有XPATH匹配增加fallback逻辑,同时给get()方法设置默认值避免返回None,以出版社字段为例:
# 先匹配新版详情页的detailBullets,匹配不到就匹配老版的表格结构 item['publisher'] = response.xpath('//div[@id = "detailBullets_feature_div"]//span[contains(text(),"Publisher")]/following-sibling::span/text()').get(default='') if not item['publisher']: item['publisher'] = response.xpath('//table[@id="productDetailsTable"]//th[contains(text(),"Publisher")]/following-sibling::td/text()').get(default='') # 所有字段都按该逻辑补充默认值和兼容规则
- 异常响应拦截
在parse_book方法开头先判断响应是否正常,如果页面包含验证码、登录等特征,直接抛出异常跳过或触发重试:
def parse_book(self, response): # 响应合法性校验 if "Enter the characters you see below" in response.text or "Sign in" in response.text or response.status >=400: from scrapy.exceptions import IgnoreRequest raise IgnoreRequest("异常响应,跳过当前请求") # 原有字段提取逻辑不变
内容的提问来源于stack exchange,提问作者Avn
相关产品推荐
相关产品推荐

