网页一改版爬虫就全崩?LLM自愈爬虫实战:选择器自动修复,运维量直降80%
发布时间:2026/10/31 22:25:39 锦皓数字建站

做工业数据采集这几年最头疼的事永远不是反爬是前端改版。很多站点反爬没把你搞死前端开发随手改个类名、调一下DOM层级、换个标签顺序你写的CSS选择器、XPath就全失效了数据抓空告警炸群。小改还好挨个修选择器要花大半天碰到全站重构等于爬虫重写一遍。去年我们团队维护的二十几个采集站点每月光花在修复选择器上的时间就有三四十个小时一半的运维精力都耗在这上面。后来尝试把LLM加进去做自愈选择器失效了自动识别新的路径不用人工介入。前后调了两个多月踩了幻觉、准确率、成本控制一堆坑最终普通改版的自愈成功率稳定在92%以上每月的人工运维时间降到了6小时以内效果比预期好很多。今天就把整套方案完整分享出来从设计思路、代码实现到踩坑优化全讲透看完就能嵌到自己的爬虫框架里。一、先想清楚传统的「耐造爬虫」方案差在哪在LLM之前大家也想了很多办法提升爬虫的鲁棒性也就是常说的Resilient Scraping但都有自己的天花板。绝对路径换相对路径不用层级过深的绝对XPath改用id、class、文本特征定位比绝对路径稳一点但类名一改还是直接挂。文本属性定位不靠DOM层级靠元素的文本、href、data-*属性定位稳定性更高但文本改了、属性删了照样失效。多规则兜底同一个元素写3-5个不同的选择器一个失效了试下一个。能应付小改动但改版幅度大一点还是全军覆没而且维护成本翻倍。这些方案本质都是「提前预判变化」靠人把可能的情况都想到。但前端改版是不可预测的你永远猜不到开发会改成什么样所以兜底规则再多也总有覆盖不到的情况。而LLM的思路完全不一样它不是靠预设规则是像人一样去「理解」页面结构根据旧元素的业务特征在新页面里找到对应的新元素。只要页面的业务逻辑没变哪怕DOM结构改得面目全非也能找对位置。二、LLM自愈爬虫的核心设计只做一件事修复选择器很多人做LLM爬虫一上来就让大模型写整个爬虫代码结果幻觉满天飞数据错了都不知道。我们的设计原则很明确LLM只负责选择器修复这一件事其余逻辑全靠原有爬虫框架。整个自愈流程触发时机非常明确当原有选择器匹配不到元素时才启动自愈流程正常情况下完全不调用LLM不增加额外成本。完整的执行链路是五步触发原有选择器匹配失败捕获异常进入自愈流程切片提取目标元素周边的DOM片段而不是整页HTML减少token消耗和干扰推理把旧选择器、旧元素特征、新页面DOM片段传给LLM生成新的选择器校验用新选择器去页面里匹配检查结果是否符合预期数量、文本、属性特征落库校验通过就更新选择器缓存下次直接用校验失败就触发人工告警核心是把LLM当成一个智能修复工具嵌在原有爬虫的异常处理链路里不侵入原有业务逻辑上线成本极低。三、手把手实现可直接落地的自愈模块接下来上完整代码基于Python实现兼容所有OpenAI协议的大模型本地部署的开源模型也能用。3.1 前置准备只需要两个基础依赖pipinstallbeautifulsoup4 openai用BeautifulSoup做DOM切片和结果校验openai库调用大模型接口通用度很高。3.2 第一步DOM切片别把整页HTML丢给LLM这是第一个大坑。一开始我直接把整页HTML传进去token又多、干扰又大LLM经常被无关元素带偏准确率很低。正确的做法是根据旧选择器的特征在新页面里定位大致的区域只截取目标周边的DOM片段再传给LLM。既省token又减少干扰准确率直接上一个台阶。frombs4importBeautifulSoupdefextract_related_dom(html,old_selector,context_range5):提取目标周边的DOM片段减少无关内容干扰soupBeautifulSoup(html,html.parser)# 尝试用旧选择器找近似区域找不到就取主体内容区targetsoup.select_one(old_selector)iftarget:# 向上找5层父元素作为上下文parenttargetfor_inrange(context_range):ifparent.parent:parentparent.parentelse:breakreturnstr(parent)# 旧选择器完全失效取页面主内容区兜底main_contentsoup.select_one(main, .content, #app)ifmain_content:returnstr(main_content)returnhtml[:5000]# 最后兜底只取前5000字符3.3 第二步核心Prompt设计精准控制LLM输出Prompt是自愈准确率的关键。要明确告诉LLM任务是什么、输入是什么、输出格式是什么、要遵守哪些规则绝对不能让它自由发挥。我们的Prompt经过十几轮优化核心包含四个部分角色定义、输入信息、输出要求、约束规则。defbuild_prompt(old_selector,element_desc,dom_snippet):returnf 你是专业的网页元素定位专家。请根据旧的选择器和元素描述在新的HTML片段中找到对应的目标元素生成新的CSS选择器。 【旧选择器】{old_selector}【元素业务含义】{element_desc}【新页面HTML片段】 html{dom_snippet}要求找到和旧元素业务含义一致的目标元素生成最稳定的CSS选择器优先使用id、data-*属性、文本特征、稳定的类名避免使用层级过深的绝对路径不要使用动态生成的随机类名优先选择语义化的、不随版本变化的特征严格按JSON格式输出不要额外解释格式如下{{“new_selector”: “新的CSS选择器”,“confidence”: 0.95,“reason”: “选择理由”}}置信度0-1越高表示越准确。如果完全找不到对应元素置信度填0。“”### 3.4 第三步调用LLM获取新选择器 用标准的OpenAI接口调用支持所有兼容协议的模型包括本地开源模型。 python from openai import OpenAI client OpenAI( api_key你的API_KEY, base_url你的接口地址 ) def llm_fix_selector(old_selector, element_desc, html): dom_snippet extract_related_dom(html, old_selector) prompt build_prompt(old_selector, element_desc, dom_snippet) try: resp client.chat.completions.create( modelqwen3-7b, # 轻量模型足够用成本极低 messages[{role: user, content: prompt}], temperature0.1, # 温度调低减少随机性 response_format{type: json_object} ) import json result json.loads(resp.choices[0].message.content) return result except Exception as e: print(fLLM调用失败: {e}) return {confidence: 0, new_selector: }实战建议用7B参数的轻量模型完全足够成本只有千亿大模型的几十分之一准确率也差不了多少。我们用本地部署的Qwen3-7B和千亿模型的准确率差不到3%单次调用成本可以忽略不计。3.5 第四步结果校验杜绝幻觉这是最重要的一步没有校验的自愈就是埋雷。LLM生成的选择器可能是错的直接用会导致脏数据入库比爬虫挂了更可怕。我们做了三层校验全部通过才认为修复成功defvalidate_selector(html,new_selector,old_features):三层校验确保新选择器正确soupBeautifulSoup(html,html.parser)elementssoup.select(new_selector)# 1. 数量校验匹配到的元素数量和预期是否一致iflen(elements)!old_features.get(expect_count,1):returnFalse# 2. 特征校验元素的文本、属性是否符合旧特征foreleinelements:# 检查文本是否包含预期关键词ifold_features.get(keywords):textele.get_text()ifnotany(kintextforkinold_features[keywords]):returnFalse# 检查关键属性是否存在ifold_features.get(required_attrs):forattrinold_features[required_attrs]:ifnotele.has_attr(attr):returnFalse# 3. 置信度校验LLM给出的置信度高于阈值才通过returnTrue只有三层校验都通过才更新选择器任何一层不通过都判定为自愈失败触发人工告警。四、嵌入现有爬虫零侵入式改造不用重写现有爬虫只要在元素匹配的异常捕获里加一行自愈逻辑就行。比如用DrissionPage的采集场景改造后是这样defsafe_get_element(page,selector,element_desc,old_features):带自愈能力的元素获取函数try:elepage.ele(selector,timeout2)returneleexceptException:# 匹配失败触发自愈流程htmlpage.html fix_resultllm_fix_selector(selector,element_desc,html)iffix_result[confidence]0.8andvalidate_selector(html,fix_result[new_selector],old_features):# 修复成功更新缓存print(f选择器自愈成功:{selector}-{fix_result[new_selector]})update_selector_cache(selector,fix_result[new_selector])returnpage.ele(fix_result[new_selector])else:# 修复失败触发告警send_alert(f选择器失效且自愈失败:{selector})raise原有业务代码完全不用改只是把原来的元素获取函数换成带自愈的版本半天就能完成全项目改造。五、踩了两个月的坑这些经验一定要避开坑1整页HTML丢给LLM费钱还不准整页HTML动辄几万字符token消耗大无关内容多LLM很容易被干扰找错元素。一定要做DOM切片只保留目标周边的上下文准确率能提升20%以上token消耗还能降到原来的十分之一。坑2温度太高结果五花八门温度参数一定要调低建议0.1以下。选择器修复是确定性任务不需要创意温度越低输出越稳定幻觉越少。一开始用默认0.7每次生成的选择器都不一样准确率忽高忽低调到0.1之后稳定多了。坑3不加校验直接用脏数据比挂了更可怕千万别相信LLM的输出一定要加多层校验。我们早期踩过这个坑LLM把别的元素当成了目标结果抓了一周的错误数据返工花了好几天。校验再严都不为过宁可自愈失败告警也不能让错误数据入库。坑4什么都让LLM做准确率反而低不要让LLM负责整个爬虫逻辑就聚焦在选择器修复这一件事上。任务越单一提示词越好写准确率越高越容易校验。贪多求全最后哪样都做不好。坑5用大模型跑成本太高完全没必要用千亿参数的大模型7B甚至3B的轻量模型就足够胜任这种结构化任务。本地部署开源模型的话成本几乎可以忽略比人工修划算太多。六、实测效果与适用边界我们在二十几个采集站点上跑了三个月核心数据非常直观普通小改版改类名、调层级自愈成功率92.6%基本不用人工介入中等改版模块重构、布局调整自愈成功率68.3%剩下的人工处理全站大重构成功率不到30%还是需要人工重写整体人工运维时间从每月30-40小时降到5-7小时下降80%以上不是万能的这些场景不适合页面完全重构业务逻辑都变了LLM也没法无中生有对准确率要求100%的场景还是人工审核更稳妥页面结构极其简单改版频率极低的站点没必要加LLM增加复杂度最适合的场景站点多、改版频繁人工维护成本高对可用性要求高不能动不动就挂数据允许少量容错有校验机制兜底总结LLM不是银弹不能解决爬虫的所有问题但在选择器自愈这件事上确实能大幅降低运维成本。它的价值从来不是100%替代人工而是把80%的简单、重复的修复工作自动化让人只去处理那20%的复杂改版。把工程师从重复的修选择器的体力活里解放出来去做更有价值的事。技术落地永远是这样不用追求高大上的方案找对具体的场景用合适的技术解决具体的痛点就是最好的方案。合规提示本文仅讨论爬虫技术的运维优化方案采集数据请严格遵守《网络安全法》及目标网站的robots协议不得用于非法用途。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。