使用 Playwright MCP 实现小红书全自动发布的完整流程:TaoToken 统一 Key 配置与验证
发布时间:2026/9/27 11:42:28来源:尧图网络
1. 为什么小红书自动发布总在登录态上翻车做小红书图文自动发布最让人头疼的从来不是写文案而是登录态。你写好了脚本图片也准备好了结果一跑起来就卡在扫码登录、滑块验证、短信验证码上。所谓全自动最后变成了全手动。Playwright MCPModel Context Protocol的出现改变了这个局面。它不再是一个单纯的浏览器控制库而是通过连接到你已经登录好的浏览器会话实现真正的无感知自动化。你只需要手动登录一次小红书之后的发布、互动等操作都可以交给脚本无需再关心复杂的登录验证流程。这篇文章聚焦 Playwright MCP 驱动小红书自动发布的场景从环境准备到发布链路打通。核心交付物有两个一份可复制的 TaoToken 统一 Key 配置骨架settings.json / config.toml 片段以及 MCP 服务连通性验证动作。适合已经会用 Python 写基础脚本、想让小红书发布流程真正跑通的同学。整个链路的关键在于MCP 服务负责调度浏览器TaoToken 负责统一管理模型调用的 Key两者配合才能让自动生成文案 自动发布形成闭环。下面按步骤拆。2. TaoToken 前置统一 Key 与 MCP 通道配置在动手写发布脚本之前先把模型调用的通道配好。小红书自动发布通常需要模型帮你生成标题、正文、话题标签如果每次调用都手动填 Key脚本会变得很脆弱。TaoToken 的作用就是把这些调用统一到一个 Key 上。2.1 获取统一 Key访问 TaoToken 官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册后进入控制台创建 API Key。建议单独建一个用于自动化脚本的 Key方便后续按项目排查用量。创建完成后在控制台的 API Keys 页面复制 Key格式通常以sk-开头。这个 Key 后面会写进配置文件不要直接硬编码在 Python 脚本里。2.2 配置 settings.json 骨架如果你用的是 Claude Code 或类似的 MCP 客户端配置文件一般是settings.json。下面是一份可直接复制的骨架把YOUR_TAOTOKEN_KEY替换成你自己的 Key{ mcpServers: { playwright: { command: npx, args: [playwright/mcplatest], env: { TAOTOKEN_API_KEY: YOUR_TAOTOKEN_KEY, TAOTOKEN_BASE_URL: https://taotoken.net/api } } }, env: { TAOTOKEN_API_KEY: YOUR_TAOTOKEN_KEY, TAOTOKEN_BASE_URL: https://taotoken.net/api } }注意TAOTOKEN_BASE_URL填的是https://taotoken.net/api不要加 UTM 参数否则部分客户端会把它当成非法地址。2.3 config.toml 骨架备选如果你用的是支持 TOML 配置的客户端等价写法如下[mcp_servers.playwright] command npx args [playwright/mcplatest] [mcp_servers.playwright.env] TAOTOKEN_API_KEY YOUR_TAOTOKEN_KEY TAOTOKEN_BASE_URL https://taotoken.net/api [env] TAOTOKEN_API_KEY YOUR_TAOTOKEN_KEY TAOTOKEN_BASE_URL https://taotoken.net/api两份配置的语义完全一致选你客户端支持的那份即可。配好之后先别急着写发布脚本下一步先验证 MCP 服务能不能连通。3. 可复制配置Playwright MCP 环境与浏览器会话配置分两层一层是 MCP 服务本身一层是浏览器会话。很多人只配了第一层结果脚本跑起来发现连不上浏览器问题就出在第二层。3.1 安装 Playwright 与 MCP 服务确保 Python 环境在 3.7 以上然后安装 Playwrightpip install playwright playwright install chromium接着安装 MCP 服务。可以通过 npm 全局安装也可以用 npx 直接运行npm install -g playwright/mcp # 或者 npx playwright/mcplatest如果你不想全局安装直接在 settings.json 里用npx playwright/mcplatest作为 command 也可以客户端会自动拉起。3.2 以调试模式启动浏览器这是实现会话复用的关键步骤。你需要让浏览器以远程调试模式运行MCP 才能连上去# Windows chrome.exe --remote-debugging-port9222 # macOS /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port9222 # Linux google-chrome --remote-debugging-port9222启动后在这个浏览器窗口里手动登录小红书账号完成所有必要的验证步骤。登录完成后不要关闭这个窗口后续脚本会复用它。注意调试端口默认只监听本机不要把它暴露到公网。如果你在容器里跑记得把 9222 端口映射出来但只映射到本机回环地址。3.3 连接已登录浏览器的 Python 骨架下面这段代码负责连接到刚才那个已登录的浏览器实例并检查登录状态import asyncio from playwright.async_api import async_playwright async def connect_to_logged_in_browser(): async with async_playwright() as p: browser await p.chromium.connect_over_cdp(http://localhost:9222) default_context browser.contexts[0] if default_context.pages: page default_context.pages[0] else: page await default_context.new_page() await page.goto(https://www.xiaohongshu.com) try: await page.wait_for_selector([data-testiduser-avatar], timeout5000) print(检测到已登录状态) return page except Exception: print(未检测到登录状态请先手动登录) return None async def test_connection(): page await connect_to_logged_in_browser() if page: await page.screenshot(pathlogin_status.png) print(截图已保存请查看登录状态) asyncio.run(test_connection())跑通这段代码说明 MCP 通道和浏览器会话都正常。如果卡在connect_over_cdp先回到 3.2 检查浏览器是不是真的以调试模式启动了。4. 验证请求从连通性自检到发布链路打通配置写完不算完得验证。验证分两步先验证 MCP 服务本身能响应再验证发布链路能跑通。4.1 MCP 服务连通性自检在客户端里调用一次 MCP 工具看它能不能正常返回。以 Claude Code 为例配置好 settings.json 后重启客户端然后让它执行一个简单的浏览器操作比如打开小红书首页并截图。如果返回了截图路径说明 MCP 服务已经连通。如果客户端报错找不到 MCP 服务检查两点一是npx playwright/mcplatest能不能在终端里单独跑起来二是 settings.json 的 JSON 格式有没有写错尤其是逗号和引号。4.2 发布脚本骨架连通性没问题后写发布脚本。下面是一个包含异常处理和步骤日志的骨架import asyncio import random from playwright.async_api import async_playwright class XiaohongshuAutoPublisher: def __init__(self): self.browser None self.page None async def init_browser(self): try: playwright await async_playwright().start() self.browser await playwright.chromium.connect_over_cdp( http://localhost:9222 ) self.context self.browser.contexts[0] self.page ( self.context.pages[0] if self.context.pages else await self.context.new_page() ) self.page.set_default_timeout(30000) return True except Exception as e: print(f浏览器连接失败: {e}) return False async def goto_create_page(self): try: await self.page.goto( https://www.xiaohongshu.com/create, waitUntilnetworkidle ) await self.page.wait_for_selector( div[contenteditabletrue], timeout15000 ) print(成功进入创作中心) return True except Exception as e: print(f进入创作中心失败: {e}) await self.page.screenshot(patherror_create_page.png) return False async def upload_images(self, image_paths): try: await self.page.wait_for_selector(input[typefile], timeout10000) file_input await self.page.query_selector(input[typefile]) if isinstance(image_paths, str): image_paths [image_paths] await file_input.set_input_files(image_paths) print(图片上传成功) await asyncio.sleep(2) return True except Exception as e: print(f图片上传失败: {e}) await self.page.screenshot(patherror_upload.png) return False async def input_content(self, content, tags): try: editor await self.page.wait_for_selector( div[contenteditabletrue], timeout10000 ) await editor.click(clickCount3) await self.page.keyboard.press(Backspace) await editor.type(content) if tags: tag_text .join([f#{tag} for tag in tags]) await editor.type(tag_text) print(内容输入成功) await asyncio.sleep(random.uniform(1, 3)) return True except Exception as e: print(f内容输入失败: {e}) await self.page.screenshot(patherror_content.png) return False async def publish(self): try: publish_button await self.page.wait_for_selector( button:has-text(发布), timeout10000 ) await publish_button.click() print(已点击发布按钮) try: await self.page.wait_for_selector(text发布成功, timeout15000) print(发布成功) return True except Exception: error_element await self.page.query_selector( text/发布失败|错误/ ) if error_element: error_text await error_element.inner_text() print(f发布失败: {error_text}) else: print(发布状态未知请手动确认) return False except Exception as e: print(f发布操作失败: {e}) await self.page.screenshot(patherror_publish.png) return False async def run(self, content, image_paths, tagsNone): if not await self.init_browser(): return False try: steps [ (self.goto_create_page, 进入创作中心), (lambda: self.upload_images(image_paths), 上传图片), (lambda: self.input_content(content, tags), 输入内容), (self.publish, 发布内容), ] for step_func, step_name in steps: print(f正在执行: {step_name}) if not await step_func(): print(f流程中断于: {step_name}) return False await asyncio.sleep(random.uniform(1, 2)) return True finally: if self.browser: await self.browser.disconnect() async def main(): publisher XiaohongshuAutoPublisher() content Playwright MCP 自动化发布测试内容 image_paths [/path/to/your/image1.jpg] tags [技术分享, 自动化, Playwright] success await publisher.run(content, image_paths, tags) print(自动化发布流程完成 if success else 发布流程失败) if __name__ __main__: asyncio.run(main())4.3 成功结果长什么样跑通后终端会依次打印成功进入创作中心图片上传成功内容输入成功已点击发布按钮发布成功。同时浏览器里能看到笔记已经出现在你的主页。如果卡在某一步脚本会自动截图保存方便你回看当时的页面状态。5. 本篇常见错排查5.1 连接被拒绝无法连接到 localhost:9222最常见的原因是浏览器没有以调试模式启动。回到 3.2 确认启动命令里带了--remote-debugging-port9222。另一个原因是端口被占用换一个端口号比如 9223同时把脚本里的地址一起改掉。5.2 元素找不到或操作超时小红书的前端选择器会变div[contenteditabletrue]这类选择器可能随版本更新失效。解决办法是用多种选择器策略组合并加上重试机制async def smart_click(page, text): selectors [ fbutton:has-text({text}), fdiv:has-text({text}), f//*[contains(text(), {text})], ] for selector in selectors: try: element await page.wait_for_selector(selector, timeout2000) await element.click() return True except Exception: continue print(f找不到文本为 {text} 的元素) return False5.3 风控检测操作被限制或账号被临时锁定自动化操作频率过高会触发风控。应对方式是加入随机延迟、模拟人类操作节奏避免短时间内高频发布。另外不要用同一个账号在极短时间内反复测试发布流程测试阶段可以用草稿箱功能验证链路确认无误后再真正发布。5.4 MCP 服务启动失败如果客户端报 MCP 服务启动失败先在终端里手动跑一次npx playwright/mcplatest看有没有报错。常见问题是 Node.js 版本过低或者 npm 缓存损坏。升级 Node.js 到 18 以上必要时清一下 npm 缓存。6. 把 Key 和 MCP 通道固定下来整条链路跑通之后最值得做的一件事是把配置固定下来。TaoToken 的统一 Key 写进 settings.json 或 config.tomlPlaywright MCP 的启动命令也写进去这样每次换项目、换脚本都不用重新配。如果你后续要做更复杂的自动化比如批量生成文案再批量发布建议把模型调用和发布逻辑拆成两个模块模型调用统一走 TaoToken 的 API发布逻辑统一走 Playwright MCP。这样任何一边出问题排查范围都很清晰。需要长期跑编码或 Agent 类任务的话可以了解一下 Coding Plan把模型调用额度固定下来避免脚本跑到一半因为额度问题中断。配置和 Key 都准备好之后剩下的就是让脚本自己跑了。
网站建设高端定制企业官网