Back to Pansou

PanTa 搜索插件设计文档

plugin/panta/panta插件设计文档.md

2.015.9 KB
Original Source

PanTa 搜索插件设计文档

1. 概述

PanTa搜索插件是一个用于从91panta.cn网站搜索并提取网盘链接的Go语言插件。该插件能够智能识别多种网盘链接类型,并自动关联提取码,是一个高性能、高可靠性的网络爬虫实现。

2. 架构设计

2.1 整体架构

插件采用模块化设计,主要包含以下几个核心部分:

  1. 插件初始化与注册:在程序启动时初始化插件并注册到全局插件管理器
  2. HTTP客户端管理:优化的HTTP客户端配置,支持连接池、重试机制和自适应并发控制
  3. 搜索执行模块:负责构建搜索请求并获取搜索结果页面
  4. 结果解析模块:解析HTML页面,提取搜索结果信息
  5. 链接提取模块:从文本中识别并提取各类网盘链接
  6. 提取码关联模块:智能关联链接与提取码
  7. 缓存管理模块:多级缓存机制,提高性能并减少重复计算

2.2 数据流

  1. 用户输入关键词 → 构建搜索请求 → 发送HTTP请求
  2. 接收HTML响应 → 解析搜索结果列表 → 并发处理每个搜索结果
  3. 提取链接和提取码 → 智能关联 → 返回最终结果

3. 关键组件详解

3.1 全局变量与常量

3.1.1 预编译正则表达式

go
// 预编译的正则表达式
var (
    topicIDRegex = regexp.MustCompile(`topicId=(\d+)`)
    yearRegex = regexp.MustCompile(`\(([0-9]{4})\)`)
    postTimeRegex = regexp.MustCompile(`发表时间:(.+)`)
    pwdParamRegex = regexp.MustCompile(`[?&]pwd=([0-9a-zA-Z]+)`)
    pwdPatterns = []*regexp.Regexp{...}
    netDiskPatterns = []*regexp.Regexp{...}
)

设计思想

  • 将正则表达式预编译为全局变量,避免每次使用时重新编译,显著提高性能
  • 模式化管理不同类型的正则表达式,便于维护和扩展

3.1.2 缓存系统

go
var (
    isNetDiskLinkCache     = sync.Map{}
    determineLinkTypeCache = sync.Map{}
    extractPasswordCache   = sync.Map{}
    topicIDCache = sync.Map{}
    postTimeCache = sync.Map{}
    yearCache = sync.Map{}
    linkExtractCache = sync.Map{}
    threadLinksCache = sync.Map{}
)

设计思想

  • 使用sync.Map实现线程安全的缓存,避免频繁的重复计算
  • 分层缓存设计,针对不同类型的操作设置独立缓存
  • 定期清理机制防止内存泄漏

3.1.3 常量配置

go
const (
    pluginName = "panta"
    searchURLTemplate = "https://www.91panta.cn/search?keyword=%s"
    threadURLTemplate = "https://www.91panta.cn/thread?topicId=%s"
    defaultPriority = 2
    defaultTimeout = 10
    defaultConcurrency = 30
    // ... 其他配置常量
)

设计思想

  • 使用常量集中管理配置参数,提高可维护性
  • 参数化设计,便于调整和优化

3.2 插件结构体

go
type PantaPlugin struct {
    client *http.Client
    maxConcurrency int
    currentConcurrency int
    responseTimes []time.Duration
    responseTimesMutex sync.Mutex
    lastAdjustTime time.Time
}

设计思想

  • 封装HTTP客户端,统一管理网络请求
  • 包含自适应并发控制相关字段,实现动态调整并发数
  • 使用互斥锁保护共享数据,确保线程安全

4. 核心功能实现

4.1 插件初始化

go
func init() {
    plugin.RegisterGlobalPlugin(NewPantaPlugin())
}

func NewPantaPlugin() *PantaPlugin {
    // 创建优化的HTTP传输层
    transport := &http.Transport{...}
    
    // 创建HTTP客户端
    client := &http.Client{...}
    
    // 启动定期清理缓存的goroutine
    go startCacheCleaner()
    
    // 创建插件实例
    plugin := &PantaPlugin{...}
    
    // 启动自适应并发控制
    go plugin.startConcurrencyAdjuster()
    
    return plugin
}

设计思想

  • 自动注册机制,确保插件在程序启动时自动加载
  • 优化的HTTP客户端配置,包括连接池、超时设置和HTTP/2支持
  • 后台任务管理,包括缓存清理和并发调整
  • 关注点分离,初始化逻辑独立封装

4.2 搜索执行

go
func (p *PantaPlugin) Search(keyword string) ([]model.SearchResult, error) {
    // 对关键词进行URL编码
    encodedKeyword := url.QueryEscape(keyword)
    
    // 构建搜索URL
    searchURL := fmt.Sprintf(searchURLTemplate, encodedKeyword)
    
    // 创建请求
    req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil)
    // ... 设置请求头
    
    // 使用带重试的请求方法发送HTTP请求
    resp, err := p.doRequestWithRetry(req)
    // ... 处理响应
    
    // 解析搜索结果
    return p.parseSearchResults(doc)
}

设计思想

  • 参数验证和处理,确保输入安全
  • 上下文管理,支持超时控制
  • 请求头优化,模拟真实浏览器行为
  • 错误处理和资源管理,确保资源正确释放
  • 重试机制,提高请求可靠性

4.3 搜索结果解析

go
func (p *PantaPlugin) parseSearchResults(doc *goquery.Document) ([]model.SearchResult, error) {
    // ... 初始化变量
    
    // 创建信号量控制并发数
    semaphore := make(chan struct{}, p.currentConcurrency)
    
    // 创建结果通道和错误通道
    resultChan := make(chan model.SearchResult, 100)
    errorChan := make(chan error, 100)
    
    // 预先收集所有需要处理的话题项
    var topicItems []*goquery.Selection
    // ... 收集话题项
    
    // 批量处理所有话题项
    for i, s := range topicItems {
        wg.Add(1)
        
        // 为每个话题创建一个goroutine
        go func(index int, s *goquery.Selection) {
            // ... 处理单个话题项
            // 提取话题ID、标题、摘要、发布时间等
            // 提取链接
            // 将结果发送到结果通道
        }(i, s)
    }
    
    // 等待所有goroutine完成
    // 收集结果
    // 调整并发数
    
    return results, nil
}

设计思想

  • 并发处理模型,提高解析效率
  • 信号量控制并发数,避免资源过度消耗
  • 通道通信模式,安全地收集并发结果
  • 自适应并发控制,根据处理时间动态调整并发数
  • 批处理优化,减少重复操作

4.4 链接提取

go
func (p *PantaPlugin) extractLinksFromElement(s *goquery.Selection, yearFromTitle string) []model.Link {
    // 创建缓存键
    // 检查缓存
    
    // 批量处理所有链接
    // 提取链接类型和提取码
    // 根据链接类型进行特殊处理
    
    // 缓存结果
    return links
}

设计思想

  • 缓存机制,避免重复提取
  • 批量处理,减少DOM遍历次数
  • 链接去重,确保结果唯一性
  • 上下文感知,考虑链接周围文本信息

4.5 帖子详情获取

go
func (p *PantaPlugin) fetchThreadLinks(topicID string) ([]model.Link, error) {
    // 检查缓存
    // 构建请求
    // 发送请求并处理响应
    // 解析HTML
    // 提取链接和提取码
    // 缓存结果
    return links, nil
}

设计思想

  • 缓存优先,减少网络请求
  • 重试机制,处理临时网络问题
  • 深度提取,获取详细内容
  • 资源管理,确保连接正确关闭

4.6 文本链接提取

go
func extractTextLinks(text string, yearFromTitle string) []model.Link {
    // 快速过滤
    // 并发提取链接和提取码
    // 智能关联算法
    // 特殊处理不同类型的网盘链接
    return links
}

设计思想

  • 预过滤机制,快速排除不包含目标内容的文本
  • 并发处理,加速大文本处理
  • 评分系统,智能关联链接与提取码
  • 特殊情况处理,针对不同网盘类型采用不同策略

4.7 提取码提取

go
func extractPassword(content string, url string) string {
    // 缓存检查
    // URL参数提取
    // 关键词检查
    // 正则匹配
    // 缓存结果
    return password
}

设计思想

  • 多级提取策略,优先从URL参数提取
  • 关键词预检查,避免不必要的正则匹配
  • 缓存结果,提高重复调用性能

4.8 链接类型判断

go
func determineLinkType(url string) string {
    // 缓存检查
    // 域名匹配
    // 缓存结果
    return linkType
}

设计思想

  • 缓存机制,减少重复判断
  • 简单高效的字符串匹配,避免复杂正则

4.9 网盘链接判断

go
func isNetDiskLink(url string) bool {
    // 缓存检查
    // 域名快速匹配
    // 缓存结果
    return isNetDisk
}

设计思想

  • 快速过滤,使用预定义域名列表
  • 缓存结果,提高性能

5. 高级特性

5.1 自适应并发控制

go
func (p *PantaPlugin) startConcurrencyAdjuster() {
    ticker := time.NewTicker(concurrencyAdjustInterval * time.Second)
    defer ticker.Stop()
    
    for range ticker.C {
        p.adjustConcurrency()
    }
}

func (p *PantaPlugin) adjustConcurrency() {
    // 计算平均响应时间
    // 根据响应时间调整并发数
}

func (p *PantaPlugin) recordResponseTime(d time.Duration) {
    // 记录响应时间
}

设计思想

  • 自适应算法,根据系统响应动态调整
  • 定时器触发,避免频繁调整
  • 样本收集,基于历史数据做出决策
  • 边界控制,确保并发数在合理范围内

5.2 HTTP请求重试机制

go
func (p *PantaPlugin) doRequestWithRetry(req *http.Request) (*http.Response, error) {
    // 重试循环
    // 指数退避算法
    // 响应时间记录
    return resp, err
}

设计思想

  • 指数退避算法,避免对服务器造成压力
  • 智能判断可重试错误,避免无效重试
  • 请求克隆,确保每次重试使用新的请求对象
  • 响应时间监控,用于并发控制

5.3 缓存清理机制

go
func startCacheCleaner() {
    ticker := time.NewTicker(1 * time.Hour)
    defer ticker.Stop()
    
    for range ticker.C {
        // 清空所有缓存
    }
}

设计思想

  • 定期清理,防止内存泄漏
  • 全面清理,确保所有缓存都得到处理
  • 后台执行,不影响主业务流程

6. 智能算法

6.1 链接与提取码关联算法

go
// 为每个链接找到最合适的提取码
// 使用评分系统选择最佳匹配

设计思想

  • 评分系统,考虑多种因素:
    • 距离:链接与提取码在文本中的距离
    • 位置关系:提取码是否在链接之后
    • 关键词:提取码周围是否有关联关键词
    • 链接类型:不同网盘对提取码的要求不同
  • 候选筛选,为每个链接收集多个可能的提取码
  • 排序选择,选择得分最高的提取码

6.2 网盘链接识别算法

go
// 使用正则表达式和域名列表识别不同类型的网盘链接

设计思想

  • 多级识别:
    1. 快速过滤:使用域名列表快速判断是否可能是网盘链接
    2. 精确匹配:使用正则表达式确认链接格式
  • 类型区分:根据不同网盘的URL特征区分链接类型
  • 缓存优化:缓存判断结果避免重复计算

7. 性能优化

7.1 HTTP连接池优化

go
transport := &http.Transport{
    MaxIdleConns:          200,
    IdleConnTimeout:       120 * time.Second,
    MaxIdleConnsPerHost:   50,
    MaxConnsPerHost:       100,
    DisableKeepAlives:     false,
    ForceAttemptHTTP2:     true,
    WriteBufferSize:       16 * 1024,
    ReadBufferSize:        16 * 1024,
    // ... 其他配置
}

设计思想

  • 连接复用,减少TCP连接建立开销
  • 参数调优,根据实际负载调整连接池大小
  • HTTP/2支持,利用多路复用提高性能
  • 缓冲区优化,减少系统调用

7.2 并发处理优化

go
// 使用信号量控制并发数
// 使用goroutine并行处理多个任务
// 使用通道安全地收集结果

设计思想

  • 并发粒度控制,选择合适的并发单位
  • 资源限制,使用信号量避免过度并发
  • 动态调整,根据系统响应调整并发数
  • 结果收集,使用通道安全地汇总结果

7.3 缓存机制

go
// 多级缓存系统
// 定期清理机制

设计思想

  • 分层缓存,针对不同操作设置独立缓存
  • 线程安全,使用sync.Map确保并发安全
  • 缓存键设计,确保唯一性和高效查找
  • 生命周期管理,定期清理避免内存泄漏

7.4 正则表达式优化

go
// 预编译正则表达式
// 减少回溯的正则模式

设计思想

  • 预编译,避免运行时编译开销
  • 模式优化,减少回溯提高匹配效率
  • 快速过滤,在使用正则前先进行简单字符串检查

7.5 批处理优化

go
// 预先收集所有需要处理的项
// 批量处理减少重复操作

设计思想

  • 减少重复扫描,一次收集多次使用
  • 批量处理,减少循环开销
  • 预处理过滤,减少需要处理的数据量

8. 错误处理与容错

8.1 重试机制

go
// 指数退避重试
// 错误类型判断

设计思想

  • 区分错误类型,只对可恢复错误进行重试
  • 指数退避,避免对服务器造成压力
  • 最大重试次数限制,避免无限重试

8.2 优雅降级

go
// 当无法获取详细信息时使用已有信息
// 当主要数据源失败时尝试备用方法

设计思想

  • 多级提取,主要方法失败时尝试备用方法
  • 部分结果返回,即使不完整也返回有用信息
  • 错误隔离,单个结果处理失败不影响整体

9. 可扩展性设计

9.1 插件接口

go
// 实现SearchPlugin接口
var _ plugin.SearchPlugin = (*PantaPlugin)(nil)

设计思想

  • 接口设计,确保插件符合统一标准
  • 静态类型检查,编译时验证接口实现

9.2 模块化结构

go
// 功能分解为独立函数
// 关注点分离

设计思想

  • 单一职责原则,每个函数只负责一个功能
  • 模块化设计,便于维护和扩展
  • 依赖注入,减少组件间耦合

9.3 配置参数化

go
// 使用常量定义配置参数
// 支持动态调整部分参数

设计思想

  • 参数集中管理,提高可维护性
  • 运行时调整,支持动态优化
  • 默认值设计,确保系统在各种情况下都能正常工作

10. 总结与最佳实践

10.1 性能优化最佳实践

  1. 预编译正则表达式:避免运行时编译开销
  2. 多级缓存:减少重复计算和网络请求
  3. 并发处理:利用多核提高处理效率
  4. 连接池优化:减少网络连接开销
  5. 批处理:减少重复操作和循环开销

10.2 可靠性保障最佳实践

  1. 重试机制:处理临时网络问题
  2. 错误处理:全面的错误捕获和处理
  3. 资源管理:确保资源正确释放
  4. 超时控制:避免请求无限等待
  5. 并发控制:避免资源过度消耗

10.3 代码质量最佳实践

  1. 接口设计:清晰定义组件职责
  2. 单一职责:每个函数只做一件事
  3. 注释文档:详细说明函数功能和参数
  4. 错误传播:合理传递和包装错误信息
  5. 命名规范:使用有意义的变量和函数名

11. 附录

11.1 支持的网盘类型

  • 移动云盘 (mobile)
  • 百度网盘 (baidu)
  • 夸克网盘 (quark)
  • 阿里云盘 (aliyun)
  • 迅雷网盘 (xunlei)
  • 天翼云盘 (tianyi)
  • 115网盘 (115)
  • PikPak网盘 (pikpak)

11.2 关键依赖

  • github.com/PuerkitoBio/goquery:HTML解析
  • net/http:网络请求
  • regexp:正则表达式处理
  • sync:并发控制

11.3 常见问题与解决方案

  1. 问题:提取码关联不准确 解决方案:使用评分系统考虑多种因素,提高关联准确性

  2. 问题:网络请求超时 解决方案:实现重试机制和超时控制

  3. 问题:内存使用过高 解决方案:定期清理缓存,控制并发数量

  4. 问题:CPU使用率过高 解决方案:优化正则表达式,减少不必要的计算

  5. 问题:某些网盘链接识别失败 解决方案:持续更新网盘链接模式,支持新的网盘类型