Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .env.test.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# 本次 Reddit + LLM 翻译联调用的系统环境变量示例
# 复制为 .env 或 .env.dev,把占位符换成真实值;勿提交含密钥的 .env 到 Git

# ---------- NoneBot ----------
ENVIRONMENT=prod
DRIVER=~fastapi+~httpx+~websockets
HOST=127.0.0.1
PORT=8080
LOG_LEVEL=INFO

# ---------- 插件:Reddit Cookie(必填,否则 Reddit 403)----------
# 方式 1:整行写入(与浏览器导出 name=value; name2=value2 一致)
# PARSER_REDDIT_CK=token_v2=...; reddit_session=...; loid=...
#
# 方式 2:项目根已有 .reddit_ck_probe 时,测试脚本会自动读取;
# 部署到另一台机器请把该文件内容合并进 PARSER_REDDIT_CK 一行,或继续用 .reddit_ck_probe + 启动前 export

PARSER_REDDIT_CK=<从 .reddit_ck_probe 复制整行 Cookie,或浏览器 Cookie 导出>

# ---------- 插件:卡片渲染 ----------
PARSER_RENDER_TYPE=common
PARSER_NEED_FORWARD_CONTENTS=true
PARSER_APPEND_URL=false

# ---------- 插件:国外帖标题/正文 LLM 翻译(Genie3 / artificial 等实测)----------
PARSER_LLM_TRANSLATE_ENABLE=true
PARSER_LLM_TRANSLATE_API_URL=http://192.168.1.37:8317/v1/
PARSER_LLM_TRANSLATE_API_KEY=<你的 API Key>
PARSER_LLM_TRANSLATE_MODEL=grok-4.20-0309-non-reasoning
PARSER_LLM_TRANSLATE_TIMEOUT=60
PARSER_LLM_TRANSLATE_PLATFORMS=reddit
# 留空则用内置「无感中文」提示词;要自定义可取消下行注释
# PARSER_LLM_TRANSLATE_PROMPT=你是中文本地化编辑。把标题和正文译成自然顺口的中文……只输出 JSON {"title":"...","text":"..."}

# ---------- 可选 ----------
# PARSER_PROXY=http://127.0.0.1:7890
# PARSER_MAX_SIZE=90
# PARSER_DURATION_MAXIMUM=480

# ---------- 控制台(Windows 建议,避免 Rich 编码报错)----------
PYTHONUTF8=1
NO_COLOR=1

# 精简 Cookie(推荐):不必 19 项全塞一行
# 核心 3 项(多数够用):token_v2 + reddit_session + loid
# 核心 5 项(更稳):再加 edgebucket、csv
# 生成方式:uv run python scripts/_reddit_minimal_cookie.py .reddit_ck_probe
# PARSER_REDDIT_CK=token_v2=...; reddit_session=...; loid=...
33 changes: 32 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,35 @@ coverage*.xml
.env.*
render_result.md
render_result_html.md
render_result_combined.md
render_result_combined.md

# local delivery / probe
*_out/
release/
*_ck_probe
.reddit_ck_probe
.tieba_ck_probe
scripts/_mo_*.html
scripts/_tieba_saved_page.html

# 本地调试脚本(PR 仅保留 delivery + 文档)
scripts/_*
scripts/instagram_delivery_*.py
!scripts/instagram_delivery.py
scripts/instagram_post_card.py
scripts/reddit_delivery*.py
scripts/reddit_media_check_download.py
scripts/reddit_share_preview.py
scripts/reddit_translate_media_test.py

# 根目录落盘与临时文件
_tmp_*.html
reddit.png
reddit_*_card.png
reddit_delivery/
reddit_media_check/
reddit_share_previews/
reddit_translate_test/

# 允许提交测试环境示例
!.env.test.example
48 changes: 48 additions & 0 deletions scripts/INSTAGRAM_ADAPT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Instagram 适配说明

## 已支持链接

- Reel: `https://www.instagram.com/reel/<id>/`
- Reels: `https://www.instagram.com/reels/<id>/`
- 帖子: `https://www.instagram.com/p/<id>/`
- IGTV: `https://www.instagram.com/tv/<id>/`

## 实现方式

- 解析与下载:**yt-dlp**(与 TikTok / YouTube 同路径)
- 发送:**卡片 + 视频消息**(Reel);多图帖子为 **图集**

## 本机已验证(Reel)

`https://www.instagram.com/reel/DBOC0Z4hOR1/`

- 作者:`@kiran.mazumder`
- 时长:约 25s
- 封面 + mp4 CDN 可由 yt-dlp 直接提取(未登录亦可,视地区/风控而定)

## 环境变量(可选)

```env
PARSER_INSTAGRAM_CK=sessionid=...; csrftoken=...; ds_user_id=...
```

登录 Cookie 写入 `instagram_cookies.txt`(Netscape),用于私密帖、风控失败时的回退。

## Browser Relay(接管浏览器)

Relay 服务已可在本机启动;要**读取你当前 Chrome 标签页**需:

1. Chrome 打开 `https://www.instagram.com/reel/DBOC0Z4hOR1/`
2. 安装并启用 **Browser Relay** 扩展(`browser-relay path` 可查看扩展目录)
3. 执行 `browser-relay tabs` 应能看到该标签

之后可用:

```bash
browser-relay snapshot --tab <id> --max-length 20000
browser-relay network --tab <id> --show-secrets --json
```

从 **network** 里提取 `sessionid` / `csrftoken` 填入 `PARSER_INSTAGRAM_CK`。

当前会话:`connected: false, tabCount: 0` — 扩展未连上,因此尚未抓取页面 DOM;解析逻辑已按 yt-dlp 字段对齐。
92 changes: 92 additions & 0 deletions scripts/TIEBA_GUEST_BROWSER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# 贴吧「未登录 / 隐身」为何能浏览(Relay 实测)

探测帖:`https://tieba.baidu.com/p/10780369243`
工具:Browser Relay 接管 Chrome 标签(可为隐身窗口,只要扩展已附着该标签)

## 结论(一句话)

**未登录也能看**,靠的是 **访客 Cookie + 带签名的 XHR**,由 **`pb.f2861951.js` 在浏览器里拼请求**;正文/视频 URL 会写进 **渲染后的 DOM/HTML**,不是首屏 11KB 壳子里的静态字段。
**Python 裸 GET 帖页** 拿不到这套流程,所以和隐身浏览器不是一回事。

## 访客身份(`/c/s/pc/sync`)

```json
"user": { "is_login": 0 },
"anti": { "tbs": "5f93bd7649de07091781401682" }
```

- **`is_login: 0`**:明确未登录。
- **`tbs`**:后续接口常用的防刷 token(需在请求里带上,具体字段以 bundle 为准)。

## `document.cookie`(非登录)

| 项 | 值 |
|----|-----|
| 长度 | ~239 |
| **BDUSS** | **无** |
| 典型键 | `TIEBA_NEW_PC=1`、`TIEBA_SID=...`、`BAIDUID=...`、`USER_JUMP=-1` |

说明:隐身/访客仍有 **贴吧会话 Cookie**,但没有百度账号登录态。

## 页面如何有内容

1. **Document** 仍可能是小壳子;**执行** `pb.*.js` 等 bundle。
2. 浏览器发起(`performance` / network 可见):
- `POST https://tieba.baidu.com/c/f/pb/page_pc` → 本环境返回 **`error_code: 110001`**(接口仍被调用)
- `GET .../c/s/pc/sync?...&sign=...` → **成功**,`error_code: 0`
- `GET .../c/f/pc/pbSidebarRight?tid=...&sign=...` → **成功**,含吧信息 JSON
3. 前端把数据 **灌进 Vue/DOM** 后:
- `htmlLen` ≈ **77 万**
- `.pb-title`、`<video src="http://tb-video.bdstatic.com/...mp4">` 均在 DOM 中
- HTML 内 **`post_list` 字符串出现 0 次**(不是旧版内嵌 JSON 帖列表)

## 和 Python 解析器的差距

| 步骤 | 隐身 Chrome | curl_cffi 无 Cookie |
|------|-------------|---------------------|
| GET `/p/{id}` | 小文档 + 跑 JS | ~11KB,无 `pb-title` |
| `sync` + `sign` | 自动带 Cookie/tbs | 未实现 sign → 失败 |
| `page_pc` | 会请求(可能失败但仍能靠其它接口+DOM) | `110001` |
| 结果 | 可看视频 | 解析失败 |

## 对「不登录也能解析」的启示

若要服务端接近隐身效果,不能只 GET HTML,需要至少:

1. 模拟访客:**首页 → 收 `TIEBA_SID` / `BAIDUID` 等 Cookie**
2. 调 **`/c/s/pc/sync`** 拿 **`tbs`** 与 `is_login`
3. 按前端规则计算 **`sign`**,请求 **`pbSidebarRight`** 等(`page_pc` 可能仍 110001,需再挖备用数据源)
4. 或:**用无头 Chrome / Relay 导出渲染后 HTML**(当前 `tieba_delivery_from_html.py` 路线)

## Relay 复现命令

```powershell
browser-relay tabs
# 在隐身窗口打开贴吧帖并附着扩展后:
Get-Content scripts\_tieba_incognito_probe.js -Raw | browser-relay eval --stdin --tab <TAB_ID>
Get-Content scripts\_tieba_incognito_fetch.js -Raw | browser-relay eval --stdin --tab <TAB_ID>
```

## 脚本

- `scripts/_tieba_incognito_probe.js` — Cookie / 脚本 / DOM 规模
- `scripts/_tieba_incognito_fetch.js` — 同页 `fetch(page_pc)` 与 resource 列表
- `scripts/_tieba_html_embed.js` — HTML 内嵌关键字统计
- `scripts/_probe_tieba_guest_cookies.py` — Python 访客 Cookie 链(仍可能只有 11KB)

## 2026-06-14 更新:服务端未登录主路径

**未登录解析优先** `GET https://tieba.baidu.com/mo/q/m?kz={tid}`(先 `GET /` 拿访客 Cookie)。

| 步骤 | 说明 |
|------|------|
| 实现 | `src/nonebot_plugin_parser/parsers/tieba.py` → `_fetch_best_html` / `_result_from_mo_html` |
| 帖元数据 | 内嵌 `threadInfo` / `'thread'` JSON,按 `tid` 取 `title`、`reply_num`、`share_num`、`author` |
| 图集 | `tiebapic.baidu.com/forum/pic/item/`(`src=` 优先,按 pic id 去重) |
| 视频 | 优先 `tieba-smallvideo-transcode-cae`;裸 `tieba-movideo` 易 HTTP 401 |
| 落盘 | `scripts/tieba_delivery.py <url>` → `tieba_{tid}_out/` |

验收(本机已跑通):

- `tieba_10787181972_out`:图集 + `card.png` + `meta.json`
- `tieba_10780369243_out`:`media_00_video.mp4` + 封面 + `card.png`
116 changes: 116 additions & 0 deletions scripts/TIEBA_RELAY_PROBE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# 百度贴吧 PC 帖页 Relay 摸底(`p/10787181972`)

探测时间:2026-06-14
工具:Browser Relay(Chrome 已登录会话)+ `curl_cffi` 裸请求对比

## 链接形态

| 类型 | 示例 |
|------|------|
| 帖页 | `https://tieba.baidu.com/p/{thread_id}` |
| 带参 | `?fr=frs` |
| 楼主帖 | `?tab=只看楼主`(URL 编码) |

本帖:`10787181972`
标题(`document.title` / `.pb-title`):**X网友抓拍到了鳄鱼捕食的瞬间**

## 页面架构(新版 PC SPA)

- **不是**旧版 `l_post` / `d_post_content` / `data-field` 直出 HTML。
- 首屏 Document 仅 ~11KB(`curl_cffi` 无 Cookie 时几乎空壳),**正文由前端 bundle 渲染**。
- 关键静态资源:
- `pc-main-core/static/js/pb.f2861951.js`
- `pc-main-core/static/css/pb.18c49760.css`
- 公共:`frs~home~home-main~pb~search~token.*.js`

Relay 下完整 DOM 可见;裸 HTTP 需带浏览器 Cookie / 或走 Relay / 或找 XHR 接口。

## DOM 元素来源(Relay `eval` 实测)

### 标题

| 选择器 | 说明 |
|--------|------|
| `.pb-title` | 主标题文案 |
| `.pb-title-wrap.pc-pb-title` | 标题容器 |

### 楼主 / 作者

- 头像:`img.avatar-img` → `himg.bdimg.com/sys/portrait/item/tb.*`
- 昵称 + 等级:楼主块内文本,如 `O钢弹在白露之滨` + `红色有角`
- 主页:`a[href*="/home/main?id=tb."]`,`id` 为贴吧用户 token(如 `tb.1.b01a014e._0rp1F3ZD_zgRMnxkcYS6Q`)
- **注意**:侧栏「我常逛的吧」也会产出 `/home/main` 链接,解析时要限定在**主内容区**(`.center-content` / `.image-text` / 标题附近),避免误取当前登录用户。

### 楼主正文与互动数

标题下方楼主区可见:

- 正文示例:`🐊没事吧?`
- 数字条(推测:分享 / 回复 / 浏览等):`20`、`212`、`6059`、`252`(需对照页面图标确认字段含义)
- 回复区:`.pc-pb-comments`、`.pc-pb-reply-list`

### 图片

- 帖内图:`https://tiebapic.baidu.com/forum/pic/item/{hash}.jpg?tbpicau=...`
- 吧头像:`tiebapic.baidu.com/forum/w=120;h=120/sign=...`
- 侧栏话题图可能带 `topic-img`,解析时应优先 **楼主正文区域内** 的 `tiebapic.../pic/item/`

本帖 Relay 抓到 **4 张** `pic/item` 图(鳄鱼抓拍多图)。

### 吧信息

- `og:url`(若存在):`https://tieba.baidu.com/{username}/p/{id}/` 可反推吧主路径;本帖楼主为个人帖,侧栏可能显示所在吧(需从面包屑或吧入口取)。

## 网络请求(Relay `network` 摘要)

| 类型 | URL 模式 | 用途猜测 |
|------|----------|----------|
| Document | `/p/{id}` | 壳 + 引导加载 pb bundle |
| XHR | `tieba.baidu.com/mo/q/getConfigData?...` | 配置/amis |
| 静态 | `tb3.bdstatic.com/tb/pc/pc-main-core/static/js/pb.*.js` | 帖页逻辑 |
| 图片 | `tiebapic.baidu.com` | 帖图/吧图 |
| 头像 | `himg.bdimg.com/sys/portrait/item/` | 用户头像 |

**未在刷新后 capture 到明显的「单帖 JSON」XHR**(`network` 列表偏静态与埋点)。后续实现可:

1. **Relay + 选择器** 抽 DOM(开发/调试)
2. **带 Cookie 的 curl_cffi** 拉完整 HTML 再解析(与 Relay 结构应对齐)
3. **逆向 `pb.*.js` 或抓包** 找 `thread_id` 对应 API(适合无头批量)

## 与项目内 NGA 解析对比

| | NGA | 贴吧(本帖) |
|---|-----|----------------|
| HTML | 服务端直出 `postcontent0` | SPA,裸请求几乎无正文 |
| 作者 | `postauthor0` + JS `userInfo` | `.avatar-img` + `/home/main?id=tb.` |
| 标题 | `#postsubject0` | `.pb-title` |
| 图片 | 附件 URL 规则 | `tiebapic.baidu.com/forum/pic/item/` |

## 建议解析器字段映射(初稿)

```text
ParseResult
├── title ← .pb-title
├── text ← 楼主首楼正文(主内容区首段,不含全楼回复)
├── author ← 楼主昵称 + portrait URL
├── url ← canonical /p/{id}/
├── timestamp ← 楼主时间(如 06-12,需补全年份或从接口)
├── contents[] ← tiebapic pic/item 多图
└── extra
├── forum_name / forum_kw(若能从页面取)
├── reply_count ← 「全部回复 (212)」
└── view_count 等(待图标对齐)
```

## 本地探测脚本

- `scripts/_tieba_relay_probe.js` / `_tieba_relay_probe4.js` — Relay DOM
- `scripts/_probe_tieba_http.py` — 无 Cookie HTTP 对比
- 输出样例:`scripts/_tieba_probe4_out.json`

## 下一步(实现前)

1. 用 **同一 Cookie** 验证 `curl_cffi` 能否拿到与 Relay 一致的 HTML 长度与 `.pb-title`。
2. 锁定**楼主正文**容器 class(避免把评论列表当 `text`)。
3. 在 `PlatformEnum` / `parsers/tieba.py` 注册 `tieba.baidu.com` + `/p/(?P<tid>\d+)`。
4. 卡片渲染:参考 NGA/论坛型,多图网格 + 标题 + 作者行。
Loading
Loading