出口这一步,知乎是四章里路最多的平台:三路形态(文章/回答/想法)各有一套操作,还多一条【创作声明】必须设。 所以本节的设计分两块——第一块是【路由】(这一篇走哪条路),第二块是【预检】,而预检要先分【发不出去】和【发出去出事】两大类。
5.4.1 取回了稿子、5.4.2 排好版,本节把稿子送进知乎编辑器、填好字段、设好声明,然后停下。 这一步的出入口形状与第二章(小红书)相同——都是点界面;但知乎多了三样东西:三路形态、创作声明、以及一套明确写进公告的处置梯度。
一、先把那条硬约束再说一遍(工程含义)
5.1.1 已经讲过结论,这里说它对这一节的工程含义:
| 事实 | 对脚本的含义 |
|---|---|
| 知乎开放平台只有只读能力(话题、热榜、知识库检索) | 发布环节没有接口可调,只能走界面自动化 |
| 《开发者协议》禁止写入类操作 | 不要试图用非公开接口——那不只是技术问题 |
| 登录态有一定有效期(依赖多重票据) | 脚本要能识别【已掉线】并停下来,不要对着登录页硬点 |
| 《用户协议》禁止用自动化工具访问或干扰网站 | 节奏必须像人——见下面第二条 |
第二条要单独说:节奏
这一条与 4.x 那章的【批量】不同——本章的批量小,但节奏问题依然在:
- 知乎有反刷机制:短时间内连续发布多篇内容,会出现验证码或【操作过于频繁】提示;
- 一个可靠的间隔是 60 秒以上——这不是矫情,是【让操作看起来像人】的最低成本;
- 而且它顺带解决另一件事:给你留出【回读校验】的时间(每发一篇,看一眼草稿/已发布页,确认字段真的进去了)。
第一层是技术性的:间隔够长、不并发、不绕过验证码。 第二层是内容性的:这正是全章一直在讲的那件事——内容要有人味。 两层其实是同一件事:知乎这个平台的治理逻辑,是围绕【真人真内容】建的(5.1.1 现状三:判定看文本特征)。所以它既不容忍【机器批量发内容】,也不容忍【机器批量做操作】。 这不是两条规则,是同一条。
二、第一件事:路由——这一篇走哪条路
这是本节最核心的设计,也是知乎独有的。 三路形态互不通用,RPA 必须先读任务表的【形态】字段,再选操作序列。
| 文章 | 回答 | 想法 | |
|---|---|---|---|
| 入口 | 创作中心 → 写文章(zhuanlan.zhihu.com/write) |
打开一个具体问题 → 写回答 | 创作中心 → 发想法 |
| 必填 | 标题、正文 | 问题(问题链接)、正文 | 正文 |
| 封面 | 有 | 无 | 无 |
| 话题 | 有(≤5 个) | 有(≤5 个) | 有(≤5 个) |
| 定时发布 | 支持 | 不支持 | 不支持 |
| 内容长度 | 长文 | 中等(可长可短) | 短内容 |
这张表里有一条特别值得记:官方定时发布只支持【文章】这一种形态——回答和想法目前必须即时发出。
这个细节的意义在于:它把【排期】这件事限制在了一条路上。 如果你想做【晚上十点发】这种节奏控制,只有文章能用定时;回答和想法要么现在发,要么先存草稿、到时候再让脚本点发布——后者要额外设计一个【定时唤醒】的环节。
路由的具体做法(脚本层面):
读任务表的【形态】字段
├─ 文章 → 走【写文章】序列:填标题 → 正文 → 封面 → 话题 → 声明
├─ 回答 → 走【回答问题】序列:打开 qurl → 写回答 → 话题 → 声明
└─ 想法 → 走【发想法】序列:正文 → 话题 → 声明
注意每条序列的最后都是【声明】——因为它是三路共同的必做项(下一节就是它)。
因为三路的不只是【操作位置】不同,【字段集合】也不同:文章有标题和封面,回答有问题链接,想法两样都没有。 如果不在最前面把路由定下来,后面每一个字段都要写一次 if——而且很容易漏(比如回答那一路忘了必填问题链接,脚本跑到最后才发现无处可填)。 更好的做法:路由决定之后,生成一张【这一篇要填哪些格子】的清单,后面的动作照着清单走。这与 5.2 那张任务表是同一个思路——先把要做的列清楚,再动手。
三、第二件事:填字段(三路的清单)
| 步 | 文章 | 回答 | 想法 |
|---|---|---|---|
| 1 | 打开编辑器 | 打开问题页 → 写回答 | 打开想法编辑器 |
| 2 | 填标题(≤80 字符) | —— | —— |
| 3 | 粘正文(5.4.2 的富文本) | 粘正文 | 粘正文 |
| 4 | 上传封面(690×260 或 800×450) | —— | —— |
| 5 | 上传正文图(走知乎图床) | 同 | 同 |
| 6 | 选话题(1~5 个,别多) | 同 | 同 |
| 7 | 设创作声明 | 同 | 同 |
| 8 | 停在【发布】前 | 同 | 同 |
这张清单里,第 5 步和第 7 步是【容易出事的两个点】:一个关系图片能不能显示,一个关系合规。下面各说一句。
四、第三件事:创作声明——知乎独有的一步
它是本节最不能漏的一步,而且它有自己的【位置】和【形态】。
| 项 | 说明 |
|---|---|
| 位置 | 编辑器底部的【发布设置】区域里,有一个【创作声明】下拉框 |
| 默认值 | 无声明 |
| 六档可选 | 无声明 / 包含剧透 / 包含医疗建议 / 虚构创作 / 包含理财内容 / 包含 AI 辅助创作(作者对内容负责) |
| 时机 | 必须在发布前设——如果发布时漏了,要回头编辑已发布的内容补设(多一步麻烦,也多一次风控记录) |
RPA 的动作是固定三步:点开下拉框 → 选中那一档 → 确认框里的文字真的变了(这一步就是【回读校验】,与 5.4.2 的第 ⑥ 步同一个原则)。
它不叫【含 AI 生成】,它叫——【包含 AI 辅助创作,作者对内容负责】。 后半句是重点。 它不是免责声明,它是责任声明:你标了它,等于你承诺【我对这篇内容的准确性负责】。 所以声明不是【把责任推给平台】的动作,是【把责任接过来】的动作——这也解释了为什么它必须由人设,不能由脚本默认。
五、第四件事:图床
这一条与 5.4.2 的结论一字不差,这里只说 RPA 的动作:
① 正文图先存到本机
↓
② 在知乎编辑器里用【本机文件路径】上传(让它自己转存到知乎图床)
↓
③ 上传完成后回读一遍:图真的在编辑器里显示出来了吗
↓
④ 如果用的是墨滴图床的地址 → 换成知乎图床的地址再发
第 ③ 步不能省——图片是【异步上传】的,点了上传不等于传完了。回读是唯一可靠的确认方式。
六、动手 Demo:发布前预检(知乎版)
它是本节真正属于你自己的那部分代码,也是全章最后一道关。
它的结构比 4.4.4 更清楚一层——先分两大类:
| 组 | 含义 | 处理方式 |
|---|---|---|
| A 组 | 会让这一篇【发不出去】的(字段/格式不对) | 技术故障——改完就能发,顺序无所谓 |
| B 组 | 能发出去、但会【被处置】的 | 风险——平台按最重的那个处置,所以必须从重往轻修 |
| 账号级 | 单篇看不出、连着发才出事的 | 单独列,因为它防的是【这一串】不是【这一篇】 |
B 组的排序依据,就是知乎官方公告里那五档:
删除 > 折叠 > 减少分发 > 建议修改 > 标识
(重) (轻)
# -*- coding: utf-8 -*-
"""5.4.3 Demo:发布前预检(知乎版)—— 先分【发不出去】和【发出去出事】,再按处置档位排序(纯标准库)"""
import re
import statistics
# 每篇:形态 / 标题 / 问题链接 / 正文 / ai参与 / 创作声明 / 有亲历素材 / 是否本机图
DOCS = [
{"id": "P1", "form": "回答", "title": "", "qurl": "https://www.zhihu.com/question/123456",
"ai": True, "declare": "包含 AI 辅助创作", "src": True, "localimg": True},
{"id": "P2", "form": "回答", "title": "", "qurl": "https://www.zhihu.com/question/123456",
"ai": True, "declare": "无声明", "src": False, "localimg": True},
{"id": "P3", "form": "想法", "title": "", "qurl": "",
"ai": True, "declare": "包含 AI 辅助创作", "src": True, "localimg": True},
{"id": "P4", "form": "回答", "title": "", "qurl": "",
"ai": True, "declare": "包含 AI 辅助创作", "src": True, "localimg": True},
]
BODIES = {
"P1": "先说预算,我给三档,都是我实际花过的数。第一档二百元以内:窄边换鞋凳一百二,壁挂钥匙盘三十,"
"折叠收纳箱五十,加起来两百,我家玄关就这一档。第二档五百元左右:把换鞋凳换成一米二宽的鞋柜,"
"约四百,再配一个可视收纳箱八十。第三档一千五左右:做嵌入式玄关柜,柜体一千二,加装感应灯带三百。"
"说实话我觉得没必要上第三档。我家门后只有二十厘米,一体式根本塞不进去。"
"真要花钱,我建议先花二十块买个卷尺,量准三个尺寸:门后到墙、玄关进深、开门半径。",
"P2": "据某机构统计,87% 的家庭都存在收纳困扰,这说明收纳是一个普遍需求。"
"我朋友在杭州买的那套房子我亲眼见过,玄关做得非常漂亮,所以我总结了三档预算方案。"
"首先,我们需要明确基本原则。合理的分类能够让物品各归其位。"
"其次,我们要选择适合的收纳工具。收纳盒是常见的选择。"
"第三档一千五左右:做嵌入式玄关柜,柜体一千二,加装感应灯带三百。"
"综上,预算规划是收纳改造的第一步,建议先明确需求再选择工具。",
"P3": "今天把玄关清空了,只留换鞋凳和钥匙盘。",
"P4": "第一档二百元以内:换鞋凳一百二,钥匙盘三十,收纳箱五十。第二档五百元:窄边鞋柜四百,"
"收纳箱八十。第三档一千五:嵌入式玄关柜一千二,加感应灯带三百。我建议先量尺寸再下单。",
}
CONNECT = ["首先", "其次", "再次", "最后", "综上所述", "总之", "综上"]
# 处置档位(从重到轻;知乎官方公告里的五档)
ORDER = ["删除", "折叠", "减少分发", "建议修改", "标识"]
def analyze(d):
"""返回 (A 组: 发不出去的问题, B 组: [(档位, 问题)], 账号级)"""
A, B, ACCT = [], [], []
body = BODIES[d["id"]]
n = len(re.sub(r"\s+", "", body))
# ---- A 组:字段/格式不对,这一篇根本发不出去 ----
if d["form"] == "回答" and not d["qurl"]:
A.append("形态是【回答】却没有问题链接 —— 知乎回答必须挂在一个具体问题下面")
if d["form"] == "文章" and not d["title"]:
A.append("形态是【文章】却没有标题")
if d["form"] == "文章" and len(d["title"]) > 80:
A.append("标题 %d 字,超过知乎上限 80" % len(d["title"]))
# ---- B 组:能发出去,但会落到某一档处置 ----
# 声明与事实不符
if d["ai"] and d["declare"] != "包含 AI 辅助创作":
B.append(("标识", "标了【AI 参与】但创作声明是【%s】—— 声明与事实不符" % d["declare"]))
if not d["declare"]:
B.append(("标识", "创作声明为空 —— 发布前必须显式设置"))
# 疑似编造亲历(正文里有亲历口吻,但没有素材来源)
if re.search(r"我亲眼|我亲历|我朋友|我同事|我邻居", body) and not d["src"]:
B.append(("删除", "正文出现亲历口吻(【%s】)但没有素材来源 —— 虚构包装经历是明确红线"
% re.search(r"我亲眼|我亲历|我朋友|我同事|我邻居", body).group(0)))
# 无出处数据
m = re.search(r"据[^,。;]{0,12}(?:统计|调查|研究|数据)|\d{2,}\s*%", body)
if m and not re.search(r"据(?:[^,。;]{0,12})(?:发布|公告|报告|白皮书)", body):
B.append(("建议修改", "出现【%s】这类数字,但找不到机构名与日期 —— 缺乏事实依据" % m.group(0)[:14]))
# 结构模板化
cn = sum(body.count(w) for w in CONNECT) * 100.0 / n
if cn > 1.5:
B.append(("减少分发", "路标词每百字 %.2f 次,结构偏模板化 —— 判 AI 的抓手之一" % cn))
# ---- 账号级:单篇看不出来,连着发才出事 ----
if n < 100:
ACCT.append("正文 %d 字,低于 100 字 —— 连续 3 天这样会让创作行为分归零重计" % n)
return A, B, ACCT
print("=" * 76)
print("一、A 组:会让这一篇【发不出去】的(先拦这一批)")
print("=" * 76)
a_total = 0
for d in DOCS:
A, B, ACCT = analyze(d)
a_total += len(A)
print()
print(" [%s] %s" % (d["id"], "OK" if not A else "%d 条" % len(A)))
for x in A:
print(" ✗ %s" % x)
print()
print("=" * 76)
print("二、B 组:能发出去、但会【被处置】的(按档位从重到轻排)")
print("=" * 76)
b_all = []
for d in DOCS:
A, B, ACCT = analyze(d)
for lvl, msg in B:
b_all.append((d["id"], lvl, msg))
b_all.sort(key=lambda t: ORDER.index(t[1]))
if not b_all:
print(" (无)")
for tid, lvl, msg in b_all:
print(" [%s]%-6s %s" % (tid, lvl, msg))
print()
print("=" * 76)
print("三、账号级:单篇看不出、连着发才出事的")
print("=" * 76)
found = False
for d in DOCS:
A, B, ACCT = analyze(d)
for x in ACCT:
found = True
print(" [%s] %s" % (d["id"], x))
if not found:
print(" (无)")
print()
print("=" * 76)
print("四、汇总与放行")
print("=" * 76)
print(" A 组 %d 条(这部分不改就发不出去,处理顺序无所谓);" % a_total)
print(" B 组 %d 条(按档位排序:%s)。" % (len(b_all), " > ".join(ORDER)))
print()
ok = [d["id"] for d in DOCS if not any(analyze(d))]
print(" 三项全清的:%s" % ("、".join(ok) if ok else "(无)"))
print(" 注意:B 组里 P2 一篇就踩了四个不同档位 —— 平台按【最重的那个】处置,")
print(" 所以要【从重往轻修】,不是按发现的先后修。")
实际运行输出(真跑,不是示意):
============================================================================
一、A 组:会让这一篇【发不出去】的(先拦这一批)
============================================================================
[P1] OK
[P2] OK
[P3] OK
[P4] 1 条
✗ 形态是【回答】却没有问题链接 —— 知乎回答必须挂在一个具体问题下面
============================================================================
二、B 组:能发出去、但会【被处置】的(按档位从重到轻排)
============================================================================
[P2]删除 正文出现亲历口吻(【我朋友】)但没有素材来源 —— 虚构包装经历是明确红线
[P2]减少分发 路标词每百字 1.57 次,结构偏模板化 —— 判 AI 的抓手之一
[P2]建议修改 出现【据某机构统计】这类数字,但找不到机构名与日期 —— 缺乏事实依据
[P2]标识 标了【AI 参与】但创作声明是【无声明】—— 声明与事实不符
============================================================================
三、账号级:单篇看不出、连着发才出事的
============================================================================
[P3] 正文 19 字,低于 100 字 —— 连续 3 天这样会让创作行为分归零重计
[P4] 正文 84 字,低于 100 字 —— 连续 3 天这样会让创作行为分归零重计
============================================================================
四、汇总与放行
============================================================================
A 组 1 条(这部分不改就发不出去,处理顺序无所谓);
B 组 4 条(按档位排序:删除 > 折叠 > 减少分发 > 建议修改 > 标识)。
三项全清的:P1
注意:B 组里 P2 一篇就踩了四个不同档位 —— 平台按【最重的那个】处置,
所以要【从重往轻修】,不是按发现的先后修。
读结果,五个值得注意的地方:
- 把预检分成 A、B 两组,是本节最重要的一个设计决定。 为什么不能合成一个清单?因为它们的性质和处理方式完全不同:
- A 组是【技术故障】——发不出去,改完就能发,顺序无所谓;
-
B 组是【风险】——能发出去,但会落到某一档处置,而且平台按最重的那个处置,所以必须从重往轻修。 如果混在一起排,人就会从第一条开始一条条改——而可能第一条是【标识】那一档(最轻),最后一条才是【删除】那一档。改完最轻的才发现最重的那条根本不该发,前面的力气白花。
-
P2一篇就踩了四个不同档位——而且两头的两条特别典型:最重的那条(编造亲历)是模型最容易犯的(它会为了"写得更像真人"而编细节),最轻的那条(声明)是人最容易忘的。这两个方向的错误同时出现在一篇稿子里——说明【机器出的错】和【人出的错】会叠加,而预检是唯一能一次看到全部的地方。 -
P2里那句【我朋友在杭州买的那套房子我亲眼见过】,是本节最典型的一条检查。 注意脚本没有去判断这句话是真是假(它判不了)——它判的是【有没有素材支撑】:正文里出现了亲历口吻,但src这一格是 False。 这也正好接回 5.2 任务表的【补过人味】那一格:如果当初真的补了亲历素材,这一条根本不会触发。 所以检查项之间不是独立的,它们在同一条链上。 -
账号级那一条被单独列出来,是因为它的性质不一样:它不是【这一篇会怎样】,是【这一串会怎样】。 单篇看,19 字和 84 字都没任何问题;但连着几天这样发,创作行为分会归零重计(5.1.1 那条规则)。 这也是【为什么要把账号级规则写进脚本】的原因——有些风险单看一篇是永远看不出来的,只有把它和【频次】放在一起才成立。
-
"三项全清"的只有
P1,而P1恰好就是 5.4.1 里放行的那一份。 这不是巧合——它说明从 5.4.1 的取稿验收、到 5.4.2 的排版体检、到本节的发布前预检,三关的判据是【一致】的。 如果三条链路各自为政(比如取稿时说"人味 5/6 可以过",发布前却要求"必须 6/6"),那么合格的标准就会前后矛盾,人就会陷入【到底听谁的】。 一致性的价值,在于让人知道【改到什么程度为止】。
两节都是【发布前预检】,但排序依据完全不同: | | 4.4.4(头条) | 5.4.3(知乎) | |---|---|---| | 排序依据 | 信用分扣几分(70/20/10/5) | 落到哪一档处置(删除/折叠/减少分发/建议修改/标识) | | 共同点 | 都按【后果有多重】排,而不是按【发现顺序】排 | 同 |
这就是本课反复出现的那条原则的又一次应用:检查项的顺序不是【你想起来几件就列几件】,而是【后果从重到轻】。 而且两个平台的【后果】量纲不同——头条是分数(可累积、可计算),知乎是档位(离散、不可逆)——这正是 5.1.1 说的"风险形状不同"。
30 秒实验
把 Demo 里 P2 的 src 从 False 改成 True,再跑一次。
会发生什么:
- 【删除】那一档的那条消失了——
P2在 B 组的条目从 4 条降到 3 条; - 但它仍然是 B 组里最靠上的那一条变成了【减少分发】——
P2整体还是不合格,只是最重的那条被消掉了。
结论:修掉最重的那条,不等于这篇就能发。 一篇文章常常是【一身的毛病】,而预检的价值是让你看清【哪一条最该先修】——先修它,防止【修了半天最轻的那条,最后还是要删掉重写】。
顺着这个思路想一层:为什么【编造亲历】会被放在最重的一档(删除)?因为它触碰的不是格式,是真实——平台可以容忍一篇写得普通的回答,但不能容忍一篇【假装真实】的回答。 这与第一章那句关于【虚构包装纪实】的公告是同一条线:在这个平台,真实性的权重高于一切。
常见误区
- 【一份脚本通吃三种形态】——错,这是本节最基础的一条。 文章、回答、想法三路互不通用:字段不同(文章有标题和封面、回答有问题链接、想法两样都没有)、入口不同、连定时发布都只有文章支持。必须先读任务表的【形态】字段做路由,再选操作序列。
- 【发布前预检就是列一张问题清单,一条条改】——错,要先分组。 A 组(发不出去)和 B 组(发出去出事)的处理方式完全不同:A 组是技术故障、顺序无所谓;B 组要按处置档位从重到轻修(删除 > 折叠 > 减少分发 > 建议修改 > 标识)。混在一起排,你可能会先改最轻的那条。
- 【创作声明可以发布之后补】——技术上可以,但代价是双重的。 漏了要回头编辑已发布的内容补设——多一步操作,多一次风控记录。而且那一档的完整名字是【包含 AI 辅助创作,作者对内容负责】——它是责任声明,不是免责声明。 责任由人签,所以这一步不能交给脚本默认。
- 【连续发多篇没关系,反正内容都合格】——错,节奏本身是被治理的对象。 短时间内连续发布会出现验证码或【操作过于频繁】提示,一个可靠的间隔是 60 秒以上。而且【像人一样操作】这件事在这一章有两层意思——技术上的节奏,和内容上的人味——它们是同一条规则的两面。
- 【只要三项全清就一定能发布成功】——不保证,但它把风险挡在了最便宜的位置。 平台的判据没有公开(5.1.1 反复强调过),预检做的是【把已知的规则全部过一遍】。它不能保证你过审,但它能保证【不因为你早就知道的原因而出事】——而在这一章,知道的原因已经很多了。
自测三问(点开对答案)
1. **为什么发布前预检要分成 A、B 两组,而不是列一张清单?** 因为**两类问题的性质和处理方式完全不同**: - **A 组是【发不出去】**(字段/格式不对)——**技术故障**,改完就能发,**顺序无所谓**; - **B 组是【能发出去、但会被处置】**——**风险**,而且**平台按最重的那个处置**,所以**必须按档位从重到轻修**(删除 > 折叠 > 减少分发 > 建议修改 > 标识)。 **混在一起排的后果**:你可能会从第一条开始改,而第一条恰好是最轻的【标识】档,最后一条才是【删除】档——**等改完最轻的,才发现最重的那条根本不该发,前面的力气白花。** 2. **知乎的三路形态,哪一条路支持【定时发布】?这个细节对流程有什么影响?** **只有【文章】支持**定时发布——**回答和想法目前必须即时发出**。 影响是:**【排期】这件事被限制在一条路上。** 如果你想做【晚上十点发】这种节奏控制,**只有文章能用官方定时**;**回答和想法要么当场发,要么先存草稿、到时候再让脚本点发布**——**后者需要额外设计一个【定时唤醒】的环节。** 3. **创作声明那一档为什么不叫【含 AI 生成】,而叫【包含 AI 辅助创作,作者对内容负责】?这个措辞对流程设计有什么含义?** 因为它**不是免责声明,是责任声明**——**标了它等于承诺【我对这篇内容的准确性负责】。** 对流程设计的含义:**声明必须由人来设,不能让脚本默认一个值。** 因为**责任不能由机器签**——这也正是 5.2 把【设声明】和【点发布】放在同一个闸门里的原因:**这两件事都属于【把责任接过来】的动作,必须同一个人、同一个时刻完成。**- ☐ 下一节(5.5 本章小结)
相关笔记
- RPA上传文章到今日头条 —— 上一个发布前预检,排序依据是信用分,本节是处置档位
- RPA上传小红书 —— 另一个【点界面】的出口,本节多了三路形态与声明
- RPA使用墨滴平台排版 —— 本节要粘进编辑器的富文本从哪来
- 知乎图文创作和发布概述 —— 三路形态与处置五档的来处,本节排序依据的出处
- 🤖 Agent+RPA实战 · 索引 —— 全课路线图