a1oa(如提示需要,请输入)
这个工具解决什么问题
上线一批新页面之后,老板问「SEO 都弄好了吧」,你只能说「应该没问题」——因为真去核对一遍的代价太大了。几十个页面,每个都要点开、按 F12、在 Elements 里翻到 head,看 title 有没有、多少字、description 是不是和隔壁页抄的一样、H1 到底有几个。一个页面两分钟,五十个页面就是整整一下午,而且人会累:看到第三十个的时候,你会开始只扫一眼就过,最后那几个页面到底查没查,自己都记不清。更麻烦的是有些问题肉眼根本看不出来——两个页面标题一模一样,你要两两对比才会发现;meta robots 里混进一个 noindex,页面上完全看不出任何异样,但搜索引擎从此不收这一页。还有一类最冤:改版时复制模板,canonical 忘了改,等于把整站的权重都指到了别人的页面上,而这个错误在浏览器里是隐形的。等这些问题被搜索引擎的数据反映出来,通常已经是几周以后,损失已经发生了。
- 原来的做法:逐个页面点开,按 F12 翻 head 看 title 和 description;标题重复要两两比对;noindex 和 canonical 只能靠人盯;五十个页面一下午,且越到后面越靠猜
- 用这个工具:把网址清单粘进窗口(或直接从 sitemap.xml 抓),点一次「开始体检」,程序并发抓取、跳过 robots.txt 禁止的地址、按同域名限速,十几秒后每个页面的标题/描述/结构/索引问题全部列清,按严重程度上色,还能一键导出 CSV 存档对照整改
- 省下来的:把一个下午的逐页人工核对压成一次点击;并且把人工最容易漏的三类问题单独拎了出来——跨页标题/描述重复、误加的 noindex、canonical 指向了别的页面。这三类在浏览器里都是隐形的,人盯不出来
技术实现
整个工具只用 Python 标准库,运行期不装任何依赖,打包后是单个 exe,双击即用。核心是四件事:① 抓取 —— urllib 读页面,只发 GET 读公开内容,不提交表单、不登录、不写任何数据;要不要遵守 robots.txt 由用户勾选,默认遵守。② 解析 —— 用标准库的 html.parser 而不是正则去啃 head,因为真实网页里标签的属性顺序、引号风格、是否自闭合、大小写、是否换行写,排列组合无穷,正则一定会漏,而 HTMLParser 按 HTML 规范切词,属性值还自带实体反转义。③ 判定 —— 全部规则集中在 check_page() 一个函数里,输入是一个 PageReport、输出是一串带严重程度的问题;界面层不参与任何判定,所以整套规则能脱离窗口跑自动化回归测试。④ 呈现 —— 结果按严重程度三档上色,点任意一行在下方给出该页的完整体检报告,可以导出带 UTF-8 BOM 的 CSV,用 Excel 双击打开中文不乱码。
- 为什么不用正则解析 HTML:一开始考虑过正则抠 <title>、<meta name="description" content="...">,很快放弃了。真实网页里同一个标签的写法至少有七八种变体:属性顺序随便排、单引号双引号混用、有的自闭合有的不闭合、name 有的写大写、content 的值里带 & 实体、标签横跨三行还夹注释。每漏一种就误报一批页面,而误报比不报更坏——用户会开始怀疑工具。改用标准库的 html.parser 后,切词与实体反转义都由标准库按规范保证,代码只需要处理「拿到属性字典之后怎么归类」。顺带解决了 </title> 忘记闭合的情况:靠 close() 时把缓冲区捞回来。
- 标题/描述长度按「显示宽度」算,不按字符数:搜索引擎是按像素宽度截断标题的,所以「30 个汉字」和「30 个字母」在结果页里占的位置差一倍。如果直接用 len() 判长度,一个 30 字的中文标题会被判合格(30 < 60),实际早就被截断了;反过来一个 50 字符的英文标题会被误判超长。display_width() 用 unicodedata.east_asian_width 把全角字符记 2、半角记 1、组合记号记 0,阈值定在标题 60 宽度、描述 160 宽度 —— 这正好对应搜索结果里约 30 个汉字或 60 个字母的展示上限。阈值都可以在界面上改。
- noindex 与 canonical:浏览器里隐形、代价最大的两类问题:noindex 有两个来源,缺一个就会漏判:页面里的 <meta name="robots" content="noindex">,以及 HTTP 响应头里的 X-Robots-Tag: noindex。后者在页面源码里完全看不到,只有看响应头才知道,而它一样会让页面不被收录。canonical 的判定用宽松比较:忽略 http/https 差异、忽略 www 前缀、忽略末尾斜杠,只有确实指向另一条路径时才提示 —— 否则带 www 的站点每一页都会被误报一遍。这两类问题在浏览器里都是隐形的,也正是人工核对最容易漏的部分。
- 跨页重复必须整批跑完再判,单页视角看不出来:「两个产品页用了同一套模板标题」是最常见也最伤的 SEO 问题:搜索引擎会认为其中一页是重复内容,两页互相抢排名。但这个结论单看任何一个页面都得不出来,必须等所有页面都抓完,把所有标题(去空白、转小写后)放进同一个字典分组,同一组里超过一页才报警。所以流水线是「先并发抓完 → 再统一去重 → 最后重算每页结论」,而不是边抓边下结论。抓取失败和 4xx 的页面不参与比较,否则一个 404 页面会和标题为空的页面组成一堆假重复。
- robots.txt 按 RFC 9309 匹配,不是「命中 Disallow 就拦」:最常见的简化写法是「路径命中任意 Disallow 就跳过」,这在真实站点上会大面积误拦:'Disallow: /' 配 'Allow: /public/' 是很常见的组合,简化写法会把公开目录整片跳掉,用户看到的是「我要体检的页面被跳过了」。正确规则是:命中多条时取**模式最长**的那条(最具体),长度完全相同时 **Allow 优先**;模式里的 * 匹配任意字符、只有出现在结尾的 $ 才有锚定含义、其余字符一律按字面量处理(所以要先 re.escape,否则 'Disallow: /a.php' 会连 '/aXphp' 一起拦)。取不到 robots.txt 时按放行处理 —— 不能因为「没问到」就拒抓用户明确要求体检的页面。
- 并发只跨域名并行,同域名强制排队:一次体检大多数是「同一个站的几十个页面」,全部并发打过去等于对人家发起一次小型压力测试,既不礼貌也容易被封 IP,结果就是后面几十条全部抓取失败。所以用 ThreadPoolExecutor 负责并发,用 HostPacer 负责礼貌:每个域名维护一个「下次可发车时间」,请求前先抢发车权,抢不到就等。跨域名照常并行(体检十几个不同站点仍然是并发速度),同域名被强制拉开间隔(默认 0.25 秒,可调)。等待切成 0.05 秒小片,这样点「停止」能立刻生效,而不是干等到当前批次全部返回。
- 中文域名会让整个并发线程崩掉:实测踩到的坑:http.client 在拼请求头时用 latin-1 编码主机名,地址里只要有一条中文域名,就会抛 UnicodeEncodeError。它是 ValueError 的子类,**不属于 OSError**,普通的网络异常分支接不住,会直接把这个 worker 带崩,进而让整批结果错位。现在分三道防线:请求前先把非 ASCII 主机名转成 punycode;fetch_page() 末尾挂一个宽泛的 except 把任何意外降级成「这一条失败」;worker 里还有第二层保护,因为 worker 抛异常会让 fut.result() 炸掉整批。单条烂地址绝不该拖垮整轮体检。
- 异常翻译成人话,而不是把堆栈甩给用户:「连接失败」这四个字对用户没有任何指导意义。_describe_error() 把异常翻译成结论:证书校验失败(并提示可以勾选「忽略证书错误」)、响应超时、域名解析不了、对方拒绝连接、连接被对方重置。这里的 isinstance 顺序**必须从具体到宽泛** —— ssl.SSLError、socket.timeout、gaierror 都是 OSError 的子类,先判 OSError 会把它们全部吞掉,用户只能看到一句笼统的失败,不知道该改什么。
技术栈:Python 3.8+、纯标准库(运行期零第三方依赖)、tkinter 图形界面、html.parser 解析 HTML 元信息、urllib + ssl 抓取页面、ThreadPoolExecutor 并发 + 同域名限速、遵守 robots.txt(RFC 9309 匹配规则)、PyInstaller 单文件打包(--noconsole)
已知局限:只体检「能被一次 GET 拿到 HTML」的页面,不执行 JavaScript,所以纯前端渲染(SPA)的站点拿不到真实内容,会被判成「抓到的 HTML 里没有标题」——这类站点建议配合服务端渲染或预渲染后再体检。不做站内递归爬取:网址来自你粘贴的清单或 sitemap.xml,程序不会顺着链接自己往下爬(这是刻意的,避免不可控的请求量)。不做关键词密度、外链分析、页面速度测量,这些各自是另一个工具的活。sitemap index 只往下多抓一层(最多 5 个子地图)。单次上限 500 个地址。总耗时取决于对方站点的响应速度与同域名间隔设置,视机器与网络而定,不是固定值。
操作教程
- 双击打开程序
解压后双击「网页SEO元信息批量体检.exe」,不需要安装 Python 或任何依赖。想先看效果可以点「载入演示站」,程序会在本机起一个演示站点并自动跑一轮。
预期结果:出现主窗口,自上而下是 ①工具条、②网址清单与体检结果左右分栏、③体检选项、④操作条与进度条 - 把要体检的网址放进左边
三种方式任选:直接把网址粘进左侧输入框(一行一个,一行挤好几个也会自动拆开,重复的自动去掉);点「从文件导入…」选一个 txt / csv 清单;或者点「抓取 sitemap.xml…」填站点地图地址,程序会把里面的地址全部读出来。
预期结果:右上角实时显示「已输入 N 条」;不认识的行走跳过并在状态栏说明原因,不会静默丢掉 - 看一眼体检选项(默认值可直接用)
默认勾选「遵守 robots.txt」,被 robots 禁止的地址会自动跳过而不是硬抓;「检查图片 alt」「检查分享标签(og)」按需要开关;「忽略证书错误」默认勾上,因为老站点证书过期太常见,不该因此判成打不开。超时默认 12 秒、并发 6、同域名间隔 0.25 秒。
预期结果:标题宽度上限显示 60、描述宽度上限显示 160;下面一行小字说明「一个汉字算 2、一个字母算 1」 - 点「开始体检」
程序先按域名各取一次 robots.txt 并跳过被禁止的地址,然后并发抓取,跨域名并行、同域名排队。每抓完一页,结果表里对应的那一行立刻从「等待中」变成真实结论,底部进度条同步推进。中途可以点「停止」。
预期结果:结果表按严重程度上色:红色=严重、橙色=警告、蓝灰=提示、绿色=完全通过;整行右侧直接写出主要问题 - 点一行看这一页的详细报告
选中任意一行,右下方的详情区会给出这一页的完整体检报告:最终地址、HTTP 状态与解码方式、标题与它的宽度、描述、canonical、robots 指令、页面语言、H1 列表与 H2 数量、图片总数与缺 alt 的数量,最后逐条列出问题并附上「建议」。表格顶部每一列都可以点击排序,按「严重」列排一次就能把最该改的页面排到最前面。
预期结果:详情区内容随选中行变化;体检完成后程序会自动选中第一个有严重问题的页面,不用手动找 - 导出 CSV 存档
点「导出 CSV…」选保存位置,导出的文件带 UTF-8 BOM,用 Excel 或 WPS 双击打开中文不乱码。整份表格包含每页的全部原始元信息与逐条问题明细,可以拿去做整改清单或者对比整改前后的效果。
预期结果:文件里第一行是表头,之后每行一个页面,最后一列是「问题明细」;导出后可以直接打开所在文件夹
常见问题
技术实现与后续维护
技术构成:Python 3.8+ 纯标准库,界面用 tkinter,抓取用 urllib + ssl,HTML 解析用 html.parser,sitemap 用 xml.etree,并发用 concurrent.futures,全部是标准库自带,没有版本冲突与依赖升级的负担。运行环境:Windows 10/11 64 位,打包成单文件 exe 后不需要安装 Python。维护要点:① 所有体检规则集中在 seo_core.check_page(),新增一条规则就是在那里加一个 _issue(...),界面会自动展示,不用改 UI;② 阈值都在 DEFAULT_OPTIONS 里,改默认值只动一处;③ 改完务必跑 tests/selftest.py,198 项断言覆盖了解析、规则、robots 匹配、编码、导出与端到端全流程,其中网络用例全部打本机演示服务,断网也能复现。④ 界面层的铁律:后台线程只能往队列里塞消息,绝对不能在非主线程里碰 tkinter 的任何对象(包括 after),否则会抛 main thread is not in main loop。
技术构成:
src/seo_core.py—— 核心逻辑层:URL 规范化、robots.txt 解析与匹配、sitemap 解析、HTML 元信息解析、全部体检规则、并发抓取与限速、CSV 导出。不 import 任何 GUI 库src/seo_audit_gui.py—— 图形界面层:窗口布局、后台线程与主线程之间的消息队列、结果表上色与排序、详情渲染、CSV 导出。只负责收集参数与展示,不做判定src/demo_server.py—— 本机演示站点(只监听 127.0.0.1):内置 12 类典型页面,覆盖正常、标题过长、跨页重复、缺 H1、多 H1、图片缺 alt、noindex、跳转、404、超时、robots 禁止、GBK 编码tests/selftest.py—— 自测脚本:198 项断言,含纯逻辑回归与对本机演示站的端到端体检DOCS/开发文档.md—— 开发文档:工程结构、技术实现要点、编译方式、扩展点与维护约定
运行环境:
本工程运行期零第三方依赖,直接用系统 Python 3.8+ 即可运行:
1) 跑界面:python src/seo_audit_gui.py
2) 跑自测:python tests/selftest.py
3) 打单文件 exe(在工程根目录执行):
pyinstaller --onefile --noconsole --name 网页SEO元信息批量体检 src/seo_audit_gui.py
注意 --noconsole 不能省,否则双击会先弹一个黑色控制台窗口。
工程根目录另附 run.bat(直接跑界面)与 requirements.txt(内容为空,声明无第三方依赖)。
可扩展方向:
- 增加更多检查项:hreflang 多语言标注、结构化数据(JSON-LD)是否存在且能解析、h2–h6 的层级是否跳级、页面字数与正文占比、是否有多个 canonical、title 与外链锚文本是否一致
- 支持与上一次体检结果对比:把导出的 CSV 读回来做差分,只列出「新出现的问题」和「已修复的问题」,适合放在上线流程里做回归
- 把体检范围扩到站内爬取:从首页或 sitemap 出发按链接巡站,配合并发上限与深度上限 —— 这属于另一个工具的量级,建议作为独立功能单独做
- 增加自定义规则文件:用简单的配置文件描述「哪些路径必须有什么标签」,方便多站点、多模板的团队统一口径
- 接入持续集成:做成命令行版本输出 JSON,在站点发布流程里自动跑一遍,有严重问题就让构建失败(现在的核心层已经是纯函数,加一层薄薄的 CLI 壳即可)
- 结果导出 HTML 报告:现在导出的是 CSV,可以再生成一份带颜色标记的单文件 HTML,方便直接发给同事或客户
本工具免费提供使用,但不对外分发源码。如果你需要在本工具基础上做功能调整、对接内部系统或长期维护,可以联系我们做二次开发,源码与工程文档可在定制合作中一并交付。
需要其它功能?可以定制
这个工具只做一件事:网站改版或批量上新之后,一堆页面的标题空着、描述互相重复、H1 忘了写、图片没配 alt,人工点开一个个看根本查不完。如果你的场景还需要别的处理,可以联系我们做二次开发,按你的实际数据格式和流程定制。
常见的定制需求:
- 按你的站点模板定制专属检查项(例如必须包含某个备案号、必须包含指定的品牌词)
- 对接你的内容管理系统,发稿前自动体检一遍并拦截不合格的稿件
- 把体检结果直接推送到企业微信 / 钉钉群,每周一早上自动跑一遍全站
- 集成到已有的建站服务后台,作为交付前的一道自检流程
- 联系方式:邮箱/微信见站内「联系我们」页面,或在本文下方留言
- 定制说明:请附上你的数据样例(脱敏即可)和期望结果,方便快速评估工作量
- 交付形式:定制项目可交付可执行程序,按需一并交付源码与工程文档,支持长期维护
使用说明与免责声明
- 本工具仅用于采集公开可见或你自身拥有权限的数据。
- 请遵守目标网站的服务条款、robots 协议及《网络安全法》等相关法律法规。
- 请合理控制请求频率,不要对目标站点造成访问压力。
- 不得用于采集个人隐私、付费墙内容或任何受法律保护的非公开数据。
- 因使用不当造成的后果由使用者自行承担;本工具不做数据二次分发。
a1oa(如提示需要,请输入)
