BISMIH / 接口学习

PIXIV / 每周榜单

从公开榜单到频道发布

读懂榜单请求、图片校验、Telegram 发布和按期去重。当前采集路线无需 Pixiv Cookie,也无需 Pixiv OAuth。

接口边界

本项目目前由青龙调用 Python 脚本,未部署 Pixiv HTTP 网关。本文介绍现有脚本入口及它调用的上游接口;不存在可直接向 Item 请求的 /pixiv API。

先做一次只读请求

在自己的终端读取公开周榜,先查看字段;这条命令不会发送频道消息。

curl --fail --get 'https://www.pixiv.net/ranking.php' \
  -H 'User-Agent: Mozilla/5.0' \
  --data-urlencode 'mode=weekly' \
  --data-urlencode 'content=illust' \
  --data-urlencode 'format=json' \
  --data-urlencode 'p=1'

上游可能拒绝匿名访问或返回验证页面。此时记录失败并停止,不把 HTML 当成榜单,不轮换 IP 或反复重试。

1. 榜单与作品首图

GEThttps://www.pixiv.net/ranking.php只读 · 不带登录 Cookie
查询参数当前值用途
modeweekly周榜。
contentillust插画榜。
formatjsonJSON 响应。
p1第一页,当前实现只处理前 50 项。

下列内容是字段示例,编号与图片地址均为演示占位,不是可用的下载样本:

{
  "date": "20261024",
  "date_range_text": "示例周榜范围",
  "contents": [{
    "illust_id": 123456789, "rank": 1,
    "title": "示例作品", "user_name": "示例作者", "user_id": 42,
    "url": "https://i.pximg.net/example-preview.jpg",
    "is_masked": false, "tags": ["original"],
    "illust_content_type": {"sexual": 0, "grotesque": 0, "violent": 0}
  }]
}

date 由平台返回,用于本期去重;不等同于任务执行当天。过滤遮罩、成人标签、性内容、血腥与暴力标记后,为每个作品保留作者、排名和原作链接。

GEThttps://www.pixiv.net/ajax/illust/{illust_id}/pages只读 · 首图来源查询

路径参数 illust_id 为榜单中的数字作品编号。仅当响应 error:false 且 body 非空时,尝试第一项的 urls.original:

{
  "error": false,
  "body": [{"urls": {"original": "https://i.pximg.net/example-original.jpg"}}]
}

公开原图不可访问、过大或下载校验失败时,才回退该作品的榜单预览,并标记 quality: ranking_preview;不能将预览称为原图。原图成功时标记 original_first_page。每作只取第一张,不遍历全部多图内容。

2. 图片请求与内容校验

GET上一步返回的 HTTPS 图片 URL只读 · 二进制图片

图片请求带 Referer: https://www.pixiv.net/;只允许 i.pximg.net 和 i-cf.pximg.net,不跟随跳转。fetch_image() 检查 HTTP 状态、图片 Content-Type、9 MiB 大小上限及 Pillow 解码,再保存 SHA-256。

频道上传前单独适配显示尺寸;本地采集源文件保留。Telegram 展示图可能被压缩,不能作为无损归档的证明。请求失败时保留真实错误,不发布空榜单。

3. Telegram 频道发布

这是会发送消息的写操作。服务器读取私有 Bot Token 和频道配置;机器人必须在目标频道拥有发帖权限。本文没有在线调用按钮或凭据输入框。

POSThttps://api.telegram.org/bot<TOKEN>/sendMediaGroup写入 · multipart/form-data
字段类型本项目用法
chat_id整数已有私有频道标识,从私有配置读取。
mediaJSON 字符串2–10 个 photo 对象,含 type、media 和 caption。
photo0…文件由 attach://photo0 等引用对应上传文件。
media = [
  {"type": "photo", "media": "attach://photo0",
   "caption": "#1 · 示例作品\n作者:示例作者\n原作链接 · 版权归原作者"},
  {"type": "photo", "media": "attach://photo1", "caption": "#2 · 示例作品"}
]

不足两张的最后一批使用 sendPhoto,参数为 chat_id、caption 和文件字段 photo。成功返回的 message_id 按批保存。每批最多 10 张;批间等待 2 秒。

发送前,先按 weekly-illust-{date}:tg:{offset} 保存 pending 预留;收到明确成功回执后改为 sent。已有 sent 批次跳过。POST 超时或结果不明时保留 pending,等待核对;仅平台明确拒绝时释放该条预留。不要因超时清空所有记录或重发整期。

Token 位于 Bot API 路径中,请勿记录原始请求 URL。参数规则参考 Telegram 官方 sendMediaGroup 文档;本项目的 9 MiB 限制是自身限制。

4. 当前项目的调用入口

下面的命令会实际发布。它应在已部署的青龙容器内由管理员执行;学习参数时使用前面的只读请求即可。

python3 /ql/data/scripts/bismih-job.py pixiv

包装器调用 worker.py pixiv,正常执行前最多随机等待 90 秒。生产每周一北京时间 09:10 运行,不需要 Codex 或浏览器保持打开。

脚本接口职责
collect()返回 (issue_key, issue),保存本期作品与图片摘要。
pixiv()发布未完成批次;返回 (summary, metrics),交由工作引擎落库。
publisher_health.py --preflight管理员维护入口:检查来源、现有频道权限和 B站会话;不会发布榜单。
publisher_health.py只核对本机记录和调度,写脱敏状态快照。

一次成功的工作引擎 stdout 示意:

{
  "job": "pixiv", "state": "success",
  "summary": "周榜图片已发布到专用私有频道;按榜单日期去重",
  "metrics": {"issue": "20261024", "images": 37, "excluded": 13, "originals": 33}
}

这是命令行输出,不是本项目的 HTTP 响应。工作引擎正常完成返回退出码 0,已记录的任务失败返回 1。若同一任务锁已被占用,本次会退出且不重复执行。运行记录、图片和发布回执继续存放在服务器私有目录。

5. 错误处理与状态灯

结果处理
HTTP 401 / 403 / 412、非 JSON 验证页停止读取并记录 needs_auth,不冒充成功。
图片原图不可用尝试同一榜单项的预览,保留来源标记;预览也失败则整轮失败。
Telegram 明确拒绝记录 rejected;只释放被拒绝的批次。
发送结果不明needs_review,保留预留,不盲目重发。

每日 07:40 检查公开榜单与频道发布权限,每 5 分钟只读本机数据库,核对青龙运行、任务启用和正确周计划。绿灯还要求最近应执行的周任务有真实成功记录;每日预检超过 36 小时、应执行任务逾期或新一轮失败会转红。周计划预留两小时完成窗口,心跳不能把失败刷成成功。

Pixiv 网页 Cookie PHPSESSID 与 App API 的 OAuth refresh_token 是不同认证体系。PixivPy 的 OAuth 实现可用于学习登录型 App API;当前公开榜单路线没有引入它。

源码对照:worker.py 中的 collect、fetch_image、telegram_photo、pixiv,以及 publisher_health.py。当前实现基线实测于 2026-10-26;上游字段与访问政策可能变化。