返回

SwiftUI 解析 HTML 提取段落和链接的终极指南

IOS

SwiftUI:解析 HTML 以提取段落和链接的完整指南

引言

在 SwiftUI 应用中,解析 HTML 文档以提取段落和链接是构建交互式内容的常见任务。本文将探讨在 SwiftUI 中使用 SwiftSoup 解析 HTML 文档并提取包含链接的段落的最佳实践。我们将逐步指导您完成过程,并提供示例代码来帮助您入门。

问题:解析包含链接的段落

在使用 SwiftSoup 解析 HTML 文档时,一个常见的挑战是如何正确处理包含链接且以特定样式(如红色文本)编写的段落。目标是提取这些段落中的链接信息,并在视图中以类似于图像中的方式显示它们。

解决方案:修改 HTML 解析代码

为了解决这个问题,我们需要修改 HTML 解析代码以识别包含链接的段落并提取链接信息。修改后的代码如下:

do {
    let doc: Document = try SwiftSoup.parse(dataPolimats.content.rendered)
    let elements: Elements = try doc.select("*")

    var viewModels: [ElementViewModel] = []
    for element in elements {
        if let tagName = try? element.tagName() {
            switch tagName {
            case "img":
                if let src = try? element.attr("src") {
                    viewModels.append(ImageViewModel(src: src))
                }

            case "a":
                if let href = try? element.attr("href"), let text = try? element.text() {
                    viewModels.append(LinkViewModel(label: text, destination: href))
                }

            case "h1", "h2", "h3", "h4", "h5", "h6":
                if let text = try? element.text() {
                    viewModels.append(TitleViewModel(text: text))
                }

            case "p":
                // 获取段落文本
                if let text = try? element.text() {
                    let paragraph = ParagraphViewModel(text: text)

                    // 检查段落中是否存在链接
                    if element.select("a").count > 0 {
                        // 提取链接信息
                        for link in try? element.select("a") {
                            if let href = try? link.attr("href"), let linkText = try? link.text() {
                                // 创建链接视图模型
                                let linkViewModel = LinkViewModel(label: linkText, destination: href)

                                // 将链接视图模型添加到段落视图模型
                                paragraph.links.append(linkViewModel)
                            }
                        }
                    }

                    viewModels.append(paragraph)
                }

            case "ul":
                if let listItems = try? element.select("li") {
                    for listItem in listItems {
                        if let text = try? listItem.text() {
                            viewModels.append(ListItemViewModel(text: text))
                        }
                    }
                }

            case "iframe":
                if let src = try? element.attr("src") {
                    viewModels.append(YouTubeViewModel(embedURL: src))
                }

            default:
                break
            }
        }
    }
}

更新段落视图模型

为了支持包含链接的段落,我们更新了段落视图模型:

struct ParagraphViewModel: ElementViewModel {
    let id = UUID().uuidString
    let text: String
    var links: [LinkViewModel] = []

    var view: AnyView {
        AnyView(
            HStack {

                Text(text)
                    .font(.custom("Roboto-Regular", size: 17))
                    .lineSpacing(10)
                    .multilineTextAlignment(.leading)

                Spacer()
            }
        )
    }
}

显示链接

要显示段落中的链接,请循环遍历 ParagraphViewModel 中的 links 数组,并使用 LinkViewModel 中的 view 属性创建链接视图。

完整代码示例

以下是一个完整代码示例,演示了如何解析 HTML 文档,提取段落和链接,并在视图中显示它们:

import SwiftUI
import SwiftSoup

struct ContentView: View {
    @State private var viewModels: [ElementViewModel] = []

    var body: some View {
        VStack {
            ForEach(viewModels) { viewModel in
                viewModel.view
            }
        }
        .onAppear {
            // HTML 解析代码
            do {
                let doc: Document = try SwiftSoup.parse(html)
                let elements: Elements = try doc.select("*")

                var viewModels: [ElementViewModel] = []
                for element in elements {
                    if let tagName = try? element.tagName() {
                        switch tagName {
                        case "img":
                            if let src = try? element.attr("src") {
                                viewModels.append(ImageViewModel(src: src))
                            }

                        case "a":
                            if let href = try? element.attr("href"), let text = try? element.text() {
                                viewModels.append(LinkViewModel(label: text, destination: href))
                            }

                        case "h1", "h2", "h3", "h4", "h5", "h6":
                            if let text = try? element.text() {
                                viewModels.append(TitleViewModel(text: text))
                            }

                        case "p":
                            // 获取段落文本
                            if let text = try? element.text() {
                                let paragraph = ParagraphViewModel(text: text)

                                // 检查段落中是否存在链接
                                if element.select("a").count > 0 {
                                    // 提取链接信息
                                    for link in try? element.select("a") {
                                        if let href = try? link.attr("href"), let linkText = try? link.text() {
                                            // 创建链接视图模型
                                            let linkViewModel = LinkViewModel(label: linkText, destination: href)

                                            // 将链接视图模型添加到段落视图模型
                                            paragraph.links.append(linkViewModel)
                                        }
                                    }
                                }

                                viewModels.append(paragraph)
                            }

                        case "ul":
                            if let listItems = try? element.select("li") {
                                for listItem in listItems {
                                    if let text = try? listItem.text() {
                                        viewModels.append(ListItemViewModel(text: text))
                                    }
                                }
                            }

                        case "iframe":
                            if let src = try? element.attr("src") {
                                viewModels.append(YouTubeViewModel(embedURL: src))
                            }

                        default:
                            break
                        }
                    }
                }

                self.viewModels = viewModels
            } catch {
                print("Error parsing HTML: \(error)")
            }
        }
    }
}

结论

通过遵循这些步骤,您可以从包含链接的段落中正确解析 HTML 文档,并以所需的方式在 SwiftUI 视图中显示它们。这种技术对于构建交互式内容,如新闻文章、博客帖子和其他富文本格式,至关重要。

常见问题解答

  1. 如何识别和提取 HTML 文档中的链接?

    • 使用 SwiftSoupselect("a") 方法来选择文档中的所有 <a> 元素,这些元素代表超链接。然后,您可以提取 href 属性以获取链接的目的地,并提取文本以获取链接的标签。
  2. 如何处理包含链接的段落?

    • 首先获取段落的文本,然后使用 select("a") 方法检查段落中是否存在 <a> 元素。如果是,请提取链接信息并创建链接视图模型,然后将它们添加到段落视图模型中。
  3. 如何显示链接?

    • 在 SwiftUI 中显示链接,请循环遍历段落视图模型中的 links 数组,并使用 LinkViewModel 中的 view 属性创建链接视图。
  4. 如何处理带有不同样式的链接?

    • 您可以在 HTML 解析阶段使用 SwiftSoupattr("style") 方法来获取链接的样式信息。然后,您可以根据样式信息自定义链接视图的样式。
  5. 我可以在 SwiftUI 中解析其他类型的 HTML 元素吗?

    • 是的,您可以在 SwiftUI 中解析任何类型的 HTML 元素,只要您使用 SwiftSoup 提供的相应方法。例如,您可以使用 select("img") 来选择图像,select("h1") 来选择标题,依此类推。