跳转到主要内容

A-Parser 与 Redis 集成:高级 API

与 HTTP API 的对比

A-Parser Redis API 的开发旨在替代 oneRequestbulkRequest 方法,以实现更高性能的执行并支持更多的使用场景:

  • 使用 Redis 作为请求和结果的服务器
  • 支持以异步或阻塞模式请求结果
  • 支持连接多个爬虫工具(无论是在同一台还是不同服务器上)以处理具有统一入口点的请求
  • 支持设置处理请求的线程数并查看运行日志
  • 支持对操作设置超时
  • 自动 Expire(过期)未被领取的结果

论坛讨论主题

预先设置

A-Parser HTTP API 不同,要使用 Redis API,需要预先配置并运行带有 API::Server::RedisAPI::Server::Redis 爬虫工具的任务:

  • 安装并启动 Redis 服务器(本地或远程)
  • API::Server::RedisAPI::Server::Redis 爬虫工具创建预设,并指定:
    • Redis Host - Redis 服务器地址,默认为 127.0.0.1
    • Redis Port - Redis 服务器端口,默认为 6379
    • Redis Queue Key - 与 A-Parser 交换数据的键名,默认为 aparser_redis_api,您可以创建独立的队列并使用不同的任务或不同的 A-Parser 副本进行处理
    • Result Expire(TTL) - 结果的生存时间(秒),用于自动控制和删除未被领取的结果,默认为 3600 秒(1 小时)
  • 添加带有 API::Server::RedisAPI::Server::Redis 爬虫工具的任务
    • 在查询中需要指定 {num:1:N},其中 N 应对应任务中指定的线程数
    • 您还可以开启日志记录选项,这样就可以查看每个请求的日志

使用 API::Server::RedisAPI::Server::Redis 配置任务的示例

获取 API 请求

使用 docker-compose 同时启动 A-Parser 和 Redis

使用此启动方式时,Redis 服务器地址 (Redis Host) 可以使用服务名称代替 IP,在下面的示例中为 redis

如果 A-Parser 之前未通过 docker-compose 启动

  1. 下载并解压发行版(需预先从会员中心获取一次性链接,详见此处):
curl -O https://a-parser.com/members/onetime/ce42f308eaa577b5/aparser.tar.gz
tar zxf aparser.tar.gz
rm -f aparser.tar.gz
  1. 创建 docker-compose.yml 文件并填入以下内容:

    • 基础无密码且不开放端口方案,Redis 仅在 Docker 网络内部可用
    version: '3'

    services:
    a-parser:
    image: aparser/runtime:latest
    command: ./aparser
    restart: always
    volumes:
    - ./aparser:/app
    ports:
    - 9091:9091

    redis:
    image: redis:latest
    restart: always
    • 带密码且开放端口方案,Redis 将对外可用,因此强烈建议使用密码
    version: '3'

    services:
    a-parser:
    image: aparser/runtime:latest
    command: ./aparser
    restart: always
    volumes:
    - ./aparser:/app
    ports:
    - 9091:9091

    redis:
    image: redis:latest
    restart: always
    command: redis-server --requirepass 此处_REDIS_密码
    ports:
    - 6379:6379

    请将 TUT_PAROL_DLYA_REDIS 替换为您想设置的 Redis 认证密码。

  2. 启动容器:

docker compose up -d

如果 A-Parser 之前已经通过 docker-compose 启动

  1. 编辑 docker-compose.yml 文件,在末尾添加以下内容:

    • 基础无密码且不开放端口方案,Redis 仅在 Docker 网络内部可用
      redis:
    image: redis:latest
    restart: always
    • 带密码且开放端口方案,Redis 将对外可用,因此强烈建议使用密码
      redis:
    image: redis:latest
    restart: always
    command: redis-server --requirepass 此处_REDIS_密码
    ports:
    - 6379:6379

    请将 TUT_PAROL_DLYA_REDIS 替换为您想设置的 Redis 认证密码。

  2. 启动容器:

docker compose up -d
备注

如果 A-Parser 已经启动且其配置未更改,则它不会重启,Docker 只会添加并启动 Redis。

执行请求

Redis API 的工作基于 Redis Lists (列表),列表操作允许向队列添加无限数量的请求(受限于内存),并支持以阻塞模式带超时 (blpop) 或异步模式 (lpop) 获取结果。

  • useproxyproxyCheckerproxybannedcleanup 外的所有设置均取自被调用的爬虫工具预设 + overrideOpts
  • 设置 useproxyproxyCheckerproxybannedcleanup 取自 API::Server::RedisAPI::Server::Redis 预设 + overrideOpts

使用 lpush 命令将请求添加到 Redis,每个请求由一个通过 JSON 序列化的数组 [queryId, parser, preset, query, overrideOpts, apiOpts] 组成:

  • parserpresetquery 与 API 请求 oneRequest 中的对应参数一致
  • queryId - 随请求一起生成,建议使用您数据库中的序号或高质量的随机数,通过该 ID 可以获取结果
  • overrideOpts - 覆盖爬虫工具预设的设置
  • apiOpts - 额外的 API 处理参数
备注

通过 Redis 请求时会跳过结果格式化阶段,因为整个结果以 JSON 形式传输,以便后续程序化处理。

redis-cli

执行请求的示例,可以使用 redis-cli 进行测试:

127.0.0.1:6379> lpush aparser_redis_api '["some_unique_id", "Net::HTTP", "default", "https://ya.ru"]'
(integer) 1
127.0.0.1:6379> blpop aparser_redis_api:some_unique_id 0
1) "aparser_redis_api:some_unique_id"
2) "{\"data\":\"<!DOCTYPE html><html.....

各种案例

异步检查结果是否存在

lpop aparser_redis_api:some_unique_id

如果结果已处理则返回结果,如果请求仍在处理中则返回 nil

阻塞式获取结果

blpop aparser_redis_api:some_unique_id 0

该请求将被阻塞直到获取结果,您也可以指定获取结果的最大超时时间,超时后命令将返回 nil

将结果保存到统一队列

默认情况下,A-Parser 为每个请求在唯一的键 aparser_redis_api:query_id 下保存结果,这允许组织多线程处理,分别为每个线程发送请求并获取结果

在某些情况下,需要按结果产生的顺序在单线程中处理结果,此时将结果保存到统一的结果队列(键名必须与请求队列的键名不同)会更方便

为此,需要在 apiOpts 中指定 output_queue 键:

lpush aparser_redis_api '["some_unique_id", "Net::HTTP", "default", "https://ya.ru", {}, {"output_queue": "aparser_results"}]'

从公共队列获取结果:

127.0.0.1:6379> blpop aparser_results 0
1) "aparser_results"
2) "{\"queryId\":\"some_unique_id\",\"results\":{\"data\":\"<!DOCTYPE html><html class=...

实现示例(SpySERP 案例)

假设我们正在创建一个评估域名参数的 SaaS 服务,为简单起见,我们将检查域名的注册日期

我们的服务由 2 个页面组成:

  • /index.php - 落地页,包含域名输入表单
  • /results.php?domain=google.com - 服务运行结果页面

为了提升用户体验,我们希望服务页面能瞬间加载,而等待数据的过程看起来很自然并显示加载器(loader)

在请求 results.php 时,我们首先向 A-Parser Redis API 发送请求,并生成唯一的 request_id:

​lpush aparser_redis_api '["request-1", "Net::Whois", "default", "google.com", {}, {}]'

之后我们可以向用户展示页面并在数据展示区域显示加载器,由于没有延迟,服务器响应将仅受限于 Redis 的连接速度(通常在 10ms 以内)

A-Parser 会在用户浏览器接收到第一批内容之前就开始处理请求,当浏览器加载完所有必要的资源和脚本后,我们就可以显示结果,为此发送一个 AJAX 请求来获取数据:

/get-results.php?request_id=request-1

get-results.php 脚本对 Redis 执行一个超时为 15 秒的阻塞请求:

blpop aparser_redis_api:request-1 15

一旦从 A-Parser 获取到响应就立即返回,如果我们因超时获得了空结果,则可以向用户显示数据获取错误

通过这种方式,在第一次打开页面 (/results.php) 时向 A-Parser 发送请求,我们将用户等待数据的时间 (/get-results.php) 缩减了浏览器等待内容、加载脚本和执行 AJAX 请求所需的时间