🤖 第四章 应用实战:今日头条批量发布助手
📄 一句话概括

本节是全章唯一真的去碰平台界面的一步——也正因为如此,它是唯一可能把信用分打没的一步。它的核心不是【怎么填这几个框】,而是在填之前,按平台的扣分档位把文章重新排一遍序:先拦能扣 70 分的,再管扣 10 分的。 排完序,才动手。

📝 本节和前面几节的关系

4.4.3 结束时,手上是剪贴板里的富文本;本节把它连同标题、封面、分类、标签、各种声明一起填进头条后台,然后停在发布按钮之前。它是整条链路的出口——也是 4.1 那句定盘星【机器送到草稿箱、人点最后一下】真正落地的地方。

一、先拆穿一个东西:今日头条没有面向个人的发布接口

这一节必须先说这件事,因为网上关于它的错误信息特别多,而且看起来特别专业。

搜【今日头条 API 自动发文】会搜到一批文章,讲得有鼻子有眼:client_id、client_secret、access_token、/v2/article/publish、cover_type……但它们互相矛盾:

说法 出自哪篇
头条的内容发布 API 归属在字节跳动开放平台(open.douyin.com) 某一篇技术博客
官方 OpenAPI 提供 /v2/article/publish 端点,要求 title ≤ 30 字符 另一篇技术博客
调用头条号创作开放平台 API,且此接口需要申请创作开放平台权限 第三篇

三条说法指向三个不同的地方,而且没有一篇给出官方文档链接。 更露馅的是第三篇自己写着:接口返回 200,但文章只保存了标题,正文丢失——这是典型的【照着想象写出来的代码】。

⚠️ 本节的处理方式,以及它为什么值得单独讲一段

本节采用的判断是:今日头条对个人创作者,不提供公开可申请的文章发布接口。 依据有两条: 1. 一份横向调研(对比微信公众号与头条号)的结论很明确:微信公众号有官方发布 API,今日头条没有,并且建议【头条仅手动分发】; 2. 那些讲【头条发文 API】的文章,来源互相矛盾、通篇无官方链接、细节自相指责——它们是 AI 生成的不可信内容。

这件事本身就是本章最好的一课:在批量生产内容的工作里,【信息来源的可靠性】是最重要的技能之一。 因为 AI 生成的稿子最容易混进这种【查不到出处的数字和接口】——你一旦照着写,代码跑不通是小事,把错误信息写进文章发出去才是大事(那是 4.1 里扣 70 分的那一档)。

结论:本节全部走界面自动化。 这和老教程里【头条可以纯 API 自动发文】的说法不同——这一点请以你打开后台时的实际情况为准。

二、界面路径:六步,最后一步故意不走

步 动作
1 浏览器打开头条号后台(mp.toutiao.com)并登录
2 左侧导航 → 创作 → 图文
3 填标题、正文(正文用 4.4.3 的富文本粘贴)
4 上传封面、选分类、加 3 到 5 个标签
5 勾选声明原创、头条广告、头条首发、含 AI 生成内容
6 点【存草稿】——不点【发布】

第 6 步是本节的设计核心,理由在 4.1 已经说过,这里再收一次口:

理由 说明
合规与风控 头条 2026 年的治理公告明确打击批量生产与模板化内容,且矩阵规避也从严。一次批量事故可能把信用分打到 60 分以下(那意味着所有权益关闭,包括分成)
责任归属 【发出去】是一件需要有人负责的事——AI 声明、原创声明、首发声明,勾选它们等于做承诺
不可逆 信用分是扣了就掉的;草稿箱里的东西,删了没有代价

三、字段清单:机器要填的七格

# 字段 值从哪来 注意
1 标题 契约的 title 上限 30 字,超过在信息流里会被截断
2 正文 4.4.3 的富文本 粘贴,不是输入(走剪贴板富文本槽位)
3 封面 cover_path 1 图或 3 图;电脑端推荐 1080×608,比例 1:1 或 16:9
4 分类 人指定(4.3.2 讲过,不由模型定) 必须落在账号的垂直领域,选错削弱推荐
5 标签 tags 3 到 5 个
6 声明 人确认(机器可预填) 原创 / 广告 / 首发 / AI 生成内容
7 草稿 —— 终点
📝 关于【含 AI 生成内容】这一个声明

它是法规要求(《人工智能生成合成内容标识办法》,2025-09-01 施行)在头条的落地:图文要在标题下方或正文开头标注,而且未标注已经明确落进【失实表述】这一档(扣 10 分)。 所以它在流程里的位置是固定的:它是七格里的一格,不是可选项。 4.2 的任务表里那个 ai_flag 字段,就是为了保证它不会在流程里被漏掉。

四、安全预检:按【扣分档位】排序,而不是按【检查项顺序】

这是本节最值钱的设计。常见的做法是把检查项列成一张清单逐条跑——问题在于,它把【扣 70 分的】和【扣 5 分的】混在一张表里,人看到一屏告警,分不出哪条必须现在改、哪条可以顺手改。

本节的做法是按官方扣分档位给检查项排序:

档位 检查什么 处理
扣 70 分 涉政敏感、医疗功效承诺、谣言编造 必须拦下(一律不进草稿箱)
扣 20 分 违规推广/极限词、标题党、引导互粉 需人确认
扣 10 分 AI 内容未标注、分类缺失、标签超限 需人确认
扣 5 分 违规声明首发(首发与跨平台冲突) 需人确认

为什么这样排:因为人的注意力是有限的。 一屏告警里,他只需要先看最上面那一条。如果最上面没有 70 分档的,那这一批就是【可继续、但要顺手改】;如果有,那这一批必须先停下来。

五、动手 Demo:安全预检 + 字段映射干跑

下面这段代码只用 Python 标准库。它做两件事:第一件按扣分档位从高到低列出风险,第二件把【RPA 要往后台哪几格填什么】先干跑一遍——干跑是不点任何按钮的填表演练。

# -*- coding: utf-8 -*-
"""4.4.4 Demo:上传头条前的安全预检 + 字段映射干跑(纯标准库)
   检查按【扣分档位】排序:先拦 70 分的(涉政/谣言/低俗),再管 10 分的(标题党/未标注)"""
import re

# 待上传的 6 篇(含故意埋进的反例)
DOCS = [
 {"id": "T01", "title": "玄关收纳三件套,我最后只留了这三样", "category": "家居",
  "body": "玄关永远堆着鞋和快递盒,出门前翻三分钟找不到钥匙。我把玄关重新收拾了一遍,"
          "只留三件东西,门口却清爽了一倍。", "tags": ["收纳", "玄关", "小户型"],
  "cover": "cover_01.jpg", "ai_flag": True, "claim_original": True, "claim_first": True,
  "cross_posted": False},

 {"id": "T02", "title": "震惊!99% 的人不知道的收纳秘诀,不看后悔一辈子", "category": "家居",
  "body": "收纳的核心就是先扔再买。把不穿的衣服和不用的袋子清掉,你会发现空间比想象的大。",
  "tags": ["收纳", "整理"], "cover": "cover_02.jpg", "ai_flag": True,
  "claim_original": True, "claim_first": True, "cross_posted": False},

 {"id": "T03", "title": "厨房台面分区法", "category": "",
  "body": "台面乱,多半不是东西太多,而是没有分区。按使用频率划成备菜区、灶台区、出菜区。",
  "tags": ["厨房", "收纳", "分区"], "cover": "cover_03.jpg", "ai_flag": False,
  "claim_original": True, "claim_first": False, "cross_posted": True},

 {"id": "T04", "title": "三千块改完阳台,钱花在哪了", "category": "家居",
  "body": "改造阳台最难的不是设计,是控制预算。我这次一共花了三千出头,其中地面铺装占了大头。"
          "动手之前建议先量尺寸,再按预算顺序排优先级。",
  "tags": ["阳台", "改造", "预算", "装修", "省钱", "经验"], "cover": "cover_04.jpg",
  "ai_flag": True, "claim_original": True, "claim_first": True, "cross_posted": False},

 {"id": "T05", "title": "这个民间偏方三天根治老寒腿,亲测有效",
  "category": "健康养生",
  "body": "家里老人膝盖疼了十几年,用了这个方法三天就好了,现在能爬山。这个方法就是坚持热敷加按摩。",
  "tags": ["健康", "养生", "偏方"], "cover": "cover_05.jpg", "ai_flag": True,
  "claim_original": True, "claim_first": True, "cross_posted": False},

 {"id": "T06", "title": "书桌线缆整理,十分钟搞定",
  "category": "数码",
  "body": "桌下线缆乱,问题通常出在插座的数量和位置上。先固定理线槽,把插线板挂上去,"
          "再把每根线收到刚好够用。多出来的线用魔术贴捆好,标上标签,以后换设备不用重新捋。",
  "tags": ["书桌", "理线", "桌面改造"], "cover": "cover_06.jpg", "ai_flag": True,
  "claim_original": True, "claim_first": True, "cross_posted": False},
]

# 按扣分档位组织的检查表(分值取 2026-03-24 生效的公告口径)
RULES = [
 (70, "涉政敏感", r"领导|时政|大案|军演|司法案|歪曲历史"),
 (70, "医疗功效承诺", r"根治|特效|防癌|包治|药到病除|治好|三天就|亲测有效"),
 (70, "谣言/编造", r"独家内幕|不传不是中国人|据说|网传必真"),
 (20, "违规推广/极限词", r"最好的|第一|国家级|绝无仅有|全网最低|免费领"),
 (20, "标题党", r"震惊|99% ?的人不知道|不看后悔|不得不看|竟然是这样"),
 (20, "引导互粉", r"互粉|互关|交个朋友"),
 (10, "AI 内容未标注", None),        # 特殊规则:看 ai_flag
 (10, "命名/分类缺失", None),        # 特殊规则:看 category
 (10, "格式低质(标签超限)", None),  # 特殊规则:看 tags 数量
 (5,  "首发与跨平台冲突", None),      # 特殊规则:看 cross_posted + claim_first
]

problems = []
for d in DOCS:
    for score, label, pat in RULES:
        hit = False
        if label == "AI 内容未标注":
            hit = not d["ai_flag"]
        elif label == "命名/分类缺失":
            hit = not d["category"]
        elif label == "格式低质(标签超限)":
            hit = not (3 <= len(d["tags"]) <= 5)
        elif label == "首发与跨平台冲突":
            hit = d["claim_first"] and d["cross_posted"]
        else:
            hit = bool(re.search(pat, d["title"]) or re.search(pat, d["body"]))
        if hit:
            problems.append((score, label, d["id"], d["title"][:24]))

problems.sort(key=lambda x: -x[0])

print("=" * 68)
print("一、安全检查(按扣分档位从高到低排序)")
print("=" * 68)
if not problems:
    print("  [OK] 无风险项")
for score, label, tid, title in problems:
    print("  [扣 %2d 分] %-4s %-14s %s" % (score, tid, label, title))
print("  风险项 %d 条(其中 70 分档 %d 条,必须先解决再谈其他)"
      % (len(problems), sum(1 for s, _, _, _ in problems if s == 70)))

print()
print("=" * 68)
print("二、阻断判定")
print("=" * 68)
fatal = sorted({tid for s, _, tid, _ in problems if s >= 70})
print("  [必须拦下] %s" % ("、".join(fatal) if fatal else "无"))
print("  [需人确认] %s" % "、".join(sorted({tid for s, _, tid, _ in problems if s < 70})))
print("  可继续(低档风险需一并修正后才入草稿箱):%s"
      % "、".join(d["id"] for d in DOCS if d["id"] not in fatal))

print()
print("=" * 68)
print("三、字段映射干跑(RPA 要往后台哪几格填什么,不发出去)")
print("=" * 68)
FIELD = [("标题", "title"), ("分类", "category"), ("标签", "tags"), ("封面", "cover"),
         ("声明原创", "claim_original"), ("头条首发", "claim_first"), ("AI 声明", "ai_flag")]
print("  %-12s" % "后台字段" + "".join("%-12s" % d["id"] for d in DOCS))
for label, key in FIELD:
    row = "  %-12s" % label
    for d in DOCS:
        v = d[key]
        if isinstance(v, bool):
            v = "是" if v else "否"
        elif isinstance(v, list):
            v = "%d个" % len(v)
        elif v == "":
            v = "【空!】"
        row += "%-12s" % str(v)[:11]
    print(row)
print()
print("  提示:AI 声明这一行如果有【否】,按 4.1 红线二是扣 10 分项,必须先改成【是】。")

实际运行输出(真跑,不是示意):

====================================================================
一、安全检查(按扣分档位从高到低排序)
====================================================================
  [扣 70 分] T05  医疗功效承诺         这个民间偏方三天根治老寒腿,亲测有效
  [扣 20 分] T02  标题党            震惊!99% 的人不知道的收纳秘诀,不看后悔一辈
  [扣 10 分] T02  格式低质(标签超限)     震惊!99% 的人不知道的收纳秘诀,不看后悔一辈
  [扣 10 分] T03  AI 内容未标注       厨房台面分区法
  [扣 10 分] T03  命名/分类缺失        厨房台面分区法
  [扣 10 分] T04  格式低质(标签超限)     三千块改完阳台,钱花在哪了
  风险项 6 条(其中 70 分档 1 条,必须先解决再谈其他)

====================================================================
二、阻断判定
====================================================================
  [必须拦下] T05
  [需人确认] T02、T03、T04
  可继续(低档风险需一并修正后才入草稿箱):T01、T02、T03、T04、T06

====================================================================
三、字段映射干跑(RPA 要往后台哪几格填什么,不发出去)
====================================================================
  后台字段        T01         T02         T03         T04         T05         T06
  标题          玄关收纳三件套,我最后 震惊!99% 的人不知 厨房台面分区法     三千块改完阳台,钱花在 这个民间偏方三天根治老 书桌线缆整理,十分钟搞
  分类          家居          家居          【空!】        家居          健康养生        数码
  标签          3个          2个          3个          6个          3个          3个
  封面          cover_01.jp cover_02.jp cover_03.jp cover_04.jp cover_05.jp cover_06.jp
  声明原创        是           是           是           是           是           是
  头条首发        是           是           否           是           是           是
  AI 声明       是           是           否           是           是           是

  提示:AI 声明这一行如果有【否】,按 4.1 红线二是扣 10 分项,必须先改成【是】。

读结果,四个值得注意的地方:

  1. T05 被单独排在最上面,并被【必须拦下】——它的标题里有【三天根治老寒腿,亲测有效】。注意它明明是【健康养生】这个看起来最正常的分类——在这一批里,它却是唯一一条 70 分档。 这就是排序的价值:如果按文档顺序列,T05 会排在第 5 条,人可能看到第 3 条就开始批量点下了;
  2. T02 一条标题同时踩了三档:扣 20 分的【标题党】+ 扣 10 分的【标签超限】。 这提醒一件事:平台是按【条】扣分的,一条内容违规可能同时触发好几档——所以检查器必须允许一条内容报出多个问题,不能报到第一个就 break;
  3. T03 的两条都是【10 分档但性质不同】:AI 内容未标注 是合规漏项(合同字面上没勾),命名/分类缺失 是策略漏项(这一格本来就该人填)。它俩被并到同一档,是因为平台的扣分档位就是这样定的——检查器按平台的档位走,不按自己的直觉走;
  4. 第三张【字段映射干跑表】是本节最实用的一块——它把【RPA 要往哪几格填什么】一行一行摆在眼前,且一个按钮都没点。干跑的意义就在这里:把填表这一步的【逻辑】和【动作】分开——逻辑在本地跑,错了改代码;动作在界面上做,错了代价很大。
📝 干跑(dry run)这个习惯,值得从本节开始养成

批量操作里最贵的错误,都是在【已经点下去之后】才被发现的。干跑是把【要做什么】先打印出来看一遍:字段对不对、值对不对、有没有空值、有没有该填没填的。 它几乎不花时间,但它挡住的错误往往不可逆。 本章的 4.4.1(请求干跑)、4.4.4(填表干跑)都用了这个手法——它是批量的标准配置,不是谨慎过头。

六、定位控件:用【字段名】,不用【坐标】

这一条直接决定你的脚本能活多久。

做法 结果
写死坐标(第 3 行第 2 个输入框) 后台改一次版,全套脚本失效
按字段名/标签文本查找 后台调整了位置,脚本照样能跑
按【同一个字段在同一个容器里的相对位置】查找 最稳(结合前两种)

为什么这条在本章特别重要:头条后台的前端改版频率不低——改版的常见形式就是【把某几个字段挪个位置、换个名字】。按坐标写,等于把你的脚本的寿命绑死在一次改版上。

同理,本节所有的字段名都用【语义命名】(cover_path、claim_first),不用界面上的中文文字命名——界面上的字会改,语义不会。

七、节奏:本节要和 4.2 一起看

上传是批量里最容易暴露机器特征的一步:同一账号、同一台机器、连续反复提交——这正是平台风控最敏感的三个信号。

所以本节必须接上 4.2 定的那三条节奏规则:

规则 在本节的体现
单账号单日上限(示例 6 篇) 这批超过就顺延,不能一口气全填草稿
两篇之间最小间隔(示例 45 分钟) 填完一篇,等一会儿再填下一篇,且间隔在区间内随机
发布落在活跃时段 由人按排期点发布(机器只负责填)
⚠️ 一个反直觉的点

【草稿箱里堆一堆同风格草稿,本身就是风险。 你可能觉得【还没发出去,先存着没问题】——但批量生产的特征,在【存草稿】这一步就已经暴露了(短时间、同风格、同模板)。 所以节奏不是【发布时才要注意的事】,它从【第一次点存草稿】就开始了。

八、⚠️ 2026 现状:本节设计的五处依据

  1. 头条没有面向个人的发布接口:横向调研的结论明确(微信有,头条没有),且那些讲【头条发文 API】的文章来源互相矛盾、无官方链接。所以本节全部走界面自动化;这一点以你打开后台时的实际情况为准;
  2. 信用分是最硬的约束:满分 100,扣到 60 分及以下所有权益关闭(含分成),30 分及以下禁言 7 天,0 分封禁;2026-03-24 12:00 起施行新档位(违规推广与严重标题党降到 20 分,违法违规、诱导低俗、谣言不实升到 70 分)。本节的档位排序就是照这份公告排的;
  3. AI 声明是扣分项:图文要在标题下方或正文开头标注【含 AI 生成内容】;未标注落进【失实表述】(扣 10 分);
  4. 原创与首发各有门道:同矩阵/同 MCN/同作者的多账号只允许勾选原创一次(违规扣 20 分,180 天内三次永久关闭原创功能);首发据公开整理要求原创、72 小时内头条独发、正文不少于 100 字,低质或频繁低质者首次冻结首发 14 天、二次永久关闭;
  5. 【批量】是治理重点:2026-09-15 公告点名治理 AI 低质批量内容,且明确【通过矩阵账号、更换题材人物、替换表达模板】规避治理的也从严处置。本节把终点停在草稿箱,正是对这一条最直接的回应。

30 秒实验

把 Demo 里 problems.sort(key=lambda x: -x[0]) 这一行临时删掉(不做排序),再跑一次。

会发生什么:

结论:排序不改变【判得对不对】,它改变【人会不会看见】。 而这一屏告警唯一的使用者就是人——看得见,才有用。

这条和本课前面几处的纪律是同一条:机器负责【判定】,人负责【判断】;而要让人的判断生效,机器还得负责【把最重要的东西放到最上面】。

常见误区

  1. 【头条能调 API 自动发文,写个脚本就行】——错。 网上那些教程来源互相矛盾、无官方链接、细节自相指责,是 AI 生成稿。横向调研的结论是:微信公众号有官方发布 API,今日头条没有。 头条这一侧只能走界面自动化。
  2. 【检查项按我习惯的顺序列就行】——错。 要按平台的扣分档位排序:先 70 分(涉政、医疗功效、谣言),再 20 分,再 10 分。人的注意力有限,他只会看最上面那几条——排序决定他先看见什么。
  3. 【一条内容报出第一个问题就够了】——错。 本节 Demo 里的 T02 一条标题同时踩了 20 分档和 10 分档两条。平台是按条扣分的,一条违规可能同时触发多档——检查器必须报全,不能报了第一个就退出。
  4. 【排版稿/字段都对,直接填就行,不用干跑】——错。 干跑把【做什么】和【做】分开:干跑在本地,错了改代码几乎零成本;真填在界面上,错了往往不可逆(草稿箱堆乱的规模、风控特征被记录)。
  5. 【存草稿很安全,可以一口气全塞进去】——错。 批量生产的特征在【存草稿】这一步就已经暴露了(短时间、同风格、同模板)。节奏从第一次点存草稿就开始了,不是发布时才要管的事。
  6. 【脚本按坐标点最省事】——错。 头条后台改版频率不低,改版最常见的动作就是挪字段位置。按字段名/标签文本定位,脚本的寿命会长几倍。
自测三问(点开对答案) 1. **为什么本节的检查要按【扣分档位】排序,而不是按检查项的顺序列?** 因为**这一屏告警唯一的使用者是人,而人的注意力有限,他只会看最上面那几条**。按档位从高到低排,**他第一眼看见的就是【必须拦下】的那一条**(本节 Demo 里的 `T05` 是 70 分档,如果不排序,它会落到第 5 条,人可能看到第 3 条就开始批量点了)。**排序不改变判得对不对,它改变人会不会看见。** 2. **为什么整条链路要停在【草稿箱】,而不做到【发布】?** 三个理由叠在一起:① **合规与风控**——头条 2026 年治理公告明确打击批量生产,且矩阵规避也从严,**一次事故可能把信用分打到 60 分以下(所有权益关闭)**;② **责任归属**——原创、首发、AI 声明这些勾选等于做承诺,**发出去是需要人负责的事**;③ **不可逆性**——**信用分扣了不回来,而草稿删了没有代价**。 3. **什么叫【干跑】,为什么它是批量的标准配置?** **干跑 = 把要做的事先打印出来看一遍,一个按钮都不点。** 本节 Demo 的第三张【字段映射干跑表】就是:把 RPA 要往后台哪几格填什么,一行一行摆在眼前。**它的价值在于把【填表的逻辑】和【填表的动作】分开**——逻辑在本地跑,错了改代码几乎零成本;**动作在界面上做,错了往往不可逆**(风控特征被记录、草稿堆乱)。**它几乎不花时间,挡住的是不可逆的错误。**

相关笔记