Kimurai搭配selenium_chrome爬取Loblaws搜索页结果异常
Loblaws站点抓取稳定性异常修复方案
核心异常根因定位
- 硬编码
sleep等待完全不匹配动态渲染的实际节奏
你当前使用固定sleep 30的逻辑,本质是不管页面是否完成商品列表渲染,到时间就开始提取DOM。但Loblaws搜索页是React驱动的异步渲染结构:主框架HTML返回后,商品列表数据是后续通过接口异步拉取、再动态渲染到DOM的,这个渲染过程的耗时受IP信誉、站点风控策略、网络波动影响极大,固定等待完全覆盖不了这种不确定性。从你贴的日志看,页面get请求在19:40:20就完成了,后续30秒等待的时间里,站点很可能返回的是不带商品数据的风控空壳页,这种情况下哪怕等300秒也取不到目标节点。 - XPath选择器写法存在根本性错误
你当前写的选择器都是全局绝对路径写法,哪怕节点正常加载出来也会取值错误:- 循环块内取商品名的逻辑,没有在当前循环的
result节点下做相对查找,直接用全局路径//span[@class='product-name product-name--product-tile'],哪怕进了循环,取的也是全页面第一个商品的名字,不是当前循环项的内容 - 价格选择器写死了节点位置
li[1],不管循环到第几个商品,取的永远是页面第一个列表项里的价格
- 循环块内取商品名的逻辑,没有在当前循环的
- 基础反爬绕过能力不足
当前配置的UA是2018年的老版本Firefox标识,加上原生headless Chrome的默认特征暴露,站点很容易把请求判定为机器人访问,直接返回不带商品数据的空壳页——这也是为什么只有10%的请求能拿到正常数据,剩下90%都是被风控拦截后的空响应。
代码修复具体操作
用显式等待替代硬编码sleep
不要用无意义的固定等待,替换成Selenium原生的显式等待,等到商品列表节点真的出现在DOM里再开始解析,既缩短整体请求耗时,又能保证节点加载完成:
def parse(response, url:, data: {}) # 最多等待20秒,每隔0.5秒检查一次目标节点是否加载完成 wait = Selenium::WebDriver::Wait.new(timeout: 20, interval: 0.5) wait.until { browser.find_elements(:xpath, "//li[@class='product-tile-group__list__item']").any? } # 检查节点是否真的加载完成,空结果直接抛出错误 product_nodes = browser.find_elements(:xpath, "//li[@class='product-tile-group__list__item']") if product_nodes.empty? # 保存当前页面源码用于排查风控问题 save_to "debug/loblaws_#{Time.now.to_i}.html", browser.page_source raise "未检测到商品列表节点,疑似触发站点风控" end product_nodes.each do |result| item = {} # 改为当前循环节点下的相对XPath,避免取错值 item[:name] = result.find(:xpath, ".//span[@class='product-name product-name--product-tile']")&.text&.squish item[:price] = result.find(:xpath, ".//span[contains(@class, 'price__value')]")&.text&.squish # 正常存储item数据 save_to data[:items] ||= [], item end end
调整Kimurai配置降低风控触发概率
替换原有简陋的配置,补全反爬特征隐藏设置:
class FoodsearchSpider < Kimurai::Base @name = 'foodsearch_spider' @engine = :selenium_chrome @config = { # 替换为当前主流版本的Chrome真实UA user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36", headless_mode: true, window_size: [1920, 1080], browser: { args: [ '--disable-blink-features=AutomationControlled', '--disable-infobars', '--start-maximized' ] } } end
避坑提醒
- 不要为了提升稳定性无限制加大sleep时长:如果请求被风控拦截,等待再久也拿不到数据,反而会把整体请求耗时拉到超时阈值
- 绝对不要使用写死层级的绝对XPath(比如从html根节点逐层数到目标div的路径),站点稍微调整页面结构就会直接失效,还容易取错值
- 遇到拿不到节点的情况,第一时间保存当前页面源码排查:先确认是站点没有返回商品数据,还是自己的选择器写错了,不要盲目调整等待时长
内容的提问来源于stack exchange,提问作者kab0b
相关产品推荐
相关产品推荐

