LLM驱动爬虫:利用大语言模型自动解析动态DOM与智能提取非结构化数据
导读:在传统爬虫开发中,我们花费了80%的时间在与网页结构“搏斗”:编写脆弱的XPath、应对动态渲染的DOM、处理千奇百怪的非结构化文本。一旦网站改版,所有努力付诸东流。2026年,随着多模态大模型(Multimodal LLM)和长上下文窗口的普及,爬虫范式迎来了根本性变革——从“规则匹配”转向“语义理解”。本文将带你构建一个由LLM驱动的 inteligente 爬虫系统,只需一句自然语言指令,即可自动解析复杂页面、提取非结构化数据,并具备极强的抗干扰和自愈能力。
一、传统爬虫的至暗时刻 vs. LLM的新范式
1. 传统痛点
- 选择器地狱:
div.container > ul.list li:nth-child(3) span.price,只要前端加个div,整个表达式失效。 - 动态渲染噩梦:面对React/Vue生成的动态DOM,需要配置Selenium/Playwright,还要等待网络空闲,速度慢且资源消耗大。
- 非结构化数据无力:想从一段混杂的新闻正文中提取“事件时间、地点、人物”,传统正则表达式几乎无法胜任。
2. LLM驱动的新范式
- 语义提取:不再关心HTML标签层级,直接告诉AI:“提取页面上所有商品的名称、价格和评分”。
- 抗干扰强:即使网页布局大变,只要语义信息(文本内容)还在,LLM就能识别。
- 结构化输出:直接输出JSON,无需后续清洗。
二、核心架构:Prompt + DOM修剪 + 模型推理
直接使用完整HTML发送给LLM是不可行的(Token消耗大、噪声多)。我们需要一套**“精简 - 推理 - 验证”**的流程。
架构图
渲染错误:Mermaid 渲染失败: Parse error on line 8: ... F --> G[LLM API (Qwen/GPT-4o)] G -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PS'
关键技术点
- DOM修剪 (DOM Pruning):剔除
<script>,<style>,<svg>等无关标签,只保留包含文本内容的节点及其父级路径线索。 - 多模态辅助:对于极度复杂的布局(如嵌套表格、Canvas绘图),结合页面截图,利用多模态模型的视觉能力辅助定位。
- 结构化约束:使用JSON Schema强制模型输出标准格式,便于程序解析。
三、实战代码:构建智能提取器
我们将使用 Python 结合BeautifulSoup进行DOM清洗,调用2026年主流的本地/云端大模型API(以兼容OpenAI格式的接口为例,如Qwen-Max, GLM-Edge等)。
1. 环境准备
pipinstallbeautifulsoup4 lxml httpx pydantic# 假设使用兼容OpenAI SDK的库pipinstallopenai2. DOM智能清洗模块
不要直接把几兆的HTML扔给模型。我们需要提取“语义树”。
frombs4importBeautifulSoup,NavigableStringimportreclassDomPruner:def__init__(self,html:str,max_tokens:int=4000):self.soup=BeautifulSoup(html,'lxml')self.max_tokens=max_tokensdefprune(self)->str:# 1. 移除无用标签fortaginself.soup(['script','style','noscript','iframe','svg']):tag.decompose()# 2. 简化属性:只保留id, class, name, href, src等关键属性fortaginself.soup.find_all(True):attrs=dict(tag.attrs)keep_attrs={}forattrin['id','class','name','href','src','alt','title']:ifattrinattrs:keep_attrs[attr]=attrs[attr]tag.attrs=keep_attrs# 3. 压缩空白字符text=self.soup.get_text(separator=' ',strip=True)# 简单的截断策略,防止超出Token限制(实际生产需按Token计数)iflen(text)>self.max_tokens*4:# 粗略估算 1 token ≈ 4 charstext=text[:self.max_tokens*4]+"...[内容截断]"returntextdefget_structured_hint(self)->str:# 可选:保留部分DOM结构线索,帮助模型理解层级# 这里简化为提取所有链接和标题作为上下文参考hints=[]forhinself.soup.find_all(['h1','h2','h3']):hints.append(f"<Heading>{h.get_text(strip=True)}</Heading>")return"\n".join(hints)3. LLM提取引擎
定义清晰的Prompt和输出Schema。
importjsonfromopenaiimportOpenAIfrompydanticimportBaseModel,Field# 定义期望的输出结构classProductItem(BaseModel):name:str=Field(description="商品名称")price:float=Field(description="商品价格,纯数字")currency:str=Field(description="货币单位")rating:float=Field(description="评分,0-5之间",default=0.0)url:str=Field(description="商品详情页链接")classExtractionResult(BaseModel):success:booldata:list[ProductItem]error_message:str|None=NoneclassLLMScraper:def__init__(self,api_key:str,base_url:str,model:str="qwen-max-2026"):self.client=OpenAI(api_key=api_key,base_url=base_url)self.model=modeldefextract(self,html:str,instruction:str)->ExtractionResult:pruner=DomPruner(html)clean_content=pruner.prune()prompt=f""" 你是一个专业的网页数据提取助手。 【任务目标】{instruction}【网页内容摘要】{clean_content}【要求】 1. 忽略导航栏、页脚、广告等无关信息。 2. 如果价格包含符号(如¥, $),请分离数字和单位。 3. 如果没有找到数据,返回空列表,不要编造。 4. 必须严格遵循JSON Schema格式输出。 请输出JSON格式结果: """try:response=self.client.beta.parse.completions.create(model=self.model,messages=[{"role":"system","content":"你只输出JSON,不包含任何Markdown标记或解释性文字。"},{"role":"user","content":prompt}],response_format=ExtractionResult,# 2026年新特性:强制结构化输出temperature=0.0)returnresponse.choices[0].message.parsedexceptExceptionase:returnExtractionResult(success=False,data=[],error_message=str(e))4. 调用示例
if__name__=="__main__":# 模拟获取到的HTMLraw_html=requests.get("https://example-shop.com/products").text scraper=LLMScraper(api_key="sk-...",base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",# 以阿里云为例model="qwen-plus")# 自然语言指令:无需写XPath!instruction="提取页面上所有的手机商品,包括名称、价格、评分和购买链接。"result=scraper.extract(raw_html,instruction)ifresult.success:print(f"成功提取{len(result.data)}个商品:")foriteminresult.data:print(f"-{item.name}:{item.price}{item.currency}(评分:{item.rating})")else:print(f"提取失败:{result.error_message}")四、进阶策略:应对复杂场景
1. 动态加载与无限滚动
对于SPA(单页应用),LLM无法直接执行JS。
- 方案:使用轻量级浏览器(如Playwright)加载页面 -> 等待网络空闲 -> 获取最终HTML -> 交给LLM解析。
- 优化:利用LLM判断“是否还有下一页”,自动生成点击“下一页”的选择器或指令,形成闭环。
2. 多模态视觉增强
当页面大量使用Canvas或图片展示文字时,纯文本DOM失效。
- 方案:截取可视区域截图 -> 连同精简后的DOM一起发送给多模态模型(如GPT-4o, Qwen-VL)。
- Prompt:“结合截图和DOM结构,找出图中红色框选区域对应的商品名称。”
3. 自适应重试机制 (Self-Correction)
LLM偶尔会幻觉或格式错误。
- 逻辑:
- 解析返回的JSON。
- 如果JSON非法或字段缺失,将错误信息+原HTML片段再次发给LLM。
- Prompt:“上次提取失败了,原因是XXX。请重新分析并修正。”
这种“反思链”(Chain of Thought)能显著提高准确率。
五、成本与性能优化
在2026年,虽然模型便宜了,但大规模爬取仍需考虑成本。
- 缓存策略:对相同URL的HTML指纹(Hash)进行缓存,避免重复调用LLM。
- 小模型蒸馏:对于结构固定的页面,先用大模型生成几次训练数据,然后微调一个7B参数的小模型(如Llama-3-8B-Instruct)专门负责该类网站的提取,成本降低90%。
- 分层提取:
- 第一层:用小模型/正则快速判断页面类型和是否有数据。
- 第二层:只有确认有数据时,才调用大模型进行详细提取。
六、伦理与合规警示
技术越强大,责任越重大。
- Robots协议:LLM爬虫依然必须遵守
robots.txt。 - 频率限制:智能不代表可以无限制高频访问,需设置合理的延迟。
- 数据隐私:严禁提取个人敏感信息(PII),在Prompt中应加入“忽略个人隐私数据”的指令。
- 版权意识:提取的数据仅用于分析或学习,严禁直接用于商业转售或构建竞品。
七、结语
LLM驱动的爬虫不是要完全取代传统技术,而是将开发者从繁琐的**“维护选择器”中解放出来,去关注更有价值的“数据逻辑”与“业务洞察”**。
在未来,一个优秀的爬虫工程师,不再是XPath大师,而是Prompt工程师和数据架构师。你只需要告诉AI“想要什么”,剩下的,交给智能去完成。
