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

iOS中如何从WebKit控件获取JavaScript执行后的完整HTML源码

在iOS的WebKit控件中获取JS执行后的完整HTML源码

嘿,我来帮你搞定这个问题~要拿到JS执行后的完整HTML,核心是得等所有动态脚本都跑完再去抓取DOM,下面给你详细的实现思路和代码示例:

核心思路

WebKit的WKWebView触发webViewDidFinishNavigation回调只是页面初始资源加载完毕,但异步JS可能还在执行。所以我们需要精准捕捉JS执行完成的时机,再注入JS去获取完整的DOM源码。

具体实现方案

方案一:可控网页(能修改网页JS)——最可靠的方式

如果是你自己开发的网页,可以在JS执行完后主动通知原生代码,这样能100%保证时机正确:

  1. 配置WKWebView,添加消息处理器
    让WKWebView能接收网页发来的通知,告诉我们JS已经执行完毕。

  2. 网页JS执行完后发送通知
    在网页的异步JS逻辑结束后,调用window.webkit.messageHandlers给原生发消息。

  3. 原生接收通知后抓取完整HTML
    收到网页的通知后,调用evaluateJavaScript执行document.documentElement.outerHTML获取完整源码。

Swift代码示例

import WebKit

class WebViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {
    private var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()
        setupWebView()
        loadTargetPage()
    }

    private func setupWebView() {
        // 配置WebView,注册消息处理器
        let userContentController = WKUserContentController()
        // 注册名为"htmlReady"的消息处理器,网页会通过这个名字发通知
        userContentController.add(self, name: "htmlReady")
        
        let config = WKWebViewConfiguration()
        config.userContentController = userContentController
        
        webView = WKWebView(frame: view.bounds, configuration: config)
        webView.navigationDelegate = self
        view.addSubview(webView)
    }

    private func loadTargetPage() {
        // 示例HTML,模拟异步创建JSON script标签
        let testHtml = """
        <html>
        <head>
            <title>Test Page</title>
            <script>
                // 模拟异步JS操作,1秒后创建JSON script标签
                setTimeout(() => {
                    const jsonScript = document.createElement('script');
                    jsonScript.type = 'application/json';
                    jsonScript.textContent = '{"data": "dynamic-content"}';
                    document.body.appendChild(jsonScript);
                    
                    // 通知原生:JS执行完成,可以抓HTML了
                    window.webkit.messageHandlers.htmlReady.postMessage("done");
                }, 1000);
            </script>
        </head>
        <body>
            <span>Hello my html sample page</span>
        </body>
        </html>
        """
        webView.loadHTMLString(testHtml, baseURL: nil)
    }

    // 接收网页发来的消息
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        guard message.name == "htmlReady" else { return }
        
        // 执行JS获取完整HTML
        webView.evaluateJavaScript("document.documentElement.outerHTML") { result, error in
            if let fullHtml = result as? String {
                print("✅ 获取到完整HTML:\(fullHtml)")
                // 这里可以处理拿到的HTML,比如保存、解析等
            } else if let err = error {
                print("❌ 获取HTML失败:\(err.localizedDescription)")
            }
        }
    }

    deinit {
        // 记得移除消息处理器,避免内存泄漏
        webView.configuration.userContentController.removeScriptMessageHandler(forName: "htmlReady")
    }
}

方案二:第三方网页(无法修改网页JS)——轮询检查目标元素

如果是第三方网页,没法修改JS,那我们可以通过轮询检查目标元素是否存在来判断JS是否执行完成:

  1. 在webViewDidFinishNavigation回调后,开始轮询
  2. 每次轮询时执行JS检查目标元素(比如你例子里的JSON类型script标签)是否存在
  3. 一旦检测到目标元素,就抓取完整HTML

Swift代码示例(轮询部分)

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    // 开始轮询检查目标元素
    checkForDynamicElement()
}

private func checkForDynamicElement() {
    // 执行JS检查JSON script标签是否存在
    webView.evaluateJavaScript("document.querySelector('script[type=\"application/json\"]') !== null") { [weak self] result, error in
        guard let self = self else { return }
        
        if let elementExists = result as? Bool, elementExists {
            // 目标元素已存在,抓取完整HTML
            self.webView.evaluateJavaScript("document.documentElement.outerHTML") { htmlResult, htmlError in
                if let fullHtml = htmlResult as? String {
                    print("✅ 获取到完整HTML:\(fullHtml)")
                }
            }
        } else {
            // 目标元素还没出现,1秒后继续轮询
            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                self.checkForDynamicElement()
            }
        }
    }
}

关键注意点

  • 一定要用document.documentElement.outerHTML,而不是document.body.innerHTML,这样才能拿到整个HTML文档的完整源码,包括head部分。
  • 轮询的间隔可以根据网页实际情况调整,避免太频繁影响性能。
  • 如果网页的JS执行时间不确定,也可以设置一个最大轮询次数,防止无限循环。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:15:28