WKZombie 1.1.1

WKZombie 1.1.1

测试已测试
Lang语言 SwiftSwift
许可证 MIT
Released最新版本2018年3月
SwiftSwift 版本4.0
SPM支持 SPM

Mathias Köhnke 维护。



WKZombie 1.1.1

  • 作者:
  • Mathias Köhnke

WKZombie

WKZombie 是一个无图形用户界面的 iOS/OSX 网页浏览器。它的开发是为了试验如何使用 Swift 4 编写功能概念

它集成了 WebKit (WKWebView) 用于渲染和 hpple (libxml2) 用于解析 HTML 内容。此外,它还可以拍摄快照,并且对解析/解码 JSON 元素 有基本支持。通过 链式异步操作,使代码紧凑且易于使用。

使用场景

无头浏览器有很多使用场景。其中一些是:

  • 没有 API 收集数据
  • 抓取网站
  • 自动化网站交互
  • 操纵网站
  • 运行自动化测试/快照
  • 等等。

示例

以下示例旨在展示 WKZombie 的功能。假设我们想要在 Apple 开发者门户中 显示所有 iOS 预配文件

手动网页浏览器导航

在 iOS 的通用网络浏览器(例如 Mobile Safari)中,您通常会输入您的凭据,登录并通过链接导航到 预配文件 部分。

使用 WKZombie 自动化

相同的导航过程可以通过在链接 WKZombie 动作 的 iOS/OSX 应用程序中 自动 生成。此外,现在可以使用 UITextFieldUIButtonUITableView 以原生方式操纵或显示此数据。

通过查看 Example 目录中的 iOS/OSX 演示来了解如何使用它。

入门

iOS / OSX

开始的最佳方式是查看示例项目。只需在您的 shell 中运行以下命令即可:

$ cd Example
$ pod install
$ open Example.xcworkspace

注意:您需要 CocoaPods 1.0 beta4 或更高版本。

命令行

对于命令行演示,请在 WKZombie 的根目录中运行以下命令

$ swift build -Xcc -I/usr/include/libxml2 -Xlinker -lxml2

$ .build/debug/Example <appleid> <password>

使用方法

WKZombie 实例相当于一个网页会话。顶级便捷方法如 WKZombie.open() 使用共享实例,该实例已配置为默认设置。

因此,以下三个语句是等效的

let action : Action<HTMLPage> = open(url)
let action : Action<HTMLPage> = WKZombie.open(url)
let browser = WKZombie.sharedInstance
let action : Action<HTMLPage> = browser.open(url)

应用程序也可以创建自己的 WKZombie 实例

self.browser = WKZombie(name: "Demo")

请确保在使用期间将 browser 保存在存储属性中。

自动化操作

网页导航基于 操作,这些操作可以在使用 >>>>>* (用于快照) 操作符进行操作时隐式执行。所有链式操作都将结果传递到下一个操作。然后,=== 操作符启动操作链的执行。

以下示例说明如何使用 WKZombie 从开发者门户收集所有配置文件,并对每一页执行快照

    open(url)
>>* get(by: .id("accountname"))
>>> setAttribute("value", value: user)
>>* get(by: .id("accountpassword"))
>>> setAttribute("value", value: password)
>>* get(by: .name("form2"))
>>> submit
>>* get(by: .contains("href", "/account/"))
>>> click(then: .wait(2.5))
>>* getAll(by: .contains("class", "row-"))
=== myOutput

为了输出或处理收集到的数据,可以使用闭包或实现一个接受结果作为参数的自定义函数。

func myOutput(result: [HTMLTableColumn]?) {
  // handle result
}

func myOutput(result: Result<[HTMLTableColumn]>) {
  switch result {
  case .success(let value): // handle success
  case .error(let error): // handle error
  }
}

手动操作

操作也可以通过调用 start() 方法手动启动。

let action : Action<HTMLPage> = browser.open(url)

action.start { result in
    switch result {
    case .success(let page): // process page
    case .error(let error):  // handle error
    }
}

这当然是一个更简单的方式,但您需要编写更多代码,当您想要连续执行 操作 时可能会变得混乱。

基本操作函数

目前实现了一些基本 操作 帮助您访问和导航网站

打开网站

返回的 WKZombie Action 将加载并返回指定 URL 的 HTML 或 JSON 页面。

func open<T : Page>(url: URL) -> Action<T>

可选,可以传递一个 PostAction。这是一个特殊的等待/验证操作,在页面加载完成后执行。更多信息请见PostAction

func open<T : Page>(then: PostAction) -> (url: URL) -> Action<T>

获取当前网站

返回的 WKZombie Action 将获取当前页面。

func inspect<T: Page>() -> Action<T>

提交表单

返回的 WKZombie Action 将提交指定的 HTML 表单。

func submit<T : Page>(form: HTMLForm) -> Action<T>

可选,可以传递一个 PostAction。更多信息请见PostAction

func submit<T : Page>(then: PostAction) -> (form: HTMLForm) -> Action<T>

点击链接 / 按钮压

返回的 WKZombie Action 将模拟与 HTML 链接/按钮的交互。

func click<T: Page>(link : HTMLLink) -> Action<T>
func press<T: Page>(button : HTMLButton) -> Action<T>

可选,可以传递一个 PostAction。更多信息请见[PostAction](#Special-Parameters)。

func click<T: Page>(then: PostAction) -> (link : HTMLLink) -> Action<T>
func press<T: Page>(then: PostAction) -> (button : HTMLButton) -> Action<T>

注意:HTMLButton 仅在存在 "onClick" HTML 属性时工作。如果您想提交 HTML 表单,应使用 提交 而不是点击。

查找 HTML 元素

返回的 WKZombie Action 将搜索指定的 HTML 页面,并返回与通用 HTML 元素类型和传递 SearchType 匹配的第一个元素。

func get<T: HTMLElement>(by: SearchType<T>) -> (page: HTMLPage) -> Action<T>

返回的 WKZombie Action 将搜索并返回所有匹配的元素。

func getAll<T: HTMLElement>(by: SearchType<T>) -> (page: HTMLPage) -> Action<[T]>

设置属性

返回的 WKZombie Action 将在指定的 HTMLElement 上设置或更新现有属性/值对。

func setAttribute<T: HTMLElement>(key: String, value: String?) -> (element: T) -> Action<HTMLPage>

执行JavaScript

返回的 WKZombie Actions 将执行一个 JavaScript 字符串。

func execute(script: JavaScript) -> (page: HTMLPage) -> Action<JavaScriptResult>
func execute(script: JavaScript) -> Action<JavaScriptResult>

例如,以下示例展示了如何使用 execute() 方法获取当前加载网站的标题

    browser.inspect
>>> browser.execute("document.title")
=== myOutput

func myOutput(result: JavaScriptResult?) {
  // handle result
}

以下代码显示了执行 JavaScript 的另一种方式,即属性的值

    browser.open(url)
>>> browser.get(by: .id("div"))
>>> browser.map { $0.objectForKey("onClick")! }
>>> browser.execute
>>> browser.inspect
=== myOutput

func myOutput(result: HTMLPage?) {
  // handle result
}

抓取

一些实现 HTMLFetchable 协议的 HTMLElement (例如 HTMLLinkHTMLImage) 包含链接到远程对象或数据的属性,如 "src""href"。以下方法返回一个可以方便地下载此数据的 WKZombie Action

func fetch<T: HTMLFetchable>(fetchable: T) -> Action<T>

一旦执行了 fetch 方法,就可以检索并 转换 数据。以下示例显示了如何将从一个链接中检索到的数据转换为 UIImage

let image : UIImage? = link.fetchedContent()

抓取数据可以转换为实现 HTMLFetchableContent 协议的类型。目前支持以下类型

  • UIImage / NSImage
  • Data

注意:有关如何使用此功能的更多信息,请参阅 OSX 示例。

转换

返回的 WKZombie Action 将使用指定的函数 f 将 WKZombie 对象转换成另一个对象。

func map<T, A>(f: T -> A) -> (element: T) -> Action<A>

该函数使用指定的函数 f 将一个对象转换成另一个对象。

func map<T, A>(f: T -> A) -> (object: T) -> A

获取快照

获取快照对 iOS 是可用的。首先,必须注册一个 snapshotHandler,它在获取快照时将被调用

WKZombie.sharedInstance.snapshotHandler = { snapshot in
    let image = snapshot.image
}

其次,添加 >>* 操作符将触发快照事件

    open(url)
>>* get(by: .id("element"))
=== myOutput

注意:此操作符仅与 WKZombie 共享实例一起工作。

或者,可以使用 snap 命令

    browser.open(url)
>>> browser.snap
>>> browser.get(by: .id("element"))
=== myOutput

有关如何获取快照的更多信息,请参阅 iOS 示例

特殊参数

1. PostAction

某些包含 (重新) 加载网页 (例如 打开提交 等) 的 Action 具有可用的 PostAction。PostAction 是一个等待或验证操作,将在页面加载完成后执行

PostAction 描述
wait (秒数) 动作将在页面加载后等待指定秒数的时间,然后返回。这适用于页面加载已完成,但 JavaScript 或图像加载仍在进行中的情况。
validate (JavaScript) 如果指定的 JavaScript 表达式或脚本返回 'true' 或发生超时,则操作将完成。

2. SearchType

为了在页面中找到某些 HTML 元素,您必须指定一个 SearchType。get() 和 getAll() 的返回类型是通用的,它确定了应该搜索哪种标签。例如,以下会返回所有具有 book 类的链接

let books : Action<HTMLLink> = browser.getAll(by: .class("book"))(page: htmlPage)

以下是当前可用和受支持的 6 种类型

SearchType 描述
id (字符串) 返回与指定 id 匹配的元素。
name (字符串) 返回所有匹配指定 name 属性值的元素。
text (字符串) 返回所有包含指定文本的内部内容的元素。
class (String) 返回所有匹配指定类名的元素。
attribute (String, String) 返回所有匹配指定属性名/值组合的元素。
contains (String, String) 返回所有具有包含指定值的属性的元素。
XPathQuery (String) 返回所有匹配指定XPath查询的元素。

运算符

以下运算符可用于操作,这使得连续的操作更容易阅读。

运算符 iOS OSX 描述
>>> x x 此运算符等效于andThen()方法。在此,左侧操作将被启动,并将结果用作右侧操作的参数。注意:如果右侧的操作不需要参数,那么左侧操作的结果将被忽略,并不会传递。
>>* x 这是snap命令的一个方便的运算符。它与>>>运算符相同,不同之处在于在左侧操作完成后会拍摄快照。注意:此运算符在与其他非共享实例一起使用时会抛出断言。
=== x x 此运算符启动左侧操作,并将结果作为Optional传递给右侧函数。

认证

有时您可能需要处理认证挑战,例如基本认证自签名证书。WKZombie提供了一个authenticationHandler,当内部网页需要响应认证挑战时会被调用。

基本认证

下面的例子演示了如何处理基本认证。

browser.authenticationHandler = { (challenge) -> (URLSession.AuthChallengeDisposition, URLCredential?) in
	return (.useCredential, URLCredential(user: "user", password: "passwd", persistence: .forSession))
}

自签名证书

在自签名证书的情况下,您可以使用认证处理程序如下操作。

browser.authenticationHandler = { (challenge) -> (URLSession.AuthChallengeDisposition, URLCredential?) in
	return (.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
}

高级操作函数

批量

返回的WKZombie操作将对指定的动作函数f进行大量执行,并使用提供的输入元素。一旦所有操作都执行完毕,将返回收集到的结果。

func batch<T, U>(f: T -> Action<U>) -> (elements: [T]) -> Action<[U]>

收集

返回的WKZombie操作将执行指定的动作(使用前一个动作执行结果作为输入参数),直到满足一定条件。之后,它将返回收集到的动作结果。

func collect<T>(f: T -> Action<T>, until: T -> Bool) -> (initial: T) -> Action<[T]>

交换

注意:由于XPath的限制,WKZombie无法直接访问iframe内部的元素。交换函数可以通过切换Web上下文来解决这个问题。

返回的WKZombie操作将交换当前页面的上下文与嵌入的<iframe>的上下文。

func swap<T: Page>(iframe : HTMLFrame) -> Action<T>
func swap<T: Page>(then postAction: PostAction) -> (iframe : HTMLFrame) -> Action<T>

以下示例演示了如何按iframe中嵌入的按钮。

    browser.open(startURL())
>>> browser.get(by: .XPathQuery("//iframe[@name='button_frame']"))
>>> browser.swap
>>> browser.get(by: .id("button"))
>>> browser.press
=== myOutput

测试/调试

转储

此命令对调试非常有用。它将WKZombie浏览器当前状态打印为DOM

func dump()

清除缓存和Cookies

清除缓存/cookies数据(例如登录数据等)。

func clearCache()

日志记录

通过设置以下Logger变量可以启用或禁用WKZombie的日志记录。

Logger.enabled = false

用户代理

可以通过设置以下变量来更改WKZombie的用户代理。

browser.userAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 9_0 like Mac OS X) AppleWebKit/601.1.32 (KHTML, like Gecko) Mobile/13A4254v"

超时

如果需要完成操作的时间超过了本属性指定的值,则操作将被取消。默认值为30秒。

browser.timeoutInSeconds = 15.0

加载媒体内容

默认值为'true'。如果设置为'false',则一旦传输完'raw' HTML数据,加载进度就会完成。不会加载视频或图像等媒体内容。

browser.loadMediaContent = false

显示网络活动

如果设置为true,则将在状态栏中显示网络活动指示器。默认为true

browser.showNetworkActivity = true

HTML元素

使用WKZombie与网站交互时,以下类的参与:

HTMLPage

此类代表一个网站的只读DOM。它允许您使用SearchType参数搜索HTML元素。

HTMLElement

HTMLElement类是DOM中所有元素的基本类。它允许您检查该元素的属性或内部内容(例如文本)。目前有7个子类,具有额外的元素特定方法和变量可供使用。

  • HTML表单
  • HTML链接
  • HTML按钮
  • HTML图像
  • HTML表格
  • HTML表格列
  • HTML表格行

可以通过轻松实现额外的子类来实现,并且可能在未来添加。

JSON元素

如上所述,WKZombie提供了对JSON文档的基本支持。

方法和协议

对于解析和编码JSON,以下方法和协议可用:

解析

WKZombie动作返回将解析数据并创建JSON对象。

func parse<T: JSON>(data: Data) -> Action<T>

解码

以下方法返回WKZombie动作,该动作将接受一个JSONParsable(数组、字典和JSONPage)并将其解码为模型对象。该特定的模型类必须实现JSONDecodable协议。

func decode<T : JSONDecodable>(element: JSONParsable) -> Action<T>
func decode<T : JSONDecodable>(array: JSONParsable) -> Action<[T]>

JSONDecodable

此协议必须由每个要支持JSON解码的类实现。实现将接收一个JSONElement(Dictionary)并创建该类的一个对象实例。

static func decode(json: JSONElement) -> Self?

示例

以下示例展示了如何结合使用WKZombie进行JSON解析/解码。

    browser.open(bookURL)
>>> browser.decode
=== myOutput
func myOutput(result: Book?) {
  // handle result
}

安装

Swift Package Manager

要使用Swift Package Manager构建WKZombie,将其添加到您的Package.swift文件中,并运行以下命令:

swift build -Xcc -I/usr/include/libxml2 -Xlinker -lxml2

常见问题解答(FAQ)

如何在同一项目中使用WKZombie和Alamofire?

在与Alamofire和WKZombie在同一项目中使用时,您可能会遇到如下的关键词Result冲突问题:

'Result' is ambiguous for type lookup in this context

这是因为这两个模块都使用了它们结果枚举类型的相同名称。您可以使用以下语法在该特定文件中明确标识该类型:

import enum WKZombie.Result

从现在起,Result将明确指代WKZombie模块中的那个。

如果这仍然在某些文件中模糊不清或不够理想,您可以为import创建一个Swift文件,并使用类型别名进行重命名:

import enum WKZombie.Result
typealias WKZombieResult<T> = Result<T>

有关更多信息,请参阅此处找到的解决方案:这里

贡献

请参阅CONTRIBUTING文件以了解如何提供帮助。您需要在WKZombie根目录中运行以下命令以设置一个可构建的框架项目(WKZombie.xcworkspace):

$ Scripts/setup-framework.sh

待办事项(TODOs)

更多单元测试

Mathias Köhnke @mkoehnke

更多资源

A list of (almost) all headless web browsers in existence

Efficient JSON in Swift with Functional Concepts and Generics — Tony DiPasquale

WKZombie可供使用MIT许可证。有关更多信息,请参阅LICENSE文件。

近期更改

发布说明可以在这里找到。

CocoaPods是一个由以下人员创建的项目: