使用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
相关产品推荐
相关产品推荐

