用HTMLParser状态机扫描<head>最轻量,仅响应title/meta标签,跳过<body>提速3–5倍;dataset不能读meta/title值,须用getAttribute();JSON解析报错多因误将HTML当JSON,需先验content-type;编码、大小写、连字符不规范是乱码主因。

直接用 HTMLParser 扫描 <head> 区域最轻量
加载完整 DOM(比如用 BeautifulSoup 或浏览器 document)对只读元数据是过度的,内存和启动开销都大。真正需要的只是识别 <title>、<meta name="keywords"> 这类标签——用 Python 标准库的 html.parser.HTMLParser 做状态机式 Token 扫描,跳过整个 <body>,速度提升 3–5 倍。
关键点:
- 只在
self.in_head == True时响应handle_starttag和handle_data -
<title>的内容在handle_data()中捕获,但必须确保当前self.current_tag == 'title' -
<meta>标签不闭合,所以只靠handle_starttag解析attrs,不等handle_endtag - 遇到
</head>立即设self.in_head = False,后续标签全部忽略
dataset 不能用来读取 <meta> 或 <title> 的值
dataset 是 DOM API 里专为 data-* 属性设计的映射,它对 <meta name="description"> 这类标准 meta 标签完全无效。试图写 document.querySelector('meta[name="description"]').dataset.content 会返回 undefined —— 因为 content 不是 data- 属性,而是普通 HTML 属性。
正确做法:
立即学习“前端免费学习笔记(深入)”;
- 读
<title>:用document.title(最简),或document.querySelector('title').textContent - 读
<meta>:必须用.getAttribute('content'),不是.dataset.content - 读
charset或viewport:document.querySelector('meta[charset]')?.getAttribute('charset'),注意空值判断
遇到 Unexpected token N in JSON at position 0 别急着改 JSON
这个错误 90% 不是 JSON 文件问题,而是解析器实际拿到的是 HTML(比如 404 页面、重定向响应、BOM 头),却强行调了 .json()。元数据提取也一样:如果你用 fetch() 加载一个 HTML 文件再手动解析,而没检查 response.headers.get('content-type'),就可能把整个 HTML 字符串当 XML/JSON 解析。
排查顺序:
- 先打印
response.text(),确认返回体是不是纯 HTML 源码 - 检查是否误把
file://路径传给 fetch —— 浏览器会拒绝,返回空响应或 error 页面 - 如果用服务端解析(如 Python),确认 HTTP 响应状态码是 200,且
Content-Type包含text/html - 避免用正则从 HTML 字符串里硬挖
<title>(.*?)</title>,遇到换行、注释、CDATA 就崩
中文乱码、大小写混用、连字符缺失是元数据提取失败主因
<meta charset="utf8"> 缺少横杠、写成 UTF-8 大写、或漏掉 charset=,都会让解析器无法识别编码,后续所有文本(包括 title)变成乱码。更隐蔽的是 name 属性大小写:有些页面写 <meta NAME="keywords">,而 querySelector('meta[name="keywords"]') 在多数浏览器里不匹配(CSS 选择器属性名区分大小写)。
安全写法:
- 用
document.querySelectorAll('meta').forEach(meta => { if (meta.getAttribute('name')?.toLowerCase() === 'description') {...} }) - 读取
<title>后立即做.trim(),防空格和 BOM 干扰 - 所有
data-属性命名必须全小写+连字符,如data-page-id,否则dataset.pageId读不到 - 服务端解析时,优先用
lxml而非BeautifulSoup(html.parser),前者对编码声明更鲁棒
提取元数据不是“找几个标签”那么简单,真正卡住人的永远是编码声明错位、大小写陷阱、以及把 HTML 当纯文本暴力处理。别省那几行容错逻辑——加个 try/except、多一次 toLowerCase()、检查下 response.ok,比事后调试两小时强。



















