Swarm是Swift编写的快速、简单且模块化的网络抓取解决方案。
特性
- 同时运行的爬虫实例
- 根据服务器请求自动重试和减速
- 可定制的网络层(默认为
URLSession
) - 深度优先/宽度优先选项
- 跨平台
快速开始
class Scrapper: SwarmDelegate {
lazy var swarm = Swarm(startURLs: [startingURL, anotherStartingURL], delegate: self)
init() {
swarm.start()
}
func scrappedURL(_ url: VisitedURL, nextScrappableURLs: @escaping ([ScrappableURL]) -> Void) {
if let htmlString = url.htmlString() {
// Scrap data from htmlString
nextScrappableURLs([ScrappableURL(url: nextURL)])
} else {
nextScrappableURLs([])
}
}
func scrappingCompleted() {
print("Scrapping took: \(swarm.scrappingDuration ?? 0)")
}
}
需求
- Xcode 12及以上
- Swift 5.3及以上
- iOS 10 / macOS 10.12 / Linux / tvOS 10.0 / watchOS 3.0
如果您在Apple Watch上进行网络抓取,您的用例非常特别:)
安装
Swift Package Manager(需要Xcode 11)
- 将软件包添加到项目设置 -> Swift 包
如果您基于Swarm构建一个包,请在Package.swift中添加以下代码
.package(url: "https://github.com/DenTelezhkin/Swarm.git", .upToNextMajor(from: "0.2.0"))
CocoaPods
pod 'Swarm'
添加更多URL
初始化Swarm并设置起始URL后,您可能需要按照以下方法添加更多
swarm.add(ScrappableURL(url: newURL, depth: desiredDepth))
您还可以在scrappedURL(_:nextScrappableURLs:)
委托方法回调中添加更多要抓取的URL
nextScrappableURLs([ScrappableURL(url: nextURL)])
请注意,无需检查这些URL的唯一性,因为内部URL存储在Set
中,并且在抓取过程中,已访问的URL会被保存在日志中。
请注意,在
scrappedURL(_:nextScrappableURLs:)
委托方法中调用nextScrappableURLs
闭包是必需的,因为Swarm正在等待所有此类闭包被调用,以完成网络抓取。
配置
SwarmConfiguration
对象在Swarm
初始化期间传递。对于所有参数,它都有合理的默认值,但是如果您需要,可以修改其中任何一个
- 成功状态码
- 延迟重试状态码
- 延迟重试延迟
- 在放弃之前,延迟重试的次数
- 最大自动节流延迟(Swarm会尝试遵守“Retry-After”响应头,但延迟不会大于该变量中指定的延迟)
- 放弃前,最大自动节流请求重试次数
- 下载延迟(每个蜘蛛的冷却时间)
- 最大并发连接数
示例:下载延迟=1,.maxConcurrentConnections=8等于每秒钟约8个请求,不包括解析时间
深度或宽度?
网页可能包含多个要跟踪的链接,根据您的目标,您可能希望更深入或更广泛地进行(例如,我是否要先获取页面上所有项目,然后再加载数据详情,反之亦然)。
默认情况下,Swarm作为LIFO队列运行,完全忽略深度。但是,您可以设置SwarmConfiguration
对象中的此值来要求首先考虑深度。
configuration.scrappingBehavior = .depthFirst
在这种情况下,当选择要抓取的下一个URL时,Swarm
将选择具有最大depth
值的ScrappableURL
实例。另请考虑,如果使用.breadthFirst
行为,最低depth
URL将优先考虑。
网络传输
默认情况下,Swarm
使用Foundation.URLSession
作为所有网络请求的网络传输。您可以通过采用代理方法来自定义请求的发送方式。
func spider(for url: ScrappableURL) -> Spider {
let spider = URLSessionSpider()
// Handle cookies instead of ignoring them
spider.httpShouldHandleCookies = true
spider.userAgent = .static("com.my_app.custom_user_agent")
spider.requestModifier = {
var request = $0
// Modify URLRequest instance for each request
request.timeoutInterval = 20
return request
}
// Modify HTTP headers
spider.httpHeaders["Authorization"] = "Basic dGVzdDp0ZXN0"
return spider
}
如果您需要,也可以通过实现简单的Spider
协议来实现自己的网络传输。
public protocol Spider {
func request(url: ScrappableURL, completion: @escaping (Data?, URLResponse?, Error?) -> Void)
}
该实现的示例可以在Swarm
的单元测试中找到,其中使用MockSpider
来模拟测试套件中的所有网络请求。
Spider生命周期
在这种情况下,图像胜于千言万语。
在Vapor应用中使用SwiftNIO管理冷却时间
默认情况下,Swarm使用DispatchQueue.main.asyncAfter(deadline:execute:)
方法在网络请求之间执行延迟。这对于Mac应用来说是可以的,但如果您在Vapor等服务器环境中运行,则不应使用主线程来安排任何事情。实际上,将任务调度到主线程可能甚至无法工作。
为了在Vapor应用中正确地执行冷却时间,请按照以下方式设置Swarm
实例上的cooldown
swarm.cooldown = { interval, closure in
eventLoop.scheduleTask(in: TimeAmount.seconds(Int64(interval)), closure)
}
成为一个友好的邻里网络爬虫
工欲善其事,必先利其器。您可能会想要减少下载冷却时间,增加并行性,并以您令人敬畏的千兆以太网的传输速度下载所有内容。请不要这样做,请!)
首先,走得更慢实际上可能会导致抓包成功,并且您不会在尝试获取数据的服务器上被禁止。此外,走得更慢意味着您将对服务器施加较小的压力,并允许服务器在处理接收到的所有数据请求时,不会使所有人的(包括您的)请求减慢。
阅读scrapy 常用实践文档,该文档的许多内容也适用于这个框架(原则上)。
为了进一步讲解如何避免被封,以下是一些关于Swarm使用的技巧。
使用用户代理池
spider.userAgent = .randomized(["com.my_app.custom_user_agent", "com.my_app.very_custom_user_agent"])
禁用cookies
spider.httpShouldHandleCookies = false
如果您遇到重试响应,通过增加蜘蛛的冷却时间并减少并发性来减慢速度。
configuration.downloadDelay = 2
configuration.maxConcurrentConnections = 5
为了监控重试响应,实现以下委托方法,您可以观察为什么Swarm
决定延迟后续请求。
func delayingRequest(to url: ScrappableURL, for timeInterval: TimeInterval, becauseOf response: URLResponse?) {
// Try inspecting (response as? HTTPURLResponse)?.statusCode
// Also: (response as? HTTPURLResponse)?.allHeaderFields
}
常见问题解答(FAQ)
我正在做一些严肃的网页抓取,这个框架适合我吗?
这取决于您的情况。本项目注重简洁性,并且首先适用于Apple平台,然后才是Linux。它适用于我的用例,但如果您的用例更复杂,您应该查看scrapy,它具有更多的特性和企业级支持。
此外,本项目是用Swift编写的,而scrapy是用Python编写的,这取决于您的起点,两者可以是一个优势也可以是一个劣势。
为什么没有内置的数据提取机制?
这又回到了简洁性的问题。您可能会喜欢使用SwiftSoup进行HTML解析,或者您可能喜欢使用Kanna使用XPath提取数据。也许您甚至需要使用无头浏览器来先渲染您的网页。当前的方法是安装Swarm
,并使用您需要的任何解析库(如果需要的话)。
有命令行界面(CLI)吗?
没有,也暂时没有计划。现在用SwiftUI在Mac上构建应用程序只需要几行代码,因此在这个时候,CLI的需求已经不存在了。
Carthage包管理器是否有支持?
目前还没有。仓库中没有Xcode项目,Carthage 并不直接支持使用由SwiftPM生成的项目。
然而,你可以遵循这个指南来解决这个问题。
关于为什么我不在这个仓库中包含Xcode项目的原因——这是个人的,我只是非常厌倦使用Carthage来管理依赖项,我发现这非常慢,并且非常可能出现问题。CocoaPods虽然被一些人评判,但非常流行,并且在我的生产环境中已经使用多年,比Carthage少了很多问题。此外,Swift Package Manager是目前我选择的依赖管理器,因为它具有Xcode集成,并且在编译速度和简单性方面实际上超过了CocoaPods。
未来规划有哪些?
根据社区反馈和我在框架上的使用情况,以下功能可能会被实现
- Linux支持
- 自动解析robots.txt
- 自动解析网站地图
- 自动检测链接并限制域名
- 在Vapor服务器上提供示例使用
- 为访问过的页面历史记录提供外部存储
- 其他我目前尚未了解的内容
许可证
Swarm
在MIT许可证下发布。详情见LICENSE。