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. 榜单与作品首图
https://www.pixiv.net/ranking.php只读 · 不带登录 Cookie| 查询参数 | 当前值 | 用途 |
|---|---|---|
| mode | weekly | 周榜。 |
| content | illust | 插画榜。 |
| format | json | JSON 响应。 |
| p | 1 | 第一页,当前实现只处理前 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 由平台返回,用于本期去重;不等同于任务执行当天。过滤遮罩、成人标签、性内容、血腥与暴力标记后,为每个作品保留作者、排名和原作链接。
https://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. 图片请求与内容校验
上一步返回的 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 和频道配置;机器人必须在目标频道拥有发帖权限。本文没有在线调用按钮或凭据输入框。
https://api.telegram.org/bot<TOKEN>/sendMediaGroup写入 · multipart/form-data| 字段 | 类型 | 本项目用法 |
|---|---|---|
| chat_id | 整数 | 已有私有频道标识,从私有配置读取。 |
| media | JSON 字符串 | 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;上游字段与访问政策可能变化。