Swift解析Google网页报错:无法确定文本编码的解决办法
问题解决:加载Google域名网页时编码检测失败的报错
问题场景
使用Swift代码解析网页内容时,访问普通网站(如https://hello.com)正常,但访问Google相关域名(如https://google.com)时,抛出错误:The file couldn’t be opened because the text encoding of the contents couldn’t be determined。原代码使用String(contentsOf:)同步加载网页内容,结合SwiftSoup解析,再通过自定义扩展将HTML转为字符串。
问题原因
- 编码不匹配:Google返回的网页内容编码可能并非UTF-8,
String(contentsOf:)自动检测编码时失败,导致无法正确转换为字符串。 - 同步请求风险:
String(contentsOf:)是同步网络请求,不仅会阻塞主线程,且对编码的自动处理能力有限,无法应对复杂的响应编码情况。 - 强制try!掩盖错误:原代码大量使用
try!,会直接崩溃而无法捕获和处理编码相关的异常。
解决方案
1. 改用URLSession异步请求,按响应头指定编码解析内容
通过URLSession发起异步请求,从响应头的Content-Type字段提取编码格式,再用对应编码将Data转为字符串,避免自动检测编码失败的问题。
2. 替换强制try!为do-catch错误处理
合理捕获解析过程中的异常,避免程序直接崩溃,同时便于排查问题。
3. 可选:直接用SwiftSoup提取文本,跳过HTML转字符串步骤
如果只是需要网页文本内容,可直接通过SwiftSoup的text()方法提取,无需再通过NSAttributedString转换,效率更高。
修改后的代码示例
import UIKit import SwiftSoup class ViewController: UIViewController { @IBOutlet weak var ingredientsLabel: UILabel! // 修正变量名拼写 override func viewDidLoad() { super.viewDidLoad() fetchAndParseWebContent() } private func fetchAndParseWebContent() { guard let url = URL(string: "https://google.com") else { ingredientsLabel.text = "无效URL" return } let task = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in guard let self = self else { return } // 处理网络错误 if let error = error { DispatchQueue.main.async { self.ingredientsLabel.text = "请求失败:\(error.localizedDescription)" } return } guard let data = data, let response = response as? HTTPURLResponse else { DispatchQueue.main.async { self.ingredientsLabel.text = "无有效响应数据" } return } // 从响应头获取编码格式 var encoding: String.Encoding = .utf8 if let contentType = response.allHeaderFields["Content-Type"] as? String { let charsetRegex = try! NSRegularExpression(pattern: "charset=([^;]+)", options: .caseInsensitive) if let match = charsetRegex.firstMatch(in: contentType, range: NSRange(contentType.startIndex..., in: contentType)) { let charsetRange = Range(match.range(at: 1), in: contentType)! let charset = String(contentType[charsetRange]) encoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding(charset as CFString))) } } // 按指定编码转换为字符串 guard let content = String(data: data, encoding: encoding) else { DispatchQueue.main.async { self.ingredientsLabel.text = "编码解析失败" } return } // 使用SwiftSoup解析内容 do { let doc = try SwiftSoup.parse(content) guard let body = doc.select("body").first() else { DispatchQueue.main.async { self.ingredientsLabel.text = "未找到body内容" } return } // 方案1:直接用SwiftSoup提取文本 let textContent = try body.text() DispatchQueue.main.async { self.ingredientsLabel.text = textContent } // 方案2:如果仍需要HTML转字符串,使用原扩展(可选) /* let htmlContent = try body.html() let convertedText = htmlContent.HtmlToString ?? "HTML转换失败" DispatchQueue.main.async { self.ingredientsLabel.text = convertedText } */ } catch { DispatchQueue.main.async { self.ingredientsLabel.text = "解析失败:\(error.localizedDescription)" } } } task.resume() } } extension String { var HtmlToString: String? { guard let data = data(using: .utf8) else { return nil } do { return try NSAttributedString(data: data, options: [ .documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue ], documentAttributes: nil).string } catch { print(error.localizedDescription) return nil } } }
关键修改点说明
- 异步请求:用URLSession替代
String(contentsOf:),避免主线程阻塞,同时能获取响应头信息。 - 编码处理:从响应头的Content-Type中提取charset,转换为对应的String.Encoding,确保字符串转换准确。
- 错误处理:新增网络错误、数据为空、解析失败等场景的处理,避免程序崩溃。
- 变量名修正:将
Ingrediants改为ingredientsLabel,符合Swift命名规范。
内容的提问来源于stack exchange,提问作者Aakarsh Tathachar
相关产品推荐
相关产品推荐

