总日志数
-
总文件数
-
| 时间 | 标题 | 大小 | 链接 | 操作 |
|---|
微信推文解析器 API 提供微信公众号推文的解析、图片代理、正文截图和 R2 对象存储上传能力。基于 Cloudflare Workers + Hono 框架构建,所有 API 端点位于 /api/v1/ 路径下。
https://xiaozhao-img-cache.pages.dev
https://xiaozhao-img-cache.hatu.work
所有 /api/v1/* 端点均要求 API Key 认证。支持以下两种方式:
# 方式一:自定义请求头(推荐) curl -H "X-API-Key: sk-xxxxxxxxxxxxxxxx" ... # 方式二:Bearer Token curl -H "Authorization: Bearer sk-xxxxxxxxxxxxxxxx" ...
| 错误码 | HTTP 状态码 | 说明 |
|---|---|---|
UNAUTHORIZED | 401 | API Key 无效或缺失 |
INVALID_URL | 400 | 微信推文链接格式无效 |
PARSE_FAILED | 500 | 文章解析失败(壹伴 API 异常或 session 过期) |
UPLOAD_FAILED | 500 | R2 上传失败 |
MISSING_IMAGE | 400 | 请求中缺少图片数据 |
PROCESS_FAILED | 500 | 完整处理流程失败 |
// 错误响应统一格式
{
"success": false,
"error": "错误描述(中文)",
"code": "ERROR_CODE" // 机器可读错误码,用于程序判断
}
由于微信图片存在严格的跨域 (CORS) 和防盗链限制,所有 API 返回数据中的微信图片 URL 均已自动替换为代理域名。
// 替换前(微信原始 CDN,浏览器跨域受限): http://mmecoa.qpic.cn/mmecoa_jpg/6r7I0M2XKy7.../0?wx_fmt=jpeg https://mmbiz.qpic.cn/sz_mmbiz_jpg/abc.../0?wx_fmt=jpeg // 替换后(代理域名,可直接在前端使用): https://weixinimg.hatu.work/mmecoa_jpg/6r7I0M2XKy7.../0?wx_fmt=jpeg https://weixinimg.hatu.work/sz_mmbiz_jpg/abc.../0?wx_fmt=jpeg // 规则说明: // 1. 仅替换域名部分(scheme + host),路径和参数完全保留 // 2. 支持 http:// 和 https:// 两种协议 // 3. 覆盖所有微信图片域名:*.qpic.cn, *.qlogo.cn // 4. 影响字段:cdn_url, cdn_url_1_1, cdn_url_235_1, content_noencode 中所有 img 标签
wx_image_proxy)"中修改。解析微信推文,返回结构化文章数据。所有图片 URL 已替换为代理域名。同时缓存文章内容用于后续渲染。
mp.weixin.qq.com。支持短链和长链。curl -X POST https://xiaozhao-img-cache.pages.dev/api/v1/parse \
-H "Content-Type: application/json" \
-H "X-API-Key: sk-xxxxxxxx" \
-d '{"url":"https://mp.weixin.qq.com/s/3MZZ1jmh7IRPLTVj9-li-A"}'
{
"success": true,
"data": {
"title": "文章标题",
"author": "作者名称",
"nick_name": "公众号名称",
"desc": "推文摘要描述",
"source_url": "阅读原文链接(可能为空字符串)",
"cdn_url": "https://weixinimg.hatu.work/mmecoa_jpg/...(主封面图)",
"cdn_url_1_1": "https://weixinimg.hatu.work/...(1:1 比例封面图)",
"cdn_url_235_1": "https://weixinimg.hatu.work/...(2.35:1 比例封面图)",
"content_noencode": "<section>正文 HTML...</section>(所有 img src 已代理)",
"article_id": "m1abc2def3",
"render_url": "https://xiaozhao-img-cache.pages.dev/render/m1abc2def3"
}
}
上传图片至 Cloudflare R2 对象存储。支持两种上传模式。
data:image/png;base64, 前缀或纯 Base64cover.png),默认自动生成image/png, image/jpeg, image/webp# JSON Base64 方式
curl -X POST https://xiaozhao-img-cache.pages.dev/api/v1/upload \
-H "Content-Type: application/json" \
-H "X-API-Key: sk-xxxxxxxx" \
-d '{"image_base64":"data:image/png;base64,iVBOR...","title":"文章截图"}'
# 二进制方式
curl -X POST https://xiaozhao-img-cache.pages.dev/api/v1/upload \
-H "Content-Type: image/png" \
-H "X-API-Key: sk-xxxxxxxx" \
-H "X-Title: 文章截图" \
-H "X-Filename: my_screenshot.png" \
--data-binary @screenshot.png
{
"success": true,
"data": {
"url": "https://xiaozhao-img-cache.hatu.work/articles/api_xxx.png",
"key": "articles/api_xxx.png",
"size": 123456 // 文件大小(字节)
}
}
一站式处理:解析推文 + 生成自动截图页面。返回文章数据和可直接访问的截图 URL。
curl -X POST https://xiaozhao-img-cache.pages.dev/api/v1/process \
-H "Content-Type: application/json" \
-H "X-API-Key: sk-xxxxxxxx" \
-d '{"url":"https://mp.weixin.qq.com/s/xxxxx"}'
{
"success": true,
"data": {
"article": {
"title": "文章标题",
"author": "作者",
"nick_name": "公众号名称",
"desc": "摘要",
"source_url": "",
"cdn_url": "https://weixinimg.hatu.work/...",
"cdn_url_1_1": "https://weixinimg.hatu.work/...",
"cdn_url_235_1": "https://weixinimg.hatu.work/...",
"content_noencode": "正文HTML(图片已代理)",
"article_id": "m1abc2def3"
},
"image": null, // 若提供 image_base64 则返回 { url, key, size }
"render_url": "https://.../render/m1abc2def3",
"screenshot_url": "https://.../render/m1abc2def3?mode=screenshot",
"_usage": {
"screenshot_flow": "打开 screenshot_url 自动完成截图并上传至 R2",
"puppeteer_detect": "通过 document.title 检测: SCREENSHOT_DONE:url",
"js_detect": "通过 window.__screenshot_result 获取结果",
"postmessage": "监听 postMessage: { type: screenshot_done, data }"
}
}
}
image_base64,返回上传结果 {url, key, size};否则为 null由于 Cloudflare Workers 无法运行无头浏览器,截图通过客户端渲染完成。推荐使用 Puppeteer 或 Playwright 配合 screenshot_url 实现全自动截图。
const puppeteer = require('puppeteer');
async function captureArticle(wxUrl, apiKey) {
const BASE = 'https://xiaozhao-img-cache.pages.dev';
// 1. 调用 /api/v1/process 获取 screenshot_url
const resp = await fetch(BASE + '/api/v1/process', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey },
body: JSON.stringify({ url: wxUrl }),
});
const { data } = await resp.json();
console.log('文章标题:', data.article.title);
// 2. 用 Puppeteer 打开 screenshot_url(自动截图 + 上传)
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 900, height: 1200 });
await page.goto(data.screenshot_url, { waitUntil: 'networkidle2' });
// 3. 等待截图完成(通过 document.title 检测)
await page.waitForFunction(
() => document.title.startsWith('SCREENSHOT_DONE:')
|| document.title.startsWith('SCREENSHOT_ERROR:'),
{ timeout: 60000 }
);
// 4. 获取结果
const result = await page.evaluate(() => window.__screenshot_result);
await browser.close();
if (result.success) {
console.log('截图 URL:', result.data.url);
return result.data;
} else {
throw new Error(result.error);
}
}
// 使用示例
captureArticle(
'https://mp.weixin.qq.com/s/3MZZ1jmh7IRPLTVj9-li-A',
'sk-xxxxxxxx'
).then(console.log);
const { chromium } = require('playwright');
async function captureWithPlaywright(wxUrl, apiKey) {
const BASE = 'https://xiaozhao-img-cache.pages.dev';
// 1. 解析文章
const resp = await fetch(BASE + '/api/v1/process', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey },
body: JSON.stringify({ url: wxUrl }),
});
const { data } = await resp.json();
// 2. 打开自动截图页面
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto(data.screenshot_url);
// 3. 等待 title 变化(包含完成或错误标记)
await page.waitForFunction(
() => document.title.startsWith('SCREENSHOT_DONE:')
|| document.title.startsWith('SCREENSHOT_ERROR:'),
{ timeout: 60000 }
);
// 4. 读取结果
const result = await page.evaluate(() => window.__screenshot_result);
await browser.close();
return result;
}
import requests, json, time
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
BASE = "https://xiaozhao-img-cache.pages.dev"
API_KEY = "sk-xxxxxxxx"
def capture_article(wx_url):
# 1. 调用 API
resp = requests.post(f"{BASE}/api/v1/process",
headers={"Content-Type": "application/json", "X-API-Key": API_KEY},
json={"url": wx_url})
data = resp.json()["data"]
# 2. Selenium 打开截图页面
driver = webdriver.Chrome()
driver.get(data["screenshot_url"])
# 3. 等待完成
WebDriverWait(driver, 60).until(
lambda d: d.title.startswith("SCREENSHOT_DONE:")
or d.title.startswith("SCREENSHOT_ERROR:"))
# 4. 读取结果
result = driver.execute_script("return window.__screenshot_result")
driver.quit()
return result
| 方式 | 说明 | 适用场景 |
|---|---|---|
document.title | 成功时为 SCREENSHOT_DONE:<url>,失败为 SCREENSHOT_ERROR:<msg> | Puppeteer/Playwright/Selenium |
window.__screenshot_result | 对象 { done, success, data, error } | Puppeteer/Playwright (page.evaluate) |
postMessage | 向 opener/parent 发送 { type: 'screenshot_done', data } | iframe 嵌入或 window.open |
管理端 API 同样支持 API Key 认证或管理员 Cookie Session。
# 登录获取 Session
POST /api/admin/login { "username": "admin", "password": "admin" }
# 返回: { "success": true, "token": "xxx" }
# 同时设置 Cookie: admin_session=xxx
# 退出
POST /api/admin/logout
# 检查登录状态
GET /api/admin/check → { "authenticated": true/false }
# 获取所有配置(敏感字段已脱敏)
GET /api/admin/config
# 更新单个配置
PUT /api/admin/config
Body: { "key": "wx_image_proxy", "value": "https://weixinimg.hatu.work" }
# 重新生成 API Key(旧 Key 立即失效)
POST /api/admin/config/regenerate-api-key
| 配置键 | 说明 | 默认值 |
|---|---|---|
yiban_session | 壹伴编辑器 Session(用于调用壹伴 API 解析文章) | 预设值 |
wx_image_proxy | 微信图片代理域名(替换 *.qpic.cn 等域名) | https://weixinimg.hatu.work |
r2_access_key_id | R2 S3 兼容 API 的 Access Key | 预设值 |
r2_secret_access_key | R2 S3 兼容 API 的 Secret Key | 预设值 |
r2_endpoint | R2 S3 兼容 API 端点 | 预设值 |
r2_bucket_name | R2 存储桶名称 | xiaozhao-cache |
r2_custom_domain | R2 自定义域名(文件 URL 前缀) | https://xiaozhao-img-cache.hatu.work |
api_key | API 调用密钥 | 首次自动生成 |
admin_username | 管理员用户名 | admin |
admin_password | 管理员密码 | admin |
max_image_width | 截图最大宽度(px) | 800 |
image_quality | 截图质量(0-1) | 0.92 |
# 查看日志(分页 + 筛选)
GET /api/admin/logs?page=1&pageSize=20
&type=info|error|warn|api # 按类型筛选
&action=parse_article # 按操作名称筛选(模糊匹配)
&keyword=奥比中光 # 按详情关键词搜索(模糊匹配)
# 导出日志(支持筛选条件)
GET /api/admin/logs/export?format=json # JSON 格式导出
GET /api/admin/logs/export?format=csv # CSV 格式导出
&type=api&keyword=xxx # 可选筛选参数
# 删除单条日志
DELETE /api/admin/logs/:id
# 清空所有日志
DELETE /api/admin/logs
# 查看文件列表
GET /api/admin/files?page=1&pageSize=20
# 删除文件(同时删除 R2 存储和索引)
DELETE /api/admin/files/:key
# key 需 URL 编码,如: articles%2Farticle_xxx.png
GET /api/admin/stats
→ { "total_logs": 42, "total_files": 15 }
以下端点供前端 Web UI 调用,支持 Admin Session Cookie 或 API Key 认证。
# 解析文章(Web UI 使用)
POST /api/article/parse { "url": "https://mp.weixin.qq.com/s/xxx" }
# 上传截图(Web UI 使用)
POST /api/article/upload-image
{
"image_base64": "data:image/png;base64,...",
"title": "文章标题",
"source_url": "原始链接"
}
# 文件渲染
GET /render/:id # 文章预览页
GET /render/:id?mode=screenshot # 自动截图页(无需人工操作)
yiban_session 会定期失效,解析失败时请在"配置"中更新。访问 yibanbianji.com 登录后从浏览器 Cookie 中获取 session 值。