Skip to content

nanzhipro/SwiftWebCrawler

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SwiftWebCrawler

Swift Platform License

SwiftWebCrawler 是一个功能强大且可扩展的 Swift 库,旨在帮助开发者在 iOS 和 macOS 应用中轻松实现网页爬取功能。该库支持代理配置、并发控制、robots.txt 遵守以及详细的错误处理和日志记录。

目录

  1. 特性
  2. 安装
  3. 使用方法
  4. 配置选项
  5. 日志记录
  6. 错误处理
  7. 示例代码
  8. 测试
  9. 贡献
  10. 许可证

特性

  • 跨平台支持:兼容 iOS 和 macOS,覆盖了大部分 Apple 生态系统设备。
  • 模块化设计:独立的库,易于维护和扩展。
  • 丰富的配置选项:通过 CrawlerConfiguration 提供代理支持、并发控制等配置。
  • 错误处理与日志记录:详细的错误分类和内置日志记录机制,便于调试和监控。
  • 高效并发控制:使用 AsyncSemaphoreResultsActor,确保在异步上下文中安全高效地处理并发任务。
  • robots.txt 遵守:可选配置,确保爬虫行为符合网站规范。
  • 代理支持:支持 HTTP、HTTPS 和 SOCKS 代理,包含可选的认证信息。

安装

SwiftWebCrawler 通过 Swift Package Manager 提供,您可以轻松将其集成到您的项目中。

步骤

  1. 打开您的项目 在 Xcode 中。
  2. 添加包依赖
    • 在 Xcode 的菜单栏中,选择 File > Add Packages...
    • 在弹出的对话框中,输入您的包仓库地址,例如:
      https://github.com/yourusername/SwiftWebCrawler.git
      
    • 点击 Add Package
  3. 选择版本
    • 选择 Up to Next Major,并确保选择的是最新的稳定版本(例如,1.0.0)。
  4. 完成安装
    • 点击 Add Package 完成安装。现在,您可以在项目中导入并使用 SwiftWebCrawler 库。

使用方法

单个 URL 爬取

以下示例展示了如何在您的应用中爬取单个 URL 并获取其文本内容。

import SwiftWebCrawler

// 创建日志记录器
let logger = ConsoleLogger()

// 创建爬虫配置
let config = CrawlerConfiguration(
    obeyRobotsTxt: true, // 启用 robots.txt 遵守
    userAgent: "MyAppCrawler/1.0", // 自定义 User-Agent
    minRequestInterval: 1.0, // 每个主机的最小请求间隔(秒)
    proxy: nil, // 设置代理(可选)
    maxConcurrentTasks: 3 // 最大并发任务数
)

// 初始化爬虫
let crawler = WebCrawler(configuration: config, logger: logger)

// 爬取单个 URL
Task {
    let url = "https://www.example.com"
    let result = await crawler.crawl(urlString: url)
    switch result {
    case .success(let text):
        print("Crawling succeeded for \(url). Text length: \(text.count)")
    case .failure(let error):
        print("Crawling failed for \(url). Error: \(error.description)")
    }
}

批量 URL 爬取

以下示例展示了如何同时爬取多个 URL,并获取每个 URL 的爬取结果。

import SwiftWebCrawler

// 创建日志记录器
let logger = ConsoleLogger()

// 创建爬虫配置
let config = CrawlerConfiguration(
    obeyRobotsTxt: true, // 启用 robots.txt 遵守
    userAgent: "MyAppCrawler/1.0", // 自定义 User-Agent
    minRequestInterval: 1.0, // 每个主机的最小请求间隔(秒)
    proxy: nil, // 设置代理(可选)
    maxConcurrentTasks: 3 // 最大并发任务数
)

// 初始化爬虫
let crawler = WebCrawler(configuration: config, logger: logger)

// 爬取多个 URL
Task {
    let urls = [
        "https://www.apple.com",
        "https://www.google.com",
        "https://www.invalidurltest12345.com", // 无效 URL 示例
        "https://www.wikipedia.org"
    ]
    let results = await crawler.crawlBatch(urls: urls)
    for (url, result) in results {
        switch result {
        case .success(let text):
            print("Crawling succeeded for \(url). Text length: \(text.count)")
        case .failure(let error):
            print("Crawling failed for \(url). Error: \(error.description)")
        }
    }
}

配置选项

CrawlerConfiguration

CrawlerConfiguration 结构体允许您自定义爬虫的行为。以下是可配置的参数:

public struct CrawlerConfiguration {
    /// 是否遵守 robots.txt 规则
    public var obeyRobotsTxt: Bool

    /// HTTP 请求中使用的 User-Agent 字符串
    public var userAgent: String

    /// 对同一主机的最小请求间隔(秒)
    public var minRequestInterval: TimeInterval

    /// 可选的代理配置
    public var proxy: ProxyConfiguration?
    
    /// 最大并发爬取任务数
    public var maxConcurrentTasks: Int

    /// 初始化 CrawlerConfiguration
    public init(
        obeyRobotsTxt: Bool = true,
        userAgent: String = "SwiftWebCrawler/1.0",
        minRequestInterval: TimeInterval = 2.0,
        proxy: ProxyConfiguration? = nil,
        maxConcurrentTasks: Int = 5
    ) {
        self.obeyRobotsTxt = obeyRobotsTxt
        self.userAgent = userAgent
        self.minRequestInterval = minRequestInterval
        self.proxy = proxy
        self.maxConcurrentTasks = maxConcurrentTasks
    }
}

ProxyConfiguration

ProxyConfiguration 结构体允许您配置爬虫使用的代理服务器,包括代理类型、主机、端口以及可选的认证信息。

public enum ProxyType {
    case http
    case https
    case socks
}

public struct ProxyConfiguration {
    public var type: ProxyType
    public var host: String
    public var port: Int
    public var username: String?
    public var password: String?
    
    /// 初始化 ProxyConfiguration
    public init(
        type: ProxyType = .http,
        host: String,
        port: Int,
        username: String? = nil,
        password: String? = nil
    ) {
        self.type = type
        self.host = host
        self.port = port
        self.username = username
        self.password = password
    }
}

示例

let proxyConfig = ProxyConfiguration(
    type: .http,
    host: "127.0.0.1",
    port: 8080,
    username: "proxyUser",
    password: "proxyPass"
)

let config = CrawlerConfiguration(
    obeyRobotsTxt: true,
    userAgent: "MyAppCrawler/1.0",
    minRequestInterval: 1.0,
    proxy: proxyConfig,
    maxConcurrentTasks: 3
)

日志记录

SwiftWebCrawler 提供了一个简单的日志记录机制,帮助您监控爬虫的运行状态。默认情况下,使用 ConsoleLogger 将日志输出到控制台。

自定义日志记录器

您可以创建自己的日志记录器,遵循 Logger 协议:

public protocol Logger {
    func log(_ message: String)
}

public class ConsoleLogger: Logger {
    public init() {}
    
    public func log(_ message: String) {
        print("[SwiftWebCrawler] \(message)")
    }
}

// 示例:创建自定义日志记录器
public class FileLogger: Logger {
    private let fileURL: URL
    
    public init(fileURL: URL) {
        self.fileURL = fileURL
    }
    
    public func log(_ message: String) {
        let logMessage = "[SwiftWebCrawler] \(message)\n"
        if let data = logMessage.data(using: .utf8) {
            if FileManager.default.fileExists(atPath: fileURL.path) {
                if let handle = try? FileHandle(forWritingTo: fileURL) {
                    handle.seekToEndOfFile()
                    handle.write(data)
                    handle.closeFile()
                }
            } else {
                try? data.write(to: fileURL)
            }
        }
    }
}

错误处理

SwiftWebCrawler 使用 Result 类型返回爬取结果,包含成功的文本内容或详细的错误信息。以下是 WebCrawlerError 枚举,描述了可能发生的错误类型:

public enum WebCrawlerError: Error, CustomStringConvertible, Equatable {
    case invalidURL
    case requestFailed(statusCode: Int)
    case decodingFailed
    case parsingFailed
    case robotsTxtDisallowed
    case robotsTxtFetchFailed
    case robotsTxtParsingFailed
    case unsupportedURLScheme

    public var description: String {
        switch self {
        case .invalidURL:
            return "Invalid URL."
        case .requestFailed(let statusCode):
            return "Request failed with status code: \(statusCode)."
        case .decodingFailed:
            return "Failed to decode data."
        case .parsingFailed:
            return "Failed to parse HTML."
        case .robotsTxtDisallowed:
            return "Crawling disallowed by robots.txt."
        case .robotsTxtFetchFailed:
            return "Failed to fetch robots.txt."
        case .robotsTxtParsingFailed:
            return "Failed to parse robots.txt."
        case .unsupportedURLScheme:
            return "Unsupported URL scheme."
        }
    }
}

使用示例

let result = await crawler.crawl(urlString: url)
switch result {
case .success(let text):
    print("Crawling succeeded. Text length: \(text.count)")
case .failure(let error):
    print("Crawling failed. Error: \(error.description)")
}

示例代码

以下示例展示了如何在 macOS 和 iOS 应用中集成和使用 SwiftWebCrawler 库。

macOS 应用

import SwiftWebCrawler

// 创建代理配置(可选)
let proxyConfig = ProxyConfiguration(
    type: .http,
    host: "127.0.0.1",
    port: 8080,
    username: "proxyUser",
    password: "proxyPass"
)

// 创建日志记录器
let logger = ConsoleLogger()

// 创建爬虫配置
let config = CrawlerConfiguration(
    obeyRobotsTxt: true, // 启用 robots.txt 遵守
    userAgent: "MyMacAppCrawler/1.0", // 自定义 User-Agent
    minRequestInterval: 1.0, // 每个主机的最小请求间隔(秒)
    proxy: proxyConfig, // 设置代理(可选)
    maxConcurrentTasks: 3 // 最大并发任务数
)

// 初始化爬虫
let crawler = WebCrawler(configuration: config, logger: logger)

// 爬取单个 URL
Task {
    let url = "https://www.example.com"
    let result = await crawler.crawl(urlString: url)
    switch result {
    case .success(let text):
        print("Crawling succeeded for \(url). Text length: \(text.count)")
    case .failure(let error):
        print("Crawling failed for \(url). Error: \(error.description)")
    }
}

// 爬取多个 URL
Task {
    let urls = [
        "https://www.apple.com",
        "https://www.google.com",
        "https://www.invalidurltest12345.com", // 无效 URL 示例
        "https://www.wikipedia.org"
    ]
    let results = await crawler.crawlBatch(urls: urls)
    for (url, result) in results {
        switch result {
        case .success(let text):
            print("Crawling succeeded for \(url). Text length: \(text.count)")
        case .failure(let error):
            print("Crawling failed for \(url). Error: \(error.description)")
        }
    }
}

iOS 应用

import SwiftWebCrawler

// 创建代理配置(可选)
let proxyConfig = ProxyConfiguration(
    type: .http,
    host: "127.0.0.1",
    port: 8080,
    username: "proxyUser",
    password: "proxyPass"
)

// 创建日志记录器
let logger = ConsoleLogger()

// 创建爬虫配置
let config = CrawlerConfiguration(
    obeyRobotsTxt: true, // 启用 robots.txt 遵守
    userAgent: "MyiOSAppCrawler/1.0", // 自定义 User-Agent
    minRequestInterval: 1.0, // 每个主机的最小请求间隔(秒)
    proxy: proxyConfig, // 设置代理(可选)
    maxConcurrentTasks: 3 // 最大并发任务数
)

// 初始化爬虫
let crawler = WebCrawler(configuration: config, logger: logger)

// 爬取单个 URL
Task {
    let url = "https://www.example.com"
    let result = await crawler.crawl(urlString: url)
    switch result {
    case .success(let text):
        print("Crawling succeeded for \(url). Text length: \(text.count)")
    case .failure(let error):
        print("Crawling failed for \(url). Error: \(error.description)")
    }
}

// 爬取多个 URL
Task {
    let urls = [
        "https://www.apple.com",
        "https://www.google.com",
        "https://www.invalidurltest12345.com", // 无效 URL 示例
        "https://www.wikipedia.org"
    ]
    let results = await crawler.crawlBatch(urls: urls)
    for (url, result) in results {
        switch result {
        case .success(let text):
            print("Crawling succeeded for \(url). Text length: \(text.count)")
        case .failure(let error):
            print("Crawling failed for \(url). Error: \(error.description)")
        }
    }
}

注意事项

  • 异步任务:在 iOS 应用中,请确保在适当的生命周期方法中启动异步任务,例如在视图控制器的 viewDidLoad 中。
  • 后台模式:如果需要在应用进入后台时继续爬取,可以考虑配置后台任务,但这需要额外的处理。

测试

SwiftWebCrawler 提供了全面的单元测试,确保库的稳定性和可靠性。您可以运行以下命令来执行测试:

swift test

测试覆盖

  • 单个 URL 爬取成功:确保爬取有效 URL 能够成功获取文本内容。
  • 单个 URL 爬取失败:确保爬取无效 URL 能够正确返回错误。
  • 批量 URL 爬取:测试同时爬取多个 URL,包含有效和无效的 URL,验证结果的正确性。
  • 代理配置:测试使用代理进行爬取,确保代理配置能够正确应用。

贡献

欢迎贡献代码、报告问题或提出改进建议!请遵循以下步骤:

  1. Fork 仓库
  2. 创建新分支git checkout -b feature/YourFeatureName
  3. 提交更改git commit -m 'Add some feature'
  4. 推送到分支git push origin feature/YourFeatureName
  5. 创建 Pull Request

请确保您的代码符合项目的编码规范,并通过所有测试。


许可证

SwiftWebCrawler 采用 MIT 许可证


联系方式

如有任何问题或建议,请通过以下方式与我们联系:


感谢您使用 SwiftWebCrawler!我们致力于不断改进和优化,为您的开发工作提供支持。

About

SwiftWebCrawler

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages