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

使用Swiftsoup解析网页:如何提取正文并加载到UIViewController?

用SwiftSoup提取网页正文(含Medium专属方案)

别担心,不懂CSS和JS完全不影响你实现类似Instapaper的网页正文提取!我给你整理了几个实用的方法,从通用方案到针对Medium的专属技巧,一步步来:

一、通用正文提取方案(适配大多数网站)

这个方法不需要依赖特定网站的结构,通过筛选长段落来过滤掉导航、广告等无关内容——核心思路是:正文段落通常比侧边栏、按钮上的文字长很多。

代码实现

import SwiftSoup

func extractArticleBody(from html: String) -> String {
    do {
        let doc = try SwiftSoup.parse(html)
        
        // 筛选所有<p>标签,只保留文字长度超过50的段落
        let allParagraphs = try doc.select("p")
        var bodyText = ""
        for paragraph in allParagraphs {
            let text = try paragraph.text()
            if text.count > 50 {
                bodyText += text + "\n\n" // 段落之间加换行,提升可读性
            }
        }
        
        // 如果筛选出了有效内容,直接返回
        if !bodyText.isEmpty {
            return bodyText
        }
        
        // 备选方案:找文本密度最高的容器(正文容器通常文本多、子标签少)
        let candidateContainers = try doc.select("div, article, section")
        var bestContainer: Element?
        var maxTextDensity = 0.0
        
        for container in candidateContainers {
            let textLength = try container.text().count
            let childTagCount = container.children().count
            
            // 跳过没有子标签的容器
            guard childTagCount > 0 else { continue }
            
            // 计算文本密度:文本长度 / 子标签数量,密度越高越可能是正文
            let density = Double(textLength) / Double(childTagCount)
            if density > maxTextDensity {
                maxTextDensity = density
                bestContainer = container
            }
        }
        
        return bestContainer?.text() ?? "无法提取正文内容"
        
    } catch {
        print("解析出错:\(error.localizedDescription)")
        return "无法提取正文内容"
    }
}

二、Medium专属提取方案(更精准)

Medium的正文有固定的容器类名,即使不懂CSS,直接用这个类名就能定位到正文区域,提取结果更干净:

修改代码中的专属逻辑

在上面的extractArticleBody函数里,加入Medium的专属判断:

// 在通用筛选之后加入这段
if let mediumContentContainer = try doc.select(".post-content").first() {
    // 提取纯文本
    return try mediumContentContainer.text()
    // 如果要保留富文本格式(比如加粗、图片),可以返回HTML:
    // return try mediumContentContainer.html()
}

三、将提取的正文加载到另一个UIViewController

假设你有一个ArticleDetailViewController用来展示正文,传递和展示的方法如下:

1. 准备目标ViewController

class ArticleDetailViewController: UIViewController {
    // 用来接收纯文本正文
    var articleBodyText: String = ""
    // 如果要展示富文本,就用这个接收HTML内容
    // var articleBodyHtml: String = ""
    
    private let textView = UITextView()
    // 如果用富文本,就用WKWebView:
    // private let webView = WKWebView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        loadContent()
    }
    
    private func setupUI() {
        view.backgroundColor = .systemBackground
        
        // 配置UITextView(纯文本展示)
        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.isEditable = false
        textView.font = UIFont.systemFont(ofSize: 16, weight: .regular)
        textView.textColor = .label
        textView.contentInset = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
        view.addSubview(textView)
        
        NSLayoutConstraint.activate([
            textView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            textView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            textView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
        ])
        
        // 如果用WKWebView展示富文本,替换上面的代码:
        /*
        webView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(webView)
        NSLayoutConstraint.activate([
            webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
        ])
        */
    }
    
    private func loadContent() {
        // 纯文本展示
        textView.text = articleBodyText
        
        // 富文本展示(需要先把HTML包装成完整的网页结构)
        /*
        let styledHtml = """
        <html>
        <head>
            <style>
                body { 
                    font-size: 16px; 
                    line-height: 1.8; 
                    padding: 20px; 
                    color: #333;
                }
                img { max-width: 100%; height: auto; margin: 16px 0; }
                h1, h2 { margin-top: 24px; margin-bottom: 16px; }
            </style>
        </head>
        <body>\(articleBodyHtml)</body>
        </html>
        """
        webView.load(styledHtml.data(using: .utf8)!, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: URL(string: "https://medium.com")!)
        */
    }
}

2. 在当前ViewController中跳转并传递数据

// 假设你已经获取到网页的HTML字符串(比如通过网络请求)
let htmlString = "你的网页HTML内容"
let bodyText = extractArticleBody(from: htmlString)

// 创建目标VC并传递数据
let detailVC = ArticleDetailViewController()
detailVC.articleBodyText = bodyText
// 如果是富文本:detailVC.articleBodyHtml = bodyHtml

// 跳转(如果用导航控制器)
navigationController?.pushViewController(detailVC, animated: true)
// 如果是模态跳转:
// present(detailVC, animated: true)

小提示

  • 如果某些网站通用方案效果不好,可以用Safari的“检查元素”功能(右键点击正文→检查),找到正文所在容器的类名或标签,然后用doc.select("类名/标签")直接定位。
  • 保留富文本格式的话,用WKWebView展示会比UITextView更接近原网页的排版效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 10:00:45