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

Swift 5使用SwiftSoup解析URLSession请求HTML与实际页面内容不符如何解决

问题原因分析
  • 客户端动态渲染:绝大多数现代网站依赖JavaScript动态加载、渲染内容,URLSession仅能获取服务器返回的初始静态HTML,不会执行页面JS脚本,自然无法拿到JS生成的元素内容,这是最常见的不一致原因。
  • 请求特征不匹配:你仅设置了User-Agent,浏览器实际发送的请求还包含Accept、Accept-Language、Cookie等多个头字段,部分站点会校验请求头完整性,缺少对应字段会返回不同的页面内容。
  • 编码解析错误:你强制使用UTF-8编码解析返回数据,若目标站点使用GBK、GB2312等其他编码,会导致解析乱码,后续提取内容自然错位。
  • 反爬策略拦截:部分站点会校验请求来源、会话状态、访问频率,识别到非浏览器请求会返回拦截页、空白页或者假数据。
对应解决方案

方案1:优先排查静态页面适配(适合无动态渲染的站点)

先完善请求配置,适配静态站点的校验规则:

import SwiftSoup

guard let link = "你的目标链接", let url = URL(string: link) else {
    print("链接非法")
    return
}

let config = URLSessionConfiguration.default
// 补全全量浏览器请求头,和你本地浏览器的请求头保持一致
config.httpAdditionalHeaders = [
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
    "Accept-Encoding": "gzip, deflate, br",
    "Connection": "keep-alive",
    "Upgrade-Insecure-Requests": "1"
]
// 自动处理重定向和Cookie
config.httpShouldSetCookies = true
config.httpCookieAcceptPolicy = .always

let session = URLSession(configuration: config)

let task = session.dataTask(with: url) { data, response, error in
    guard let data = data, error == nil, let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
        print("请求失败:\(error?.localizedDescription ?? "未知错误")")
        return
    }
    // 自动识别响应编码,不要硬写UTF-8
    let encodingName = httpResponse.textEncodingName ?? "utf-8"
    let encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding(encodingName as CFString))
    guard let htmlContent = String(data: data, encoding: String.Encoding(rawValue: encoding)) else {
        print("编码解析失败")
        return
    }
    do {
        let doc = try SwiftSoup.parse(htmlContent)
        let elements = try doc.getAllElements().array()
        // 后续元素提取逻辑
    } catch {
        print("解析错误:\(error.localizedDescription)")
    }
}
task.resume()

方案2:使用WKWebView渲染页面(适合JS动态渲染的站点)

如果调整请求头后还是拿不到正确内容,说明站点是动态渲染的,直接用WebKit框架的WKWebView加载页面,完全模拟浏览器行为,等页面加载完成后再提取完整DOM即可,不需要更换解析库,拿到HTML后还是可以用SwiftSoup解析:

import WebKit
import SwiftSoup

class WebScraper: NSObject, WKNavigationDelegate {
    private var webView: WKWebView!
    private var completion: ((String?) -> Void)?
    
    override init() {
        super.init()
        let config = WKWebViewConfiguration()
        // 禁用缓存保证每次拿到最新内容
        config.websiteDataStore = .nonPersistent()
        webView = WKWebView(frame: .zero, configuration: config)
        webView.navigationDelegate = self
        // 和浏览器UA保持一致
        webView.customUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"
    }
    
    func loadUrl(_ urlString: String, completion: @escaping (String?) -> Void) {
        guard let url = URL(string: urlString) else {
            completion(nil)
            return
        }
        self.completion = completion
        webView.load(URLRequest(url: url))
    }
    
    // 页面加载完成回调
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        // 可选:延迟1-2秒再拿内容,等待页面异步JS请求完成
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
            // 执行JS获取完整HTML
            webView.evaluateJavaScript("document.documentElement.outerHTML.toString()") { [weak self] result, error in
                guard let html = result as? String, error == nil else {
                    self?.completion?(nil)
                    return
                }
                self?.completion?(html)
                // 拿到HTML后可以直接用SwiftSoup解析
                do {
                    let doc = try SwiftSoup.parse(html)
                    // 后续元素提取逻辑
                } catch {
                    print("解析错误:\(error.localizedDescription)")
                }
            }
        }
    }
    
    func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
        completion?(nil)
    }
}

// 使用示例
let scraper = WebScraper()
scraper.loadUrl("你的目标链接") { html in
    if let html = html {
        print("拿到完整渲染后的HTML:\(html)")
    } else {
        print("页面加载失败")
    }
}

方案3:直接调用站点接口(最优方案)

如果可以的话,用浏览器开发者工具的网络面板,筛选XHR/ Fetch请求,找到目标内容对应的后端接口,直接请求接口获取JSON数据,比解析HTML稳定性高、速度快,也不会受页面渲染逻辑变更的影响。

注意事项
  • 爬取内容请遵守目标站点的robots.txt规则与用户协议,避免非法爬取带来的法律风险
  • 控制请求频率,避免高频请求触发站点反爬策略,导致IP被封禁

内容的提问来源于stack exchange,提问作者aadi sach

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 08:54:04