「AI Python 系列」第 03 栏 · Python 爬虫实战
全栏 15 篇 · 零成本跟完 🍃
作者:梅雅达编程笔记
首发:CSDN
摘要:有些网页结构复杂到
XPath根本写不出来——嵌套表格、混合排版、图文混排。这时候与其死磕HTML结构,不如换个思路:截图,让视觉模型"看图说话"。Day 13教你用GLM-4V-Flash视觉模型,直接把页面截图扔给它,让它输出你要的结构化数据。对比文本模型和视觉模型各自的适用场景,让你知道什么时候该用哪个。
前面讲了用文本模型处理 HTML。但有些页面,给你 HTML 也没用:
- 表格嵌套了好几层,
<td>里套<div>再套<table> - 数据散落在不同位置,靠排版让人看懂,HTML 结构完全没逻辑
- 图片里包含文字信息(比如价格标签是张图片)
Canvas渲染的图表数据
这时候XPath、正则、甚至文本大模型都帮不了你。
该请视觉模型上场了。
一、视觉模型能做什么
简单说:你给它一张图片,它告诉你图片里有什么。
GLM-4V-Flash 是智谱的免费视觉模型,可以理解图片内容并回答问题。
我们的用法:
- 用
DrissionPage把页面截图 - 把截图发给 GLM-4V-Flash
- 让模型从图片中提取你要的结构化数据
- 返回 JSON
不用分析 HTML,不用写选择器,你看到什么,AI 就能提取什么。
二、基础流程
第一步:页面截图
用DrissionPage截图:
fromDrissionPageimportChromiumPage page=ChromiumPage()page.get("https://example-complex-table.com/")# 全屏截图page.get_screenshot(path="page_screenshot.png")# 或者只截某个区域(注意:page.ele 可能命中隐藏的布局表格,# 建议遍历找第一个有宽高的可见表格,避免 NoRectError)element=Noneforeleinpage.eles("css:table"):try:w,h=ele.rect.sizeifwandhandw>0andh>0:element=elebreakexceptException:continueifelementisNone:page.get_screenshot(path="table_only.png")# 兜底整页截图else:element.scroll.to_see()# 滚动到表格位置,确保完整渲染element.get_screenshot(path="table_only.png")page.quit()踩坑提醒:很多网站(尤其是政府网站)的第一个
<table>是用来做页面布局的隐藏表格,display:none没有宽高。直接用page.ele("css:table")再get_screenshot()会报NoRectError: 该元素没有位置及大小。上面的遍历写法可以自动跳过隐藏表格,找到第一个真正可见的。
第二步:发给视觉模型
importbase64fromopenaiimportOpenAI client=OpenAI(api_key="YOUR_API_KEY",base_url="https://open.bigmodel.cn/api/paas/v4/")# 读取图片并编码withopen("table_only.png","rb")asf:img_base64=base64.b64encode(f.read()).decode()# 调用视觉模型response=client.chat.completions.create(model="glm-4v-flash",# GLM-4V-Flash 视觉模型messages=[{"role":"user","content":[{"type":"text","text":"请从这张图片中的表格里提取所有数据,返回 JSON 数组。每行一个对象,字段名用表格的表头。"},{"type":"image_url","image_url":{"url":f"data:image/png;base64,{img_base64}"}}]}])print(response.choices[0].message.content)就这么简单。截图 → 发给模型 → 拿到结果。
三、实战:从复杂公告页提取表格数据
场景
假设你要从政府网站提取一份公告中的表格数据。这个表格合并了单元格、嵌套了子表格,HTML 结构极其复杂:
<tableclass="government-notice"><tr><tdcolspan="3">2026年度重点项目公示</td></tr><tr><tdrowspan="2">序号</td><td>项目名称</td><td>预算(万元)</td></tr><tr><tdcolspan="2">详细信息</td></tr><!-- 后面还有几十行嵌套结构 --></table>用XPath提取?写到你怀疑人生。
用视觉模型解决
""" Day 13 示例代码:视觉模型提取复杂表格 作者:梅雅达编程笔记 """fromDrissionPageimportChromiumPageimportbase64importjsonimporttimefromopenaiimportOpenAI client=OpenAI(api_key="YOUR_API_KEY",base_url="https://open.bigmodel.cn/api/paas/v4/")deffind_visible_table(page,timeout=10):"""遍历页面所有 table,返回第一个真正可见(有宽高)的元素。 政府网站常有隐藏的布局表格,直接 page.ele 会报 NoRectError。 """page.wait.ele_displayed("css:table",timeout=timeout)foreleinpage.eles("css:table"):try:w,h=ele.rect.sizeifwandhandw>0andh>0:returneleexceptException:continuereturnNonedefscreenshot_table(page,path):"""截取页面中第一个可见表格;找不到则整页截图兜底。"""table=find_visible_table(page)iftableisNone:print("未找到可见表格,改为整页截图")page.get_screenshot(path=path)else:table.scroll.to_see()time.sleep(0.5)# 等滚动后的渲染table.get_screenshot(path=path)defextract_table_from_image(image_path,prompt):"""用视觉模型从图片中提取结构化数据"""withopen(image_path,"rb")asf:img_base64=base64.b64encode(f.read()).decode()response=client.chat.completions.create(model="glm-4v-flash",messages=[{"role":"user","content":[{"type":"text","text":prompt},{"type":"image_url","image_url":{"url":f"data:image/png;base64,{img_base64}"}}]}],temperature=0.1)content=response.choices[0].message.content.strip()# 提取 JSONif"```json"incontent:content=content.split("```json")[1].split("```")[0]elif"```"incontent:content=content.split("```")[1].split("```")[0]returnjson.loads(content)defmain():# 1. 打开页面并截图page=ChromiumPage()page.get("http://www.ccgp.gov.cn/cggg/dfgg/zbgg/202607/t20260716_26951272.htm")# 截图(只截可见表格区域,已修复 NoRectError)screenshot_table(page,"table_screenshot.png")page.quit()# 2. 用视觉模型提取prompt="""请从这张表格截图中提取所有数据。 要求: 1. 返回 JSON 数组,每行一个对象 2. 字段名使用表格的表头 3. 合并单元格的内容填到对应的每一行 4. 如果有子表格,展开到同一层级 5. 只返回 JSON,不要其他内容 示例输出格式: [ {"序号": 1, "项目名称": "智慧城市项目", "预算_万元": 5000, "负责人": "张三"}, {"序号": 2, "项目名称": "数据平台项目", "预算_万元": 3000, "负责人": "李四"} ]"""print("正在用视觉模型分析表格...")data=extract_table_from_image("table_screenshot.png",prompt)print(f"\n提取到{len(data)}条数据:")print("-"*60)foritemindata:print(json.dumps(item,ensure_ascii=False))# 3. 保存importpandasaspd df=pd.DataFrame(data)df.to_csv("extracted_table.csv",index=False,encoding="utf-8-sig")print(f"\n已保存到 extracted_table.csv")if__name__=="__main__":main()运行结果示例:
正在用视觉模型分析表格... 提取到 1 条数据: ------------------------------------------------------------ {"供应商名称": "甘肃禾木物业有限责任公司", "供应商地址": "甘肃省张掖市山丹县南大街16号", "中标金额": "2627700.00", "评审得分": 82.19} 已保存到 extracted_table.csv四、视觉模型 vs 文本模型
两种方法各有优劣,不是替代关系:
| 维度 | 文本模型(处理 HTML) | 视觉模型(处理截图) |
|---|---|---|
| 速度 | 快(文本传输) | 慢(图片传输 + 理解) |
| 成本 | 低(文本 token 少) | 高(图片 token 多) |
| 准确率 | 高(数据结构清晰时) | 中高(偶尔识别错误) |
| 适用场景 | 结构化的 HTML | 复杂排版、图片数据、Canvas |
| 抗改版 | 中(改 HTML 结构可能失效) | 高(只要视觉上没变就不影响) |
什么时候用视觉模型
- HTML 结构极度复杂,文本模型搞不定
- 数据在图片里(价格标签、二维码、图表)
Canvas渲染的内容(ECharts图表、地图)- 需要 OCR 的场景(扫描件、PDF 截图)
- 页面频繁改版,你不想每次都改解析代码
什么时候用文本模型
- HTML 结构规整,选择器好写
- 数据量大,需要考虑成本和速度
- 批量处理,几千条数据
最佳实践:先用文本,不行再视觉
defsmart_extract(url,prompt_template):"""智能提取:先尝试文本模型,失败再上视觉模型"""# 1. 先尝试直接获取 HTML + 文本模型try:response=requests.get(url,headers=headers)html=response.text# 用文本模型提取data=extract_with_text_model(html,prompt_template)ifdataandlen(data)>0:print("文本模型提取成功")returndataexcept:pass# 2. 文本模型搞不定,用视觉模型print("文本模型提取失败,切换到视觉模型...")page=ChromiumPage()page.get(url)page.get_screenshot(path="temp_screenshot.png")page.quit()data=extract_with_vision_model("temp_screenshot.png",prompt_template)print("视觉模型提取完成")returndata五、更多应用场景
1. 提取图表数据
prompt="""这张图片包含一个柱状图/折线图。 请提取图表中的所有数据点,返回 JSON 数组。 格式:[{"label": "类别名", "value": 数值}] 如果有多个系列,每个系列分别提取。"""2. 提取图片中的价格
prompt="""这张图片是商品列表页的截图。 请提取每个商品的名称和价格,返回 JSON 数组。 格式:[{"name": "商品名", "price": 数字, "unit": "单位"}] 注意识别图片中的价格标签。"""3. 提取地图标注
prompt="""这张图片是一张地图,上面标注了一些位置点。 请提取所有标注点的信息,返回 JSON 数组。 格式:[{"name": "地点名", "description": "描述(如果有)"}]"""4. 提取 PDF 扫描件
# 先把 PDF 转成图片,再用视觉模型提取frompdf2imageimportconvert_from_path images=convert_from_path("scanned_document.pdf")fori,imginenumerate(images):img.save(f"page_{i}.png")data=extract_with_vision_model(f"page_{i}.png",prompt)六、提高视觉模型准确率的技巧
1. 截图要清晰
# 放大页面再截图,提高清晰度page.run_js("document.body.style.zoom = '1.5'")time.sleep(1)page.get_screenshot(path="zoomed_screenshot.png")2. 只截需要的部分
# 不要全屏截图,只截目标区域# 复用上面的 screenshot_table() 函数,自动跳过隐藏表格screenshot_table(page,"target.png")3. Prompt 要具体
# 模糊的 prompt(不好)prompt="提取表格数据"# 具体的 prompt(好)prompt="""提取图片中的表格数据: - 第一列是"序号"(整数) - 第二列是"项目名称"(字符串) - 第三列是"金额"(数字,单位万元) - 第四列是"状态"(字符串) 返回 JSON 数组,只返回 JSON。"""4. 大图分段处理
defsplit_and_extract(image_path,rows_per_chunk=20):"""大表格分段截图提取"""fromPILimportImage img=Image.open(image_path)width,height=img.size# 估算每段高度(根据行高)row_height=40# 每行大约 40 像素chunk_height=rows_per_chunk*row_height+100# 加上表头all_data=[]forstart_yinrange(0,height,chunk_height-100):# 重叠100像素保证不丢行# 裁剪end_y=min(start_y+chunk_height,height)chunk=img.crop((0,start_y,width,end_y))chunk_path=f"chunk_{start_y}.png"chunk.save(chunk_path)# 提取data=extract_with_vision_model(chunk_path,prompt)all_data.extend(data)# 去重(重叠部分可能有重复数据)# ...returnall_data📊 本篇成本透明栏
| 项目 | 数值 |
|---|---|
| API 调用次数 | ~5-20 次(取决于页面数) |
| 消耗 Token | 约 5-20 万 tokens(图片 token 较多) |
| 成本 | ¥0(GLM-4V-Flash 免费额度内) |
视觉模型的图片 token 消耗比文本高,但免费额度一般够用。
练手改造题
改造 1(基础):打开一个包含复杂表格的网站(比如统计局数据页),用DrissionPage截图,然后用视觉模型提取表格数据。对比你手动看页面和 AI 提取的结果。
改造 2(进阶):找一个包含ECharts图表的网页,截图后用视觉模型提取图表的数据点。尝试还原出原始数据。
下期预告
Day 14 · 实战:数据采集全流程 Pipeline
前面学的东西要串起来了。Day 14 从零搭建一个完整的数据采集系统——目标:电商商品价格监控。包含分析页面、编写爬虫、数据存储、AI 清洗、输出报告,一条龙走完。
📚 资源与工具
- 智谱 GLM-4V-Flash(视觉模型):https://open.bigmodel.cn/
- DrissionPage 文档:https://g1879823.gitlab.io/DrissionPage/
- 本专栏配套代码:CSDN 下载区(每篇更新)
- 梅雅达编程笔记:专注 Python + AI 实战教程,提供数据采集与自动化定制服务
往期回顾
Day 01 · 爬虫能干啥不能干啥
Day 02 · 环境搭建与第一个爬虫
Day 03 · 列表页抓取:批量拿数据
Day 04 · 详情页 + 翻页:从一条到一百条
Day 05 · Ajax 请求拦截:抓看不到的数据
Day 06 · 浏览器自动化:DrissionPage 实战
Day 07 · Cookie 与 Session:处理登录状态
Day 08 · 反爬对策:Headers + 限速 + 代理
Day 09 · 存成文件:CSV / Excel / JSON
Day 10 · 存进数据库:SQLite 从零上手
Day 11 · 用 AI 替代正则:智能提取结构化数据
Day 12 · AI 数据清洗:脏数据一键变干净
专栏
「AI Python 系列」 第 03 栏 ·AI+Python 爬虫实战
「AI Python 系列」第 01 栏 ·AI+办公自动化
「AI Python 系列」第 02 栏 ·AI+数据可视化
「AI Python 系列」第 05 栏 · AI Agent实战
资源领取
- 关注作者获取本栏完整代码和数据集
原创声明:本文为梅雅达编程笔记原创作品,未经允许不得转载。