Kanna解析HTML时部分站点节点丢失,无法提取application/ld+json内容
问题根因
该问题是Kanna依赖的libxml2 HTML解析器的容错机制导致的:
- 当页面中前序
<script>标签的内容存在未转义的特殊字符、过长内容或不规范的换行格式时,libxml2会错误判定script标签提前闭合,打乱后续DOM树结构,最终导致目标节点的属性丢失、被解析为纯文本。 - 这种行为和页面的HTML规范符合度直接绑定,因此会出现大部分站点正常、少数站点异常的情况。
解决方案
方案1:从原始HTML直接正则匹配(推荐)
完全绕开DOM解析的容错问题,是目前提取application/ld+json数据最稳定的方案,适配所有站点:
func scrapeRecipe(from data: Data) { guard let html = String(data: data, encoding: .utf8) else { return } // 兼容type属性的大小写、多余空格、单双引号写法 let pattern = #"<script[^>]*type\s*=\s*['"]application/ld\+json['"][^>]*>(.*?)</script>"# guard let regex = try? NSRegularExpression( pattern: pattern, options: [.dotMatchesLineSeparators, .caseInsensitive] ) else { return } let fullRange = NSRange(html.startIndex..., in: html) regex.enumerateMatches(in: html, options: [], range: fullRange) { result, _, _ in guard let match = result, let contentRange = Range(match.range(at: 1), in: html) else { return } let jsonString = String(html[contentRange]) guard let jsonData = jsonString.data(using: .utf8), let json = try? JSONSerialization.jsonObject(with: jsonData) else { return } // 在此处处理提取到的食谱结构化数据 } }
- 优势:不受DOM解析错误影响,ld+json的格式约束非常明确,正则匹配的准确率远高于容错后的DOM查询。
方案2:调整libxml2解析配置
如果仍需使用DOM查询,可以关闭libxml2的自动容错修复:
let document = try HTML( html: html!, encoding: .utf8, options: [.htmlLoadNoError, .htmlLoadNoWarning, .htmlRecoverNone] )
- 注意:该配置下如果页面存在语法错误,会直接抛出解析异常,需要根据实际站点情况调整。
方案3:增加DOM查询兜底逻辑
如果仅为部分站点丢失type属性,可以增加内容特征匹配的兜底规则:
var scriptNodes = document.xpath("//script[@type='application/ld+json']//text()") // 原有规则查询不到时,遍历所有script节点匹配ld+json特征 if scriptNodes.isEmpty { let allScripts = document.xpath("//script//text()") scriptNodes = allScripts.filter { textNode in guard let text = textNode.text else { return false } let trimmedText = text.trimmingCharacters(in: .whitespacesAndNewlines) // ld+json的典型特征:以{开头,包含@context或@type字段 return trimmedText.hasPrefix("{") && (trimmedText.contains("\"@context\"") || trimmedText.contains("\"@type\"")) } }
注意事项
- 不要使用
document.text直接提取内容,该属性会拼接所有节点的纯文本,完全丢失结构信息。 - 正则匹配必须开启
.dotMatchesLineSeparators选项,否则跨多行的JSON内容无法被正确识别。
内容的提问来源于stack exchange,提问作者boywithaxe
相关产品推荐
相关产品推荐

