简介面向熟悉 Python希望通过爬虫与自动化脚本提升内容发布效率的开发者提供一套今日头条自动发文项目源码。项目综合运用爬虫技术与浏览器自动化从新闻 API、知乎热榜等渠道抓取内容并采用 PyQt5 构建可视化操作界面实现爬取、发布、账号登录等核心功能重点解决了发布按钮必须先预览的问题通过禁用 JS 与并发控制完成无人干预的自动发文流程。资源包含 1 个 Python 主程序脚本、1 个依赖说明文本和 1 个配置文件共 3 个文件整体压缩包仅 8KB轻量易读。当前已有 333 人学习下载。代码还展示了 cookie 登录与每日定时发布如自动发布“舔狗日记”的实际落地效果适合自媒体运营者、爬虫与自动化初学者作为完整工程参考也能帮助理解多线程抓取、界面交互与浏览器自动化协作的完整思路。 写自媒体的人大概都有过这种体验每天要在电脑前登录后台、标题编一遍、正文粘一遍、封面调一遍最后还要确认发布时间。如果手上有三四个平台要同步这一套流程下来一小时就没了。为了解决这个问题我用 Python 写了一个“今日头条自动发文”的自动化脚本把从打开后台到点击发布这一整条链路全部交给程序执行目前已经稳定跑了一阵子单篇文章从登录态校验到发布成功最快不到 25 秒。这篇文章就把整套项目的思路、核心代码片段和实操中踩过的坑完整记录下来。内容主要面向两类人一是做自媒体分发、想减少重复劳动的内容运营者二是刚接触浏览器自动化、想找个真实案例练手的 Python 学习者。你不需要很深的开发基础只要会装 Python 包、能看懂基础语法按文中的步骤走一遍就能跑起来。1. 不做逆向接口我选了浏览器自动化1.1 头条发文的三种实现路径对比做头条自动发文第一反应往往是找官方接口。但头条的创作发布接口并不完全开放给个人开发者我们找了一圈也没有看到直接可用的、能稳定申请到的开放发布通道所以这条路对普通内容运营者来说基本走不通。第二条路是逆向内部接口。简单说就是通过抓包分析头条创作平台提交文章时调用的私有接口然后用代码模拟这些请求。这条路听起来高效实际维护成本却非常高发布接口通常有签名参数、加密逻辑、风控校验平台只要稍微调整算法脚本就会立刻失效。更要命的是一旦代码触发平台的风控策略账号可能被限制发文这种风险我承担不起。第三条路就是浏览器自动化用 Playwright 或 Selenium 模拟真实用户在浏览器里的操作。它不依赖私有接口不涉及解密签名本质上是“用人家的网页办自己的事”。头条后台的交互逻辑再怎么变只要页面元素还在脚本调整选择器就能适配风险和工作量都可控。我把三条路径整理成了对比表方便你做决定。实现路径开发难度稳定性账号风险适用场景官方开放接口高申请门槛高高低企业级批量接入个人基本拿不到逆向内部接口极高低改版即失效高不推荐除非你只想短时间跑量浏览器自动化中等中高低模拟真人操作个人分发、多账号运营、定时发文选型结论很明确对个人和中小团队来说浏览器自动化是投入产出比最高的方案。1.2 为什么最终锁定 Playwright 而不是 SeleniumSelenium 是老牌工具资料多但有几个痛点元素等待策略比较原始以前写 Selenium 经常要 sleep 一大段动态页面元素渲染慢的时候定位失败是家常便饭另外对现代浏览器的自动化控制总感觉不够顺手。Playwright 是微软开源的工具在处理动态页面体验上好很多。它内置了自动等待机制——页面上元素没出现、不可见、不可用的时候定位操作会一直等到超时为止这直接解决了“页面还没加载完就找不到元素”的经典问题。它的选择器体系也更灵活支持 text、css、xpath、frame 等多种定位方式处理 iframe 嵌套比 Selenium 简单许多。对头条后台这种重度动态渲染的页面来说Playwright 的稳定性和容错性都更让人省心所以最终选了它。2. 核心代码模块拆解2.1 登录态保持用持久化上下文解决反复扫码问题自动发文绕不开登录。头条后台的账号密码登录往往伴随滑块验证和手机验证码处理起来非常麻烦。我的方案是用 Playwright 的持久化上下文把登录后的 cookie、localStorage 保存在本地目录里第一次手动扫码登录一次之后脚本启动就是正常的登录态。核心代码大概是这样的from playwright.sync_api import sync_playwright USER_DATA_DIR ./toutiao_profile def create_context(playwright): context playwright.chromium.launch_persistent_context( USER_DATA_DIR, headlessFalse, # 跑自动化时保持有头模式更不容易触发风控 viewport{width: 1280, height: 800}, user_agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 ) return context这里有个细节launch_persistent_context会在当前机器上生成一个浏览器用户目录登录成功后的所有状态都存在里面。下次再次启动时它会直接以“上次登录的浏览器”身份打开页面免去扫码流程。用不同的目录存不同账号的登录态就能实现多账号并存且互不干扰。如果你发现启动后依然没有登录态最常见的原因是清理了目录或者浏览器多开导致用户目录被占用。记得保持每次运行使用同一个目录不要手动去删里面的文件。2.2 编辑器操作标题、正文、封面的自动化填写登录之后进入头条创作后台的“发布文章”页面核心操作是填标题、写正文、选封面。头条的标题框是一个单行输入框可以直接用fill写入正文区域是 contenteditable 富文本编辑器fill对它无效我用的是逐段键盘输入或者走剪贴板方式粘入。标题和正文的核心逻辑如下def fill_article(page, title, content): # 标题输入框 title_locator page.locator(input[placeholder*标题]) # 有的版式可能用 textarea需要用 css 或 xpath 二次确认 title_locator.wait_for(statevisible, timeout15000) title_locator.fill(title) # 正文区是一个 contenteditable 区域先点击激活再输入 editor page.locator(div[contenteditabletrue]).first editor.click() # 内容较长时一次输入易丢字我用“分段写入”的方式 for paragraph in content.split(\n): paragraph paragraph.strip() if not paragraph: continue page.keyboard.type(paragraph, delay30) page.keyboard.press(Enter) page.wait_for_timeout(300)正文分段写入这个思路是遇到丢字问题之后调整出来的。一次性粘贴大段文字时头条的编辑器偶尔会丢失尾段或者触发自动保存覆盖分段输入模拟的是真人逐段打字的行为成功率明显更高。封面可以直接不单独设置。头条支持自动从正文首图匹配封面如果正文里有一张合适的图发布时会自动带上。我在脚本里省掉了封面上传步骤减少一个不稳定的交互点。需要自定义封面时再单独写一个点击“选择封面”区域、按路径上传文件的逻辑。2.3 发布动作与结果校验文章填完后发布会先弹出一系列设置项比如声明原创、自动封面等之后点击发布按钮。发布按钮的文案是“发布”但也有可能飘在顶部的“发布”只是触发下拉菜单里面还有“发布到头条”这种二级按钮。所以脚本里我做了两步点击再用页面出现的提示文字判断是否成功。结果校验我统一放在最后def publish_article(page): publish_btn page.get_by_role(button, name发布).first publish_btn.click() page.wait_for_timeout(1000) # 如果出现“发布到头条”等二级按钮点击它 confirm page.locator(text发布到头条) if confirm.count() 0: confirm.first.click() # 等待“发布成功”或“已发布”提示出现 try: page.wait_for_selector(text发布成功, timeout20000) return True except Exception: return False发布成功的文案有时候是“已发布”有时候是“发布成功请等待审核”。不同账号版本可能有差异。稳妥的做法是在wait_for_selector里传一个多文本选择器比如text发布成功, text已发布只要任意一个出现就算成功。3. 实操过程从环境搭建到第一篇文章自动发出3.1 环境准备与配置项在看代码之前先把环境准备好。我的项目基于 Python 3.10依赖就两个核心包playwright和pyperclip备用剪贴板方案。安装命令如下pip install playwright pyperclip playwright install chromiumplaywright install chromium会下载浏览器内核这一步在国内网络环境下可能需要一点耐心下载完成后脚本就能调起浏览器了。我把可变的参数都集中放在一个config.json里脚本运行时读取配置而不是去改代码。这样换账号、换文章文件都很方便{ user_data_dir: ./toutiao_profile, article_file: ./articles/demo.txt, headless: false, wait_timeout: 20000 }头条创作平台的地址直接写在脚本常量里登录后它会跳转到创作后台首页。建议你第一次运行时先把headless保持为false亲眼看看浏览器窗口内每一步操作是否正常确认全流程 OK 后再考虑是否改为无头模式。3.2 完整运行流程现场记录整个流程一共三步准备文章、运行脚本、确认结果。我这里放一个最小可跑的完整代码结构你可以直接照着搭import json from playwright.sync_api import sync_playwright def main(): with open(config.json, r, encodingutf-8) as f: cfg json.load(f) with sync_playwright() as p: context p.chromium.launch_persistent_context( cfg[user_data_dir], headlesscfg[headless] ) page context.new_page() # 1. 打开头条创作平台 page.goto(https://mp.toutiao.com, wait_untilnetworkidle) page.wait_for_timeout(2000) # 2. 判断当前是否已登录未登录则人工扫码 if 登录 in page.title(): print(请在弹出的浏览器窗口中完成扫码或手动登录...) input(登录完成后按下回车继续...) # 3. 进入发布文章页 page.goto(https://mp.toutiao.com/profile_v4/graphic/publish, wait_untilnetworkidle) page.wait_for_timeout(2000) # 4. 读取本地文章填入页面 with open(cfg[article_file], r, encodingutf-8) as f: content f.read() title content.strip().split(\n, 1)[0] body content.strip().split(\n, 1)[1] fill_article(page, title, body) publish_article(page) context.close() if __name__ __main__: main()实际运行时脚本打开浏览器、等待登录校验、跳转发布页、填入标题正文、点击发布每一步都有对应的等待和日志输出。第一次完整跑通那一瞬间那种“再也不用手动发头条”的轻松感是真的爽。3.3 时间与频率控制很多人的发文需求是定时发布。头条发布页有“定时发布”功能脚本里只需要在发布前切换到定时发布按钮然后把时间填入日期和时间输入框即可。填日期的时候要注意输入框格式一般是用fill写入2025-06-18 09:30这种格式如果没有生效就点击输入框后手动输入。频率控制是一个很容易忽略但很重要的点。同一账号连续自动发很多篇的话即使操作完全模拟真人也可能触发平台的风控保护表现为突然弹出一个“操作过于频繁”的提示。我的处理是在两篇文章之间强制等待 60 到 120 秒随机间隔同时单日发文量控制在合理范围。自动化是提效工具不是批量刷量工具把握好度才能长久稳定。4. 踩坑实录与常见问题排查4.1 滑块验证与登录风控第一次跑脚本时在登录环节遇到了滑块拼图验证Playwright 虽然能操作页面但这种验证码通过机械坐标拖动成功率很低反复尝试反而会增加账号风险。我的处理方式是脚本检测到滑块出现时不做任何自动尝试直接弹窗提醒让我手动完成。因为滑块只在登录或敏感操作时出现手动处理一次后持久化上下文里就保存了合法的登录态后续长时间内都不需要再验证。不要把自动化脚本的优先级放在账号安全之前遇到验证码果断转人工这是最稳定也最安全的策略。4.2 iframe 与动态元素定位失败头条后台页面里正文编辑器是被包在 iframe 里的。刚开始我总是定位不到正文区控制台报错找不到元素后来检查页面结构才发现要先切换到对应的 frame 才能操作里面的 DOM。Playwright 处理这个比 Selenium 顺手很多直接提供frame_locatorframe page.frame_locator(iframe[class*editor]) editor frame.locator(div[contenteditabletrue]) editor.click()另外一个更隐蔽的问题是头条后台改版频率不低同一页面的按钮文案或者 class 偶尔会变。脚本跑不通时第一件事就是在浏览器里手动打开目标页面按 F12 检查当前选择器是否还匹配不要盲目地去代码里加 sleep。动态等待超时上限我设为 20 秒超过这个时间还没定位到基本就是选择器失效了。4.3 中文输入丢字或触发自动保存提示这个坑值得单独说。用fill给标题框写中文时偶尔会出现最后一个字没写进去的情况正文如果一次性粘入几千字偶尔也会触发编辑器的自动保存异常导致页面提示“草稿已保存失败”或者光标位置错乱。我的解决方法是标题写在fill之后再读一次输入框的值检查是否等于预期标题不等就重新fill正文则做成按段落输入每段之间留 300 毫秒的间隔。虽然整体时间会多花几秒但稳定性提升非常明显。如果输入的文章里包含 Markdown 语法符号头条编辑器不识别这些符号会原样显示。所以在写入之前最好先在代码里把#、*、这类 Markdown 标记清洗掉只保留纯文本段落。4.4 发布频次过高导致异常连续自动发布多篇文章时偶尔会遇到提示“操作频率过快请稍后再试”。这其实不是 bug而是平台的合理风控。一开始我把间隔设成 5 秒结果发了几篇就触发限制。后来我总结出一个稳妥的节奏两篇间隔至少 60 秒以上单日总量控制在个位数遇到“操作频繁”提示就立刻停止当天任务切到手动模式观察一段时间。自动化工具的价值是节省重复劳动而不应该是和风控系统对抗。4.5 一些容易被忽略的小细节浏览器窗口尽量保持前台运行。有头模式下如果浏览器窗口被最小化部分页面交互尤其是弹窗和悬浮层可能表现异常。我自己跑脚本的电脑上会单独开一个虚拟桌面把自动化浏览器固定在那边。失败自动截图。发布失败时脚本会page.screenshot(pathlogs/fail.png)把现场画面存下来。排查问题的时候一张截图比十行日志都管用。注意头条后台改版。后台结构一改脚本定位器就可能失效。建议每隔一段时间手动登录后台看一眼确认发布按钮文案和编辑器结构是否变化。这是自建脚本类项目逃不掉的维护成本。5. 还能怎么扩展这个脚本目前解决的是“单篇文章自动发到头条”这个最基础的问题实际使用中我还在上面做了几个扩展你参考着用从本地 Markdown 目录批量读取文章逐篇自动发布配合随机间隔就做成了一套简单的批量分发工具。把发布时间设为凌晨等低峰时段用脚本提前设置好第二天起来文章已经发布完成。用不同的user_data_dir目录管理多个账号多账号分发时互不占用登录态。不同账号发布的频率、数量各自独立控制避免集体触发风控。内容来源方面我也试过接 AI 接口自动生成初稿但头条对文章质量和原创度有持续要求纯 AI 生成的稿子不经修改直接发布长期看风险较大。所以我的脚本最终只负责“发布”这个环节内容仍然由人来写、人来审。最后再说一个我自己的体会自动化发文工具的最大价值不是“全自动赚钱”而是把从内心抗拒的机械操作消解掉把节省出来的精力放在选题和内容本身。工具是放大器内容才是一切的基础。如果这篇记录能让你少走几步弯路那我踩过的这些坑也算没白踩。本文还有配套的精品资源点击获取
