You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中用URLSession读取YouTube HTML出现十六进制字符,无法解析JSON

解决YouTube网页HTML中JSON转义字符解析问题

问题概述

使用Swift的URLSession请求YouTube网页后,提取<script>标签里的ytInitialData时,发现原本的JSON内容被转换成了十六进制转义字符(如\x7b\x22responseContext\x22),无法直接解析成JSON对象。

解决方案

需要先将这些十六进制转义字符还原为正常的UTF-8字符串,再进行JSON解析。以下是具体实现步骤:


1. 提取包含转义字符的ytInitialData字符串

从HTML中匹配var ytInitialData = '到';之间的内容:

// 假设已获取到htmlString
guard let regex = try? NSRegularExpression(pattern: #"var ytInitialData = '(.*?)';?"#, options: .dotMatchesLineSeparators) else {
    return nil
}

let matches = regex.matches(in: htmlString, range: NSRange(htmlString.startIndex..., in: htmlString))
guard let match = matches.first, let range = Range(match.range(at: 1), in: htmlString) else {
    return nil
}

let escapedJsonString = String(htmlString[range])

2. 解析十六进制转义字符为正常字符串

编写函数将\xXX格式的转义字符转换为对应UTF-8字符:

func unescapeHexString(_ input: String) -> String {
    var result = ""
    var index = input.startIndex
    
    while index < input.endIndex {
        if input[index] == "\\" && index != input.index(before: input.endIndex) && input[input.index(after: index)] == "x" {
            // 提取\x后的两位十六进制字符
            let hexStart = input.index(index, offsetBy: 2)
            guard hexStart < input.endIndex, let hexEnd = input.index(hexStart, offsetBy: 2, limitedBy: input.endIndex) else {
                result.append(input[index])
                index = input.index(after: index)
                continue
            }
            let hexStr = String(input[hexStart..<hexEnd])
            if let hexValue = UInt8(hexStr, radix: 16) {
                result.append(Character(UnicodeScalar(hexValue)))
            } else {
                // 解析失败则保留原字符
                result.append("\(input[index])\(input[input.index(after: index)])\(hexStr)")
            }
            index = hexEnd
        } else {
            result.append(input[index])
            index = input.index(after: index)
        }
    }
    return result
}

// 转换转义字符串
let rawJsonString = unescapeHexString(escapedJsonString)

3. 解析JSON为对象

将还原后的字符串解析为JSON对象:

do {
    let jsonData = rawJsonString.data(using: .utf8)!
    let ytInitialData = try JSONSerialization.jsonObject(with: jsonData, options: [])
    // 处理解析后的JSON数据
    print(ytInitialData)
} catch {
    print("JSON解析失败: \(error.localizedDescription)")
}

完整整合代码

将上述步骤整合到原有请求逻辑中:

func fetchYouTubeData(url: URL) async -> Any? {
    var request = URLRequest(url: url)
    guard let (data, response) = try? await URLSession.shared.data(from: url) else {
        return nil
    }
    
    guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
        return nil
    }

    guard let htmlString = String(data: data, encoding: .utf8) else {
        return nil
    }
    
    // 提取转义的JSON字符串
    guard let regex = try? NSRegularExpression(pattern: #"var ytInitialData = '(.*?)';?"#, options: .dotMatchesLineSeparators) else {
        return nil
    }
    let matches = regex.matches(in: htmlString, range: NSRange(htmlString.startIndex..., in: htmlString))
    guard let match = matches.first, let range = Range(match.range(at: 1), in: htmlString) else {
        return nil
    }
    let escapedJsonString = String(htmlString[range])
    
    // 还原转义字符
    func unescapeHexString(_ input: String) -> String {
        var result = ""
        var index = input.startIndex
        while index < input.endIndex {
            if input[index] == "\\" && index != input.index(before: input.endIndex) && input[input.index(after: index)] == "x" {
                let hexStart = input.index(index, offsetBy: 2)
                guard hexStart < input.endIndex, let hexEnd = input.index(hexStart, offsetBy: 2, limitedBy: input.endIndex) else {
                    result.append(input[index])
                    index = input.index(after: index)
                    continue
                }
                let hexStr = String(input[hexStart..<hexEnd])
                if let hexValue = UInt8(hexStr, radix: 16) {
                    result.append(Character(UnicodeScalar(hexValue)))
                } else {
                    result.append("\(input[index])\(input[input.index(after: index)])\(hexStr)")
                }
                index = hexEnd
            } else {
                result.append(input[index])
                index = input.index(after: index)
            }
        }
        return result
    }
    let rawJsonString = unescapeHexString(escapedJsonString)
    
    // 解析JSON
    do {
        let jsonData = rawJsonString.data(using: .utf8)!
        return try JSONSerialization.jsonObject(with: jsonData, options: [])
    } catch {
        print("JSON解析失败: \(error)")
        return nil
    }
}

说明

  • 正则表达式需根据实际返回的HTML结构微调,确保精准匹配ytInitialData内容
  • 转义字符处理函数会遍历字符串,逐个解析\xXX格式的转义序列
  • 解析JSON时可替换为Codable模型,更便捷地访问数据

内容的提问来源于stack exchange,提问作者sumit kumar Pradhan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 07:53:15