跳转至

Spitolas — 基于浏览器的网络爬虫

Spitolas 是一个状态机驱动的网络爬虫,它驱动真实的 Chromium 浏览器,通过类似用户的操作(点击、表单填写、iframe 遍历)来发现应用状态。所有产生的 HTTP 流量在 CDP 层面被捕获,并输入到 vigolium 的扫描管道中。

工作原理

RunSpider(config, recordSaver)
┌─────────────────────────────────────────────────┐
│  浏览器池(通过 rod 控制 Chromium)              │
│  ├── CDP 网络捕获(所有标签页/iframe)           │
│  └── 自动对话框处理(alert/confirm/prompt)      │
└────────────────────┬────────────────────────────┘
                     │ 导航到目标 URL
┌─────────────────────────────────────────────────┐
│  捕获初始状态(DOM 快照 + SHA256 ID)            │
└────────────────────┬────────────────────────────┘
┌─────────────────────────────────────────────────┐
│  爬取循环                                       │
│  1. 选取下一个状态(候选 + 优先级)              │
│  2. 重置浏览器 → 导航到初始状态                  │
│  3. 重放到达目标状态的最短路径                    │
│  4. 触发未触发的动作(点击/悬停/提交)            │
│  5. 捕获新 DOM → 比较 → 添加状态/边              │
│  6. 重复直到满足终止条件                          │
└────────────────────┬────────────────────────────┘
┌─────────────────────────────────────────────────┐
│  网络写入器                                     │
│  CDP 事件 → httpmsg.HttpRequestResponse         │
│  → RecordSaver(数据库)→ 扫描管道               │
└─────────────────────────────────────────────────┘

核心概念

状态与状态图

状态是由 SHA256(strippedDOM)[:16] 标识的 DOM 快照。状态图是一个有向图,其中节点是状态,边是引起状态转换的动作。状态间的导航使用 Dijkstra 最短路径算法(备用方案为 Yen 的 K 最短路径)。

近似重复检测使用归一化 Levenshtein 距离(阈值:10%)。对于大型 DOM(超过 10K 字符),采用基于采样的距离以提高性能。

动作与候选元素

候选可点击元素通过 CSS 选择器发现(abutton[onclick][role=button]input[type=submit]、框架特定绑定如 [ng-click][v-on:click] 等)。每个候选在被触发后成为 Eventable(图边),将源状态与目标状态通过事件类型(点击、悬停、回车)连接起来。

片段(视觉页面分割)

页面被分解为片段——由 XPath、边界框、子树大小和内容哈希标识的 DOM 区域。两种模式:

  • Landmark(默认):基于 DOM 的快速提取
  • VIPS:基于视觉的页面分割,使用多轮递减阈值

片段比较使用 APTED 树编辑距离算法。片段管理器跟踪重复/等效片段上的探索状态,并使用候选影响力评分来决定下一步探索哪些状态。

表单处理

表单处理器检测并填写表单,使用智能值生成:

  • 字段名称感知的值(电子邮件、密码、电话、URL 等)
  • 约束感知生成(遵循 patternmin/maxminlength/maxlength
  • 当一次性填写所有输入失败时,使用成对回退
  • 支持文件上传,具有类型感知的文件选择

探索策略

策略 描述
默认(BFS/DFS) 基于片段优先级的确定性遍历
adaptive Exp3.1 多臂老虎机——通过重要性加权概率采样平衡利用(已知良好动作)与探索(未尝试动作)。奖励基于新状态发现。

浏览器管理

  • 内嵌二进制文件:附带 Chromium(macOS/Windows/Linux)和 ungoogled-Chromium(Linux)。首次运行时解压,按版本缓存。
  • 无头模式:加载扩展时使用 headless=new(支持 Chrome 扩展,不同于旧版无头模式)。
  • 扩展:通过 --load-extension 加载(例如,爬取时使用 uBlock Origin Lite 进行广告拦截)。
  • 为爬取禁用安全标志--disable-web-security--ignore-certificate-errors--allow-running-insecure-content
  • :多个浏览器实例,采用轮询选择。

网络捕获

流量在浏览器层面(而非页面层面)通过 CDP 事件捕获,覆盖所有标签页、弹出窗口和 iframe:

  1. NetworkRequestWillBeSent → 记录请求
  2. NetworkResponseReceived → 记录响应头
  3. NetworkLoadingFinished → 获取响应体

基于哈希的去重防止重复记录。清理循环移除过期的待处理请求(超过 15 秒)。捕获的流量转换为 httpmsg.HttpRequestResponse,并通过 RecordSaver 接口保存,来源标记为 "spidering"

终止条件

当满足以下任一条件时,爬取停止:

  • 发现的最大状态数
  • 最大持续时间已过
  • 达到最大爬取深度
  • 连续失败次数达到上限
  • 没有更多候选动作可探索
  • 上下文取消

入口点

result, err := spitolas.RunSpider(ctx, spitolas.SpiderConfig{
    TargetURL:    "https://example.com",
    MaxStates:    100,
    MaxDuration:  10 * time.Minute,
    MaxDepth:     5,
    BrowserCount: 1,
    CrawlStrategy: "adaptive", // 或 "" 表示默认
}, recordSaver)

返回 SpiderResult,包含:发现的状态数、执行/失败的动作数、提交的表单数、持续时间和保存的记录数。