资讯动态

基于MCP与Playwright构建小红书内容自动化发布工作流

发布时间:2026/8/16 13:25:09 来源:尧图企业网站定制
1. 项目缘起从手动“肝”笔记到自动化“种”草做内容运营或者个人博主的朋友对“日更”这个词一定又爱又恨。爱的是它带来的稳定流量和粉丝粘性恨的是背后日复一日的选题、找图、写文案、排版、发布这套繁琐流程。尤其是在小红书这样的平台图片要精美文案要“网感”标签要精准手动操作一次少说也得十几二十分钟。我之前运营一个垂直领域的小红书账号为了保持日更几乎每天都要专门划出一块时间来做这件事效率低不说还经常因为重复劳动感到疲惫和创意枯竭。直到我接触到“MCP”这个概念事情开始有了转机。MCP全称是Model Context Protocol你可以把它理解为一个让不同AI工具和应用之间能够“对话”和“协作”的标准协议。简单来说它就像给各种AI能力比如写文案的GPT、画图的DALL-E、分析数据的工具装上了统一的USB接口然后通过一个“主控大脑”比如Claude、Cursor等支持MCP的AI助手来指挥它们协同工作。这让我想到既然MCP能让AI工具链动起来那能不能用它来把小红书发帖这个固定流程给自动化了呢这个想法并非空穴来风。看看网络上的讨论热词“小红书爬虫”、“自动化测试”、“jenkins自动化部署”、“playwright自动化框架”……这些词背后是技术圈对“自动化”解决重复性工作的普遍追求。而“小红书协议评论点赞”、“小红书图片下载”这类词则隐隐指向了平台交互自动化这个灰色地带。我的目标很明确不走那些可能违规的“协议”路线而是利用公开、合法的前端自动化技术比如Playwright结合MCP赋予的AI智能编排能力打造一个合规、高效、可定制的小红书内容自动发布工作流。这不仅仅是省时间更是将我从执行者解放为策略制定者和监督者让AI去处理那些标准化的“体力活”。2. MCP与自动化技术栈的融合为什么是Playwright AI Agent要实现自动化发帖我们得先拆解“发帖”这个动作。在小红书Web端或移动端它至少包含登录、进入发布页、上传图片/视频、输入标题和正文、添加地点/话题标签、选择封面、最终发布。手动操作是线性的但用程序实现就需要考虑稳定性、可维护性和抗平台变更能力。市面上常见的自动化方案主要有两类一类是基于逆向工程的“协议”调用直接模拟App对服务器的请求另一类是基于UI操作的“模拟点击”。前者效率高但风险也高涉及对私有协议的破解容易触发平台风控甚至导致封号从“小红书协议”这类热词的模糊性就能看出其灰色属性。后者虽然慢一些但完全模拟真人操作行为更合规更贴近真实用户场景。我们的目标是长期、稳定、安全地使用因此UI自动化是更稳妥的选择。在UI自动化框架中Selenium历史悠久Playwright则是后起之秀。我选择Playwright主要原因有三点对现代Web技术的支持更好Playwright由微软开发对单页应用SPA、Shadow DOM等现代Web特性的支持更成熟而小红书的Web端正是典型的复杂SPA。自动等待机制Playwright内置了智能等待能自动等待元素出现、可点击、网络请求完成等大大减少了编写time.sleep这类不稳定等待的代码量脚本更健壮。多浏览器与移动端模拟它支持Chromium、Firefox、WebKit并且可以非常方便地模拟移动设备如iPhone 13的视口和User-Agent这对于需要适配移动端布局的小红书发布页至关重要。确定了“手”Playwright之后我们需要“大脑”AI来指挥。这就是MCP的用武之地。传统的自动化脚本是写死的第一步打开浏览器第二步输入账号……缺乏灵活性。如果发布流程变了比如小红书新增了一个“添加商品”的步骤脚本就得重写。而MCP的理念是将能力工具化。我们可以构建或利用现成的MCP Server一个负责内容生成的MCP Server调用大语言模型API根据关键词或大纲生成符合小红书风格的标题和正文。一个负责图片处理的MCP Server调用图像生成或编辑API根据文案主题创建或优化图片。Playwright MCP Server这是关键我们需要一个能将Playwright操作如点击、输入、上传文件封装成MCP工具的Server。这样AI主控如Claude Desktop里配置了MCP的Claude就能通过自然语言或结构化指令来调用这些浏览器操作。整个工作流就变成了AI主控大脑接收任务如“发布一篇关于周末咖啡馆探店的笔记”它先调用内容生成工具MCP Server A创作文案再调用图片生成工具MCP Server B配图最后指挥Playwright工具MCP Server C在小红书网页上执行一系列发布操作。所有步骤通过MCP协议串联形成一个智能体Agent。注意这里提到的“Playwright MCP Server”并非官方标准产品需要我们自己利用Playwright的API和MCP的SDK进行封装。这也是本项目的核心开发部分。3. 构建你的Playwright MCP Server从零到一的工具封装要让AI能指挥Playwright我们必须先创建一个MCP Server它对外提供一系列“工具”比如open_browser,goto_page,fill_text,upload_image,click_button等。下面我将以Node.js环境为例展示核心的搭建步骤。选择Node.js是因为Playwright对其支持非常完善且MCP的官方SDK也有TypeScript/JavaScript版本。3.1 环境准备与项目初始化首先确保你的系统已安装Node.js建议版本18以上和npm。然后初始化项目并安装关键依赖。# 创建一个新目录并进入 mkdir playwright-mcp-server cd playwright-mcp-server # 初始化npm项目 npm init -y # 安装核心依赖 npm install modelcontextprotocol/sdk playwright # 安装Playwright的浏览器内核以Chromium为例 npx playwright install chromium # 安装TypeScript及相关类型定义可选但推荐 npm install --save-dev typescript types/node npx tsc --init关键依赖说明modelcontextprotocol/sdk这是实现MCP Server的官方SDK它定义了Server与Client如Claude之间的通信协议和标准接口。playwrightUI自动化核心库。我们安装的是其Node.js版本。3.2 定义MCP工具将浏览器操作抽象成APIMCP Server的核心是向客户端声明自己有哪些“工具”可用。每个工具都有名称、描述、参数列表。我们需要把小红书发帖的关键步骤抽象成工具。创建一个src/server.ts文件import { Server } from modelcontextprotocol/sdk/server/index.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; import { CallToolRequestSchema, ListToolsRequestSchema } from modelcontextprotocol/sdk/types.js; import { chromium, Browser, Page } from playwright; // 声明工具的类型和参数 interface Tool { name: string; description: string; inputSchema: { type: object; properties: Recordstring, any; required?: string[]; }; } class PlaywrightMCPServer { private server: Server; private browser: Browser | null null; private page: Page | null null; constructor() { this.server new Server( { name: playwright-mcp-server, version: 0.1.0, }, { capabilities: { tools: {}, }, } ); this.setupToolHandlers(); this.setupConnection(); } private setupToolHandlers() { // 1. 列出可用工具 this.server.setRequestHandler(ListToolsRequestSchema, async () ({ tools: [ { name: open_browser, description: 启动一个无头浏览器并创建新页面。可以指定是否显示浏览器界面。, inputSchema: { type: object, properties: { headless: { type: boolean, description: 是否为无头模式不显示界面默认为true }, }, }, } as Tool, { name: navigate_to, description: 控制浏览器导航到指定的URL。, inputSchema: { type: object, properties: { url: { type: string, description: 要访问的完整URL }, }, required: [url], }, } as Tool, { name: fill_input, description: 在指定的输入框元素中输入文本。需要先通过CSS选择器定位元素。, inputSchema: { type: object, properties: { selector: { type: string, description: CSS选择器用于定位输入框 }, text: { type: string, description: 要输入的文本内容 }, }, required: [selector, text], }, } as Tool, { name: upload_file, description: 通过文件输入框上传本地文件。, inputSchema: { type: object, properties: { selector: { type: string, description: CSS选择器用于定位文件输入框input[typefile] }, filePath: { type: string, description: 本地文件的绝对路径 }, }, required: [selector, filePath], }, } as Tool, { name: click_element, description: 点击页面上符合CSS选择器的元素。, inputSchema: { type: object, properties: { selector: { type: string, description: CSS选择器用于定位要点击的元素 }, }, required: [selector], }, } as Tool, // ... 可以继续添加更多工具如 get_page_content, screenshot, wait_for_selector 等 ], })); // 2. 处理工具调用请求 this.server.setRequestHandler(CallToolRequestSchema, async (request) { const { name, arguments: args } request.params; try { let result: any; switch (name) { case open_browser: result await this.openBrowser(args as { headless?: boolean }); break; case navigate_to: result await this.navigateTo(args as { url: string }); break; case fill_input: result await this.fillInput(args as { selector: string; text: string }); break; case upload_file: result await this.uploadFile(args as { selector: string; filePath: string }); break; case click_element: result await this.clickElement(args as { selector: string }); break; default: throw new Error(未知工具: ${name}); } return { content: [{ type: text, text: JSON.stringify(result, null, 2) }], }; } catch (error: any) { return { content: [{ type: text, text: 工具执行失败: ${error.message} }], isError: true, }; } }); } // 工具的具体实现 private async openBrowser(args: { headless?: boolean }) { const headless args.headless ?? true; this.browser await chromium.launch({ headless }); this.page await this.browser.newPage(); // 模拟移动设备这对小红书很重要 await this.page.setViewportSize({ width: 390, height: 844 }); // iPhone 13尺寸 await this.page.setUserAgent(Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1); return { success: true, message: 浏览器已启动无头模式: ${headless} }; } private async navigateTo(args: { url: string }) { if (!this.page) throw new Error(请先打开浏览器); await this.page.goto(args.url, { waitUntil: networkidle }); return { success: true, message: 已导航至: ${args.url} }; } private async fillInput(args: { selector: string; text: string }) { if (!this.page) throw new Error(请先打开浏览器并导航到页面); await this.page.waitForSelector(args.selector, { state: visible }); await this.page.fill(args.selector, args.text); return { success: true, message: 已在选择器 ${args.selector} 中输入文本 }; } private async uploadFile(args: { selector: string; filePath: string }) { if (!this.page) throw new Error(请先打开浏览器并导航到页面); await this.page.waitForSelector(args.selector, { state: visible }); // Playwright 的 setInputFiles 方法专门用于文件上传 await this.page.setInputFiles(args.selector, args.filePath); return { success: true, message: 已上传文件: ${args.filePath} }; } private async clickElement(args: { selector: string }) { if (!this.page) throw new Error(请先打开浏览器并导航到页面); await this.page.waitForSelector(args.selector, { state: visible }); await this.page.click(args.selector); return { success: true, message: 已点击元素: ${args.selector} }; } private async setupConnection() { const transport new StdioServerTransport(); await this.server.connect(transport); console.error(Playwright MCP Server 已通过 stdio 启动); } } // 启动服务器 new PlaywrightMCPServer();这个Server定义了几个核心工具。它通过标准输入输出stdio与MCP客户端通信。接下来我们需要编译并运行它。3.3 配置AI客户端以使用自定义Server以Claude Desktop为例我们需要修改其配置文件告诉它去哪里找我们这个自定义的Playwright MCP Server。找到Claude Desktop的配置文件位置macOS通常在~/Library/Application Support/Claude/claude_desktop_config.jsonWindows在%APPDATA%\Claude\claude_desktop_config.json在mcpServers部分添加我们的Server配置{ mcpServers: { playwright: { command: node, args: [ /ABSOLUTE/PATH/TO/YOUR/playwright-mcp-server/build/server.js ], env: { NODE_ENV: production } } // ... 可以同时配置其他MCP Server如AI写作、图片生成等 } }配置完成后重启Claude Desktop。如果配置成功你在和Claude对话时它应该能“看到”并可以使用open_browser,navigate_to等我们定义的工具了。你可以尝试用自然语言指挥它“请用playwright工具打开浏览器访问百度首页。” Claude会尝试调用相应的工具。4. 小红书发布流程的自动化拆解与实战编码有了能指挥Playwright的AI大脑接下来就需要把小红书的具体发布流程翻译成一系列精确的工具调用指令。这一步的关键在于元素定位——我们必须找到页面上每一个需要操作的按钮、输入框所对应的CSS选择器。提示由于小红书前端代码会频繁更新以下选择器仅为示例在实际操作中可能需要使用浏览器开发者工具F12重新检查并调整。优先使用具有稳定>// 在PlaywrightMCPServer类中添加一个工具和对应的实现方法 // 工具声明 { name: save_storage_state, description: 将当前页面的存储状态如cookies、localStorage保存到指定文件。用于持久化登录状态。, inputSchema: { type: object, properties: { path: { type: string, description: 保存状态的JSON文件路径 }, }, required: [path], }, }, { name: load_storage_state, description: 从指定文件加载存储状态到当前上下文恢复登录会话。, inputSchema: { type: object, properties: { path: { type: string, description: 存储状态JSON文件路径 }, }, required: [path], }, } // 工具实现 private async saveStorageState(args: { path: string }) { if (!this.page?.context()) throw new Error(无有效的浏览器上下文); await this.page.context().storageState({ path: args.path }); return { success: true, message: 存储状态已保存至: ${args.path} }; } private async loadStorageState(args: { path: string }) { if (!this.browser) throw new Error(请先打开浏览器); // 加载状态通常需要在创建浏览器上下文时进行这里简化处理重新创建一个带状态的上下文 const context await this.browser.newContext({ storageState: args.path }); this.page await context.newPage(); await this.page.setViewportSize({ width: 390, height: 844 }); // ... 设置UserAgent return { success: true, message: 已从 ${args.path} 加载存储状态 }; }操作流程手动编写一个脚本或用我们已实现的工具打开浏览器headless: false显示界面导航到小红书登录页https://www.xiaohongshu.com手动完成登录。登录成功后调用save_storage_state工具将状态保存为xiaohongshu_state.json。后续的自动化脚本第一步就是调用load_storage_state加载这个文件从而绕过登录环节。登录后导航到发布页。小红书Web端的发布入口通常在主站。我们可以用工具组合来完成1. 调用 navigate_to url: https://www.xiaohongshu.com 2. 调用 click_element selector: div[data-v-xxxxxx] button.publish-btn (选择器需实际查找)4.2 内容填充与图片上传发布页面的核心是内容编辑区。我们需要定位标题输入框、正文输入框、图片上传按钮。// 假设我们已经进入了发布页面 // 填充标题 await fill_input({ selector: textarea[placeholder填写标题可能会有更多赞哦], text: AI帮我发的第一篇笔记 | 周末咖啡馆探店 }); // 填充正文 await fill_input({ selector: div[contenteditabletrue].post-editor, text: 这家藏在胡同里的咖啡馆真的太绝了\n☕️冰博客Dirty一口入魂\n巴斯克蛋糕口感绵密不甜腻\n... #咖啡馆探店 #周末去哪儿 #我的咖啡日记 }); // 上传图片 - 可能需要点击一个按钮触发文件选择框 await click_element({ selector: div.upload-area button // 上传区域的按钮 }); // 然后使用 upload_file 工具选择器通常是隐藏的 input[typefile] await upload_file({ selector: input[typefile][acceptimage/*], filePath: /Users/yourname/Desktop/coffee_shop_1.jpg }); // 小红书支持多图可能需要重复上传操作或一次选择多个文件setInputFiles支持文件数组这里有一个关键细节小红的富文本编辑器可能不是标准的textarea或input而是一个contenteditable的div。Playwright的page.fill()方法对这类元素同样有效。如果遇到无法输入的情况可以尝试先用page.click()聚焦该元素再用page.keyboard.type()模拟打字。4.3 添加标签与发布添加话题标签和地点是小红书笔记获得流量的关键。通常页面会有明确的“添加标签”或“地点”按钮。// 点击添加话题标签按钮 await click_element({ selector: button:has-text(添加标签) }); // 在弹出的搜索框中输入话题 await fill_input({ selector: input[placeholder搜索话题], text: 咖啡馆探店 }); // 等待搜索结果出现并选择第一个这里需要更复杂的交互逻辑可能需要组合 wait_for_selector 和 click await wait_for_selector({ selector: div.topic-item:first-child, state: visible }); await click_element({ selector: div.topic-item:first-child }); // 添加地点类似 await click_element({ selector: button:has-text(添加地点) }); await fill_input({ selector: input[placeholder搜索地点], text: 北京三里屯 }); // ... 选择地点 // 最后点击发布按钮 await click_element({ selector: button.publish-button:has-text(发布) }); // 发布成功后可以等待一个成功提示元素出现 await wait_for_selector({ selector: div.success-message, state: visible, timeout: 10000 // 10秒超时 });4.4 整合成可执行的AI指令流现在我们可以将上述所有步骤组织成一段给AI如Claude的“剧本”或提示词。因为AI已经通过MCP具备了调用Playwright工具的能力我们可以这样描述任务“请使用playwright工具执行以下步骤来发布一篇小红书笔记加载之前保存的登录状态文件/path/to/xiaohongshu_state.json。导航到小红书首页https://www.xiaohongshu.com。点击发布按钮你需要用开发者工具查看当前页面找到发布按钮的正确选择器。在标题输入框选择器可能是textarea[placeholder*标题]输入‘周末放松好去处 | 发现一家宝藏咖啡馆’。在正文编辑器选择器可能是一个contenteditabletrue的div输入我提供的正文内容‘...’。点击图片上传区域并上传本地图片/path/to/image1.jpg。点击‘添加标签’按钮搜索并选择‘咖啡馆探店’和‘周末去哪儿’这两个话题。点击‘添加地点’按钮搜索并选择‘北京三里屯’。最后点击发布按钮。 请在每个步骤后确认页面状态是否如预期如果元素找不到请尝试等待或使用备用选择器。”AI会根据这个指令逐步调用我们封装好的MCP工具去执行。这比写死代码灵活得多你可以随时用自然语言调整发布流程。5. 避坑指南与进阶优化让自动化流程更稳健在实际操作中你会遇到各种各样的问题。以下是我在搭建和测试过程中总结的几个核心坑点及解决方案。5.1 元素选择器失效动态Class与数据属性小红书作为大型SPA其前端元素的类名如.xxxxxx经常是编译后生成的哈希值每次更新都可能变化极不稳定。解决方案优先使用>// 在工具列表中声明一个高级工具 { name: publish_xiaohongshu_note, description: 发布一篇小红书笔记。需要已登录状态。, inputSchema: { type: object, properties: { title: { type: string }, content: { type: string }, imagePaths: { type: array, items: { type: string } }, topics: { type: array, items: { type: string } }, location: { type: string }, }, required: [title, content, imagePaths], }, } // 在工具处理函数中实现这个高级流程 private async publishXiaohongshuNote(args: { title: string; content: string; imagePaths: string[]; topics?: string[]; location?: string }) { // 1. 导航到发布页这里需要先找到发布入口的选择器 await this.page?.goto(https://www.xiaohongshu.com/creation); // 2. 填充标题和正文 await this.page?.fill(textarea[placeholder*标题], args.title); await this.page?.fill(div[contenteditabletrue].editor, args.content); // 3. 上传多张图片 const uploadInput await this.page?.$(input[typefile][acceptimage/*]); if (uploadInput) { await uploadInput.setInputFiles(args.imagePaths); } // 4. 添加话题循环处理 if (args.topics) { for (const topic of args.topics) { await this.page?.click(button:has-text(添加标签)); await this.page?.fill(input[placeholder*搜索话题], topic); await this.page?.waitForTimeout(500); // 等待下拉列表 await this.page?.click(div.topic-item:first-child); } } // 5. 添加地点 if (args.location) { // ... 类似逻辑 } // 6. 点击发布并等待成功 await this.page?.click(button:has-text(发布)); await this.page?.waitForSelector(div:has-text(发布成功), { timeout: 15000 }); return { success: true, noteId: 从成功页面解析出的ID如果有 }; }这样AI只需要调用这一个高级工具并传递内容参数即可整个流程的稳定性和效率都更高。错误处理如重试、超时、截图记录失败现场也可以在这个高级函数内统一完成。6. 将一切串联构建你的自动化内容工作流至此我们拥有了一个可被AI调用的Playwright MCP Server也清楚了小红书发布的每一步。最终的自动化工作流可以这样设计内容生成阶段利用另一个AI写作MCP Server或直接使用Claude的对话能力根据你的选题指令如“生成一篇关于夏日办公室好物的小红书文案”产出标题、正文和图片生成提示词。图片生成/处理阶段调用文生图MCP Server如集成Stable Diffusion或Midjourney的API或用爬虫/素材库工具获取配图。发布执行阶段AI主控Claude调用我们的publish_xiaohongshu_note工具传入前两个阶段产出的内容完成发布。调度与监控你可以将整个流程写成一个脚本用CronLinux/macOS或任务计划程序Windows定时触发或者使用更专业的CI/CD工具如Jenkins、GitHub Actions来调度并集成通知如成功/失败发消息到钉钉、飞书。这个方案的魅力在于它的模块化和可进化性。如果明天小红书改版了你只需要更新Playwright MCP Server中那个高级工具的元素选择器逻辑。如果你想增加发布前的内容合规检查可以插入一个调用内容审核API的MCP Server。整个流程像搭积木一样灵活。最后必须再次强调合规与风险。本方案完全基于模拟真人前端操作不破解、不干扰任何平台协议是相对健康的自动化方式。但它依然可能违反小红书用户协议中关于“自动化操作”的条款。因此请务必控制发布频率和数量模拟真人行为节奏。确保发布内容的质量和原创性创造真实价值。将此技术用于学习和效率提升避免用于 spam 或恶意行为。技术是放大器它放大了效率也可能放大风险。用对地方才能让工具真正为我们服务。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价