You need to enable JavaScript to run this app. Kanna解析HTML时部分站点节点丢失,无法提取application/ld+json内容
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.26 22:06:02