HTMLReader
一个遵循 WHATWG HTML 规范 且包含 CSS 选择器 的 Objective-C 和 Foundation 中的 HTML 解析器。它解析 HTML 的方式就像浏览器一样。
用法
以下是一个快速示例:解析一个内联文档并找到加粗文本
import HTMLReader
let document = HTMLDocument(string: """
<p>
Ahoy there, <b>sailor</b>!
</p>
""")
print(document.firstNode(matchingSelector: "b")?.textContent ?? "")
// => sailor
操作文档稍微复杂一些,但完全可以做到。这里我们从第一个示例中取出文档并给段落包裹一个新的元素
if
let p = document.firstNode(matchingSelector: "p"),
let parent = p.parent
{
let wrapper = HTMLElement(tagName: "div", attributes: ["class": "special"])
let children = parent.mutableChildren
children.insert(wrapper, at: children.index(of: p))
p.parent = wrapper
}
print(document.innerHTML)
// => <html><head></head><body><div class="special"><p>\
// Ahoy there, <b>sailor</b>!\
// </p></div></body></html>
您可以保存文档为文件
let serialized = document.serializedFragment
let temp = FileManager.default.temporaryDirectory
let fileURL = temp.appendPathComponent("nifty.html", isDirectory: false)
do {
try serialized.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
print("Could not save nifty document", error)
}
最后,最复杂的示例:获取 HTMLReader 仓库的主页,并抓取项目的描述。(这只是示例;如果需要获取仓库的描述,GitHub 提供了一个出色的 API!)
@import HTMLReader;
// Load a web page.
NSURL *url = [NSURL URLWithString:@"https://github.com/nolanw/HTMLReader"];
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:url completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *contentType = nil;
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSDictionary *headers = [(NSHTTPURLResponse *)response allHeaderFields];
contentType = headers[@"Content-Type"];
}
HTMLDocument *home = [HTMLDocument documentWithData:data
contentTypeHeader:contentType];
HTMLElement *div = [home firstNodeMatchingSelector:@".repository-meta-content"];
NSCharacterSet *whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSLog(@"%@", [div.textContent stringByTrimmingCharactersInSet:whitespace]);
// => A WHATWG-compliant HTML parser in Objective-C.
}] resume];
安装
您有多种选择
-
将 Sources 文件夹中的文件复制到您的项目中。
-
将以下行添加到您的 Cartfile 中
github "nolanw/HTMLReader"
-
将以下行添加到您的 Podfile 中
pod "HTMLReader"
-
将以下行添加到您的 Package.swift 中
.package(url: "https://github.com/nolanw/HTMLReader", from: "2.1.6")
-
在Xcode中,通过选择“文件 > Swift包 > 添加包依赖”来添加此包,并输入URL
https://github.com/nolanw/HTMLReader
。 -
克隆此仓库(可能将其添加为子模块),并将
HTMLReader.xcodeproj
添加到您的项目/工作区中。然后,将HTMLReader.framework
添加到您的应用程序目标中。(或者,如果您正在针对iOS 8.0之前的版本:将libHTMLReader.a
添加到您的应用程序目标中,并将"$(SYMROOT)/include"
添加到应用程序目标的首选头文件搜索路径中。)
HTMLReader除了Foundation没有其他依赖。
为什么选择HTMLReader?
我需要像浏览器一样抓取HTML。在iOS上找不到一个不错的选择。
替代方案
libxml2 与iOS一起提供。它解析HTML 4的某些版本(?)并且不像现代浏览器那样处理新的/损坏的标记。
我遇到的其他Objective-C和Swift库(例如,Fuzi,hpple,Kanna,Ono)使用libxml2并且继承了其缺点。
SwiftSoup 是Jsoup的Swift端口。我在创建HTMLReader时还不存在。(公平地说,Swift也尚未公开。)
还有诸如Gumbo 或 Hubbub 这样的C库,但您需要将数据在Objective-C或Swift之间 shuffle。(此外,Gumbo在HTMLReader进度很远后才公开。)
WebKit 与iOS一起提供,但它的HTML解析功能被视为私有API。我认为通过网页视图进行循环不适合解析HTML。而且我没有深入构建自己的WebCore副本。
Google Toolbox for Mac 会为HTML(例如,&
⇔ &
)进行转义和反转义,但又不像是现代浏览器。例如,GTM不会反转义A
(请注意缺少分号)。
CFStringTransform 通过(可逆的)kCFStringTransformToXMLHex
进行数字实体转换,但排除了命名实体。
它正常工作吗?
HTMLReader 不断运行 html5lib 的标记化和树结构构建测试,忽略对 <template>
的测试(该元素 HTMLReader 并未实现)。请注意,为了实际运行这些测试,您需要检出 HTMLReaderTests/html5lib
Git 子模块。
HTMLReader 在 GitHub Actions 提供的 SDK 上持续构建和测试。它旨在支持 iOS 5.0、macOS 10.7、tvOS 9.0 和 watchOS 2.0,但尚未进行自动化测试(尽管如此,您可以提交问题!)。
HTMLReader 至少被一个已发布的应用程序使用:Awful.app。
它有多快?
我不确定。
项目包含了名为 Benchmarker 的一个实用工具。它知道如何运行三个测试:
- 解析一个大型 HTML 文件。在这种情况下,7MB 的单页 HTML 规范。
- 在大型 HTML 文件中转义和反转义实体。
- 运行大量 CSS 选择器。基本上是从 WebKit 性能测试 中复制而来。
对 HTMLReader 的更改不应导致这些基准测试速度变慢。理想情况下,更改应该使它们运行更快!
错误和功能请求
可以通过 问题追踪器 报告错误并请求功能。或者如果您更喜欢直接联系。
许可证
HTMLReader 处于公有领域。
致谢
HTMLReader 由 Nolan Waite 开发。
感谢 Chris Williams 贡献 CSS 选择器的实现。