Python批量检测本地HTML死链需用pathlib补全相对路径再HEAD验证,跳过伪协议链接;200不等于有效(需查Content-Type和响应体),404不等于真死(可补index.html或后缀);CI中应扫描构建产物目录并设超时重试。

用 Python + requests.head() 批量检测本地 HTML 中的 href 链接
本地静态站点的死链问题,不能靠在线工具——它们只爬线上 URL,而你改完代码还没部署,死链已经埋好了。最直接的办法是写个脚本,读取所有 .html 文件,提取 <a href="..."> 和 <link href="...">,再对每个链接发 HEAD 请求验证状态码。
关键点不在“能不能发请求”,而在“怎么构造有效请求地址”:相对路径必须补全为绝对路径,否则 requests.head() 会直接报错或误判。常见错误是直接拿 href 值去请求,比如 ./about.html 或 ../css/style.css,不结合当前 HTML 文件所在目录就发出去,结果全是 ConnectionError。
- 用
pathlib.Path解析 HTML 文件路径,再用.resolve().parent得到其真实父目录 - 对每个
href,调用pathlib.Path(file_dir).joinpath(href).resolve()拼出本地文件路径(注意处理..和.) - 仅当路径存在且是文件时,才认为链接有效;若不存在,再尝试加
.html后缀补全(很多静态站省略后缀) - 跳过以
#、javascript:、mailto:开头的伪链接,它们本就不该被HEAD请求
为什么不能只依赖 HTTP 状态码判断死链
200 OK 不等于链接可用,404 Not Found 也不一定就是真死链——这是本地检测最容易踩的坑。
比如一个 index.html 里写了 <a href="data.json">,本地文件系统里它确实存在,HEAD 返回 200,但页面运行时 JS 加载失败(MIME 类型不对、CORS 限制),实际仍是“内容死链”。反过来,某些构建工具(如 Docusaurus、Hugo)生成的页面会把 /blog/ 当作目录,实际对应 /blog/index.html,但没配服务器重定向时,直接请求 /blog/ 会返回 404,而请求 /blog/index.html 才是 200。
立即学习“前端免费学习笔记(深入)”;
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 对以斜杠结尾的路径(如
/docs/),自动补index.html再试一次 - 对 JSON、JS、CSS 等资源,检查响应头中的
Content-Type是否匹配预期(如application/json) - 对 HTML 链接,额外读取响应体前 1KB,确认是否含有效 HTML 标签(避免返回 200 却是 Nginx 默认 404 页面)
自动清理:改 HTML 还是加重定向,取决于链接类型
检测出死链后,不能一概用正则替换删掉 <a> 标签——这会破坏语义和可访问性。处理方式必须按链接来源分类:
- 内部相对链接(如
./old-page.html):优先查 Git 历史,看目标文件是否只是改名或移位;若是,用git mv记录重命名,再批量更新href值 - 内部绝对路径(如
/assets/img/logo.png):检查文件系统是否存在,不存在则从构建产物中恢复,或标记为待删除资源 - 外部链接(如
https://example.com/old-api):不自动修改,只写入broken-external.log,人工确认是否要替换成存档链接(Wayback Machine)或移除 - 锚点链接(如
#section-2):提取对应 HTML 文件,用BeautifulSoup查找是否有id="section-2"或name="section-2",没有则删href属性,保留文本内容
CI/CD 流程中嵌入死链检查的实操要点
把死链检测塞进 GitHub Actions 或 GitLab CI,不是加一行 python check_links.py 就完事。它得快、得稳、还得能准确定位问题源头。
常见失败是超时或权限问题:CI 环境默认无网络访问(尤其私有 GitLab Runner),或构建产物还没生成就去扫描 dist/ 目录。更隐蔽的问题是并发请求触发本地服务器限流(比如用 live-server 启服务后再扫,但没等服务完全 ready 就发请求)。
- 在
build步骤后、deploy步骤前插入检查,确保扫描的是最终产物目录(如dist/或public/) - 设置
timeout=3和max_retries=1,避免单个卡住拖慢整个流程;对 5xx 错误直接跳过,不计入失败 - 输出格式用
--json,让 CI 能解析并高亮显示具体哪行 HTML 的哪个href失效(配合grep -n定位) - 允许临时豁免:通过
ignore_patterns.txt列出已知待修复的外部链接,避免每次 PR 都失败
真正麻烦的从来不是发现死链,而是区分哪些该修、哪些该删、哪些该加 redirect —— 这需要结合 Git 历史、路由配置、SEO 权重流向一起看,自动化只能做到定位和分类,决策还得人来拍板。


















