Files
xspider/README.md
Flik a1a13aae65 feat(storage): 引入任务日志记录功能
- 新增 MongoLogRepository 类用于记录任务执行日志
- 在 FlowRunner 中集成日志记录,跟踪流程执行步骤
- 记录登录与业务流程的开始、成功、错误与中止状态
- 支持记录 XML 来源与快照(超长内容自动截断)- 扩展 README 文档说明日志字段与使用方式
-优化分页抓取逻辑,返回页面与记录统计数据
- 统一存储接口,暴露 MongoDB 客户端与数据库属性
- 增加步骤计数与执行时间统计功能
2025-10-20 22:38:13 +08:00

15 KiB
Raw Permalink Blame History

xspider 模板爬虫

基于 XML 模板驱动的浏览器自动化采集引擎,负责监听 Redis 队列获取任务模板,利用 DrissionPage 浏览器执行登录与业务流程,并将抽取结果写入 MongoDB。本项目同时集成变量服务、验证码识别及分页、下载等常见采集场景的基础能力。

功能亮点

  • 使用标准化 XML 模板描述站点、流程与字段,无需改动代码即可上线新任务。
  • Redis 队列拉取模板地址,支持多实例横向扩展。
  • 内置 Redis 变量服务,自动处理站点作用域与全局作用域的变量读写。
  • 提供可扩展的 Action 注册机制,可按需新增自定义动作。
  • 采集结果默认落地 MongoDB可按数据类型、唯一键去重。

目录结构

├── main.py                 # 应用入口,初始化日志并启动循环
├── xspider/
│   ├── app.py              # TemplateCrawlerApp协调整体流程
│   ├── runner.py           # FlowRunner调度动作与抽取逻辑
│   ├── browser.py          # 浏览器会话封装,对接 DrissionPage
│   ├── actions/            # 内置动作定义、注册
│   ├── extraction.py       # HTML 解析与字段抽取
│   ├── storage.py          # MongoDB 存储实现
│   ├── redis_queue.py      # Redis BLPOP 队列封装
│   ├── variables.py        # 变量服务访问与缓存
│   ├── xml_parser.py       # XML 模板解析
│   └── utils/              # 辅助方法(选择器判断等)

环境要求

  • Python 3.10+
  • 浏览器驱动建议使用 DrissionPage 配套环境

安装依赖示例:

pip install drissionpage redis requests pymongo lxml cssselect

如需验证码识别、自定义变量服务或下载逻辑,请在业务环境中提供对应服务。

环境变量

变量 默认值 说明
XSPIDER_REDIS_URL redis://localhost:6379/0 Redis 连接串
XSPIDER_REDIS_LIST_KEY xspider:config 待消费的模板地址所在 list
XSPIDER_REDIS_BLOCK_TIMEOUT 30 Redis BLPOP 阻塞秒数
XSPIDER_MONGO_URI mongodb://localhost:27017 MongoDB 连接串
XSPIDER_MONGO_DB xspider MongoDB 数据库名称

变量作用域说明

  • 对变量名使用 site:变量名 将强制读取/写入当前站点作用域;global:变量名 将强制使用全局作用域。
  • 无前缀的变量名会先从站点作用域(依赖 site_id)查找,未命中再自动回退到全局作用域。
  • 写入变量时默认落到当前站点作用域(若缺少 site_id 则写入全局),也可通过 var_scope="global" 强制写入全局。
  • var_ttlvar_single_use 会转换为 Redis 过期时间与一次性读取语义,所有数据均保存在 XSPIDER_REDIS_URL 指定的实例中。

启动方式

python main.py

程序将持续拉取 Redis 队列中的 XML 模板地址,下载模板后解析 site → flow → action并完成采集与存储。如需停止可使用 Ctrl+C

XML 模板结构

Schema 提示

若希望编辑器自动提示必填项与可选属性,可在 XML 顶部引入项目内置的 Schema

<site xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="../schema/xspider.xsd"
      id="example"
      base="https://example.com">
  ...
</site>
  • Schema 位于 schema/xspider.xsd,涵盖站点、配置、动作、抽取等全部节点。
  • 修改为相对路径或网络路径后,可在常见 IDEVS Code、IntelliJ、XMLSpy 等)中获得自动补全、枚举提示与校验。
  • Schema 对可扩展字段使用 anyAttribute,因此自定义参数不会报错。

顶层元素

元素 说明
<site id base?> 必填站点定义,可配置默认 base URL
<config> 站点级配置:代理、重试、请求头等
<login> 登录流程,允许缺少 extract;执行完可配置校验选择器
<flows> / <flow> 业务流程,至少包含动作与抽取配置

模板示例

<site id="example" base="https://example.com">
  <config enable_proxy="false" rotate_ua="false" retry="3">
    <header name="Accept-Language" value="zh-CN,zh;q=0.9"/>
  </config>

  <login url="https://example.com/login" selector="div.dashboard" mode="css">
    <action type="wait_dom_show" selector="form#login"/>
    <action type="type" selector="//input[@name='username']" text="${account}" mode="xpath"/>
    <action type="type" selector="//input[@name='password']" text="${password}" mode="xpath"/>
    <action type="click" selector="button.submit"/>
  </login>

  <flows>
    <flow id="orders" entry="/orders" data_type="sales" unique_keys="custom" columns="order_id">
      <action type="wait_dom_show" selector="table.data-list"/>
      <extract record_css="table.data-list tbody tr">
        <field name="order_id" selector="td:nth-child(1)" mode="css"/>
        <field name="customer" selector="td:nth-child(2)" mode="css"/>
      </extract>
      <paginate selector="button.next" mode="css" max_pages="10"/>
    </flow>
  </flows>
</site>

Action 基础属性

所有 <action> 元素都会被解析为 ActionConfig,其通用属性如下:

属性 类型 / 默认值 说明
type str(必填) 决定实际执行的动作类
selector str / None DOM 选择器;部分动作必须提供
mode xpath(默认)或 css 选择器解析模式
timeout_ms int / 10_000 等待元素的超时,单位毫秒(仅在需要等待元素/动作时使用,部分动作会忽略该值)
after_wait int / 0 执行完动作后的额外等待时间
params dict / {} 其它自定义键值对,会透传给动作

params 支持 ${变量名} 形式的变量占位符,运行时通过 VariableResolver 在站点上下文中解析。下文动作表格中出现的 params.xxx 表示 <action> 元素上的额外属性(除通用字段外),在解析时会放入 params 字典中。
通用字段包括typeselectormodetimeout_msafter_wait,这些字段会直接映射到 ActionConfig 对象,对应的属性不会出现在 params 中。

params 字段举例

解析器会把 <action> 标签上的自定义属性收入 params,例如:

<!-- url 属性不是通用字段,因此会进入 params -->
<action type="goto" url="https://example.com/dashboard"/>

<!-- download_filename 也会进入 params执行时通过 self.config.params['download_filename'] 读取 -->
<action type="click"
        selector="button.export"
        download_filename="orders.xlsx"/>

<!-- 多个参数同样会被保留在 params 中 -->
<action type="set_var"
        var_name="latest_token"
        var_value="${token}"
        var_scope="global"
        var_ttl="86400"/>

上面的三个动作在 Python 中最终对应的 ActionConfig.params 分别为:

  • {"url": "https://example.com/dashboard"}
  • {"download_filename": "orders.xlsx"}
  • {"var_name": "latest_token", "var_value": "${token}", "var_scope": "global", "var_ttl": "86400"}

动作类内部通过 self.config.params[...] 访问这些值,因此 README 中的 params.urlparams.download_filenameparams.var_ttl 等写法,正是指这些自定义属性在 params 字典里的访问方式。

内置 Action 类型

类型 必填字段 可选字段 说明
goto params.urlselector 跳转到指定 URL为空时回退到 entry_url/base_url
click selector params.button(支持 left/right/middle)、params.download_filename 点击元素,可触发下载。
type selectorparams.text 输入文本;执行前会清空原内容。
wait_dom_show selector 等待元素出现并返回元素引用。
wait_dom_gone selector 等待元素从 DOM 中移除。
wait_dom_hide selector 等待元素隐藏style 含 display: none)。
wait_time params.timeout_ms(毫秒,若缺省使用 timeout_ms 单纯 sleep 指定时间。
run_js params.scriptparams.text 在当前页面执行 JavaScript并返回结果。
set_header params.header_nameparams.header_value 为当前会话追加请求头。
set_attr selectorparams.attr_name params.attr_value(默认空字符串) 修改 DOM 属性值。
set_var params.var_nameparams.var_value params.var_scopeparams.var_ttlparams.var_single_use 将变量写入变量服务,可带过期策略。
captcha 无(若缺省会截取 selector 或整页截图) params.imageparams.captcha_typeparams.captcha_urlparams.captcha_configparams.variable 调用远程接口识别验证码并写入变量。

提示

  • selectormode 不匹配时(例如 CSS 模式中使用 XPath解析阶段会抛出异常。
  • 所有 params 字段均支持变量解析与自定义透传。
  • 若需要扩展新动作,继承 BaseAction 并设置 type_name 后自动被 ActionRegistry 注册。

SetVarAction 使用示例

SetVarAction 用于把临时生成的数据写入 Redis 变量库,并同步到当前站点上下文,供后续步骤通过 ${变量名}ctx.site_context[...] 复用。其主要参数含义如下:

参数 说明
var_name 变量名称,支持 ${...} 占位符,最终会作为变量服务的 name 字段。
var_value 变量值,可引用已存在的变量或静态字符串。
var_scope 变量作用域,可选,支持 global/site。未指定时默认写入当前站点作用域。
var_ttl 过期时间,单位秒,可选。变量服务可据此设置生命周期。
var_single_use 是否一次性变量,可选,常用于验证码、令牌等场景。

示例(写入站点作用域中的 latest_token,并设置 1 小时过期):

<action type="set_var"
        var_name="latest_token"
        var_value="${jwt_token}"
        var_ttl="3600"
        var_single_use="false"/>

动作执行步骤:

  1. 解析所有参数并进行变量替换,例如 ${site_id}${jwt_token}
  2. 根据 var_scope 与上下文自动选择站点或全局作用域,并附带 TTL/一次性读取标记写入 Redis。
  3. VariableResolver 会将值缓存在内存中,后续解析 ${latest_token}${site:latest_token} 均可命中(前者会先查站点再回退全局)。

如需写入全局变量,可设置 var_scope="global" 或直接在变量名上使用 global:token_name 前缀。

CaptchaAction 使用示例

CaptchaAction 会将验证码截图或指定的 base64 图片提交到远程接口,并把识别结果写入 Redis 变量库以及 site_context。使用方式如下:

<action type="captcha"
        selector="//img[@id='captcha']"
        mode="xpath"
        captcha_type="text"
        variable="login:captcha_code"
        captcha_config='{"url": "https://captcha.example.com/api", "timeout": 60}'/>

执行流程:

  • 若提供 params.image,会直接使用该 base64 字符串;否则优先根据 selector 截取元素截图,再退回到整页截图。
  • 请求地址取 params.captcha_urlcaptcha_config.url,最后回退到默认 https://captcha.lfei007s.workers.dev
  • 请求体默认为 {"image": "...", "type": params.captcha_type},并支持通过 captcha_config 添加额外键值(如 {"headers": {...}})。
  • 服务返回 JSON 后,会尝试读取 resulttextvaluecodedata 等字段作为识别结果。
  • 结果最终写入 Redis默认键为站点作用域下的 captcha_result,或使用 params.variable 覆盖),并同步到 site_context 供后续动作引用。

抽取与存储

  • <extract> 支持 record_css / record_xpath 指定列表元素,<field> 定义字段名称、选择器、取值模式及可选的 value_type
  • <paginate> 可配置下一页元素,并限制 max_pages
  • <excel_extract> 用于声明需下载并解析 Excel 文件,当前实现会给出提示,具体逻辑可在 FlowRunner._handle_excel_extract 自行扩展。
  • MongoRepository.save_records 会按照 flow.unique_keysunique_columns 控制去重策略。

任务日志

每次执行登录流程或业务流程都会写入 MongoDB 的 task_logs 集合,记录执行状态与关键步骤,方便排查问题。

  • 基础字段:
    • _idUUID全局唯一的任务编号。
    • site_id / flow_id:对应模板中的站点与流程。
    • statusstartsuccesserroraborted
    • started_at / ended_atUTC 时间戳,duration_ms 为执行耗时。
    • steps:执行步骤列表,包含动作类型、分页点击、抽取结果等摘要。
    • metadata:附加信息(如保存记录数量、分页页数、是否登录流程)。
    • xml_source:原始 XML 的来源地址;xml_snapshot:当前执行所用 XML超出 50k 字符会截断并标记)。
    • error / abort_reason:当流程异常或被中止时记录详细信息(包含堆栈)。
  • 日志写入策略:
    • 流程开始时插入 status=start 的文档;
    • 正常结束时更新为 success 并补充 stepssummarymetadata
    • 运行中出现异常时标记为 error 并写入堆栈;
    • 收到 KeyboardInterrupt 或其它不可恢复的中断时标记为 aborted

可以根据 site_id + flow_id + started_at 建立索引用于快速查询,也可按 status 过滤待排查的任务。

开发与调试建议

  • 日志级别默认 INFO,可自行设置环境变量 PYTHONLOGGING 或修改 configure_logging
  • 建议在本地通过伪造 XML 模板直接调用 FlowRunner.run_site,避免频繁依赖 Redis。
  • 若需要 Network/Proxy、自定义 Headers可在 <config> 中补充;所有配置都会在浏览器会话创建前应用。
  • CaptchaAction 默认使用 https://captcha.lfei007s.workers.dev,如需替换可在 params.captcha_config 传入 JSON例如 {"url": "...", "timeout": 60})。

扩展方向

  • 自定义 Action:在 xspider/actions 新增子类并继承 BaseAction,设置 type_name_execute 逻辑即可使用。
  • 变量服务实现:可基于 VariableService 接口扩展自定义存储,例如不同的 Redis 集群或 REST 服务。
  • 采集结果落地:若不使用 MongoDB可实现新的存储类并在初始化 FlowRunner 时注入。