当前位置: 首页 > news >正文

B端系统自动化MCP工具开发指南

由于 Trae 和 Cursor 主要作为辅助编码工具(类似智能 IDE 或 Code Copilot),而不是运行时框架,我们将把重点放在如何利用它们的开发效率来快速实现核心的 AI 驱动的自动化逻辑

在这个项目中,我们选择 Playwright 作为浏览器自动化技术,因为它比 Puppeteer 具有更好的跨浏览器支持和内置的 auto-wait 功能,这对于应对 B 端系统复杂的动态页面更加稳定。
在这里插入图片描述

B端系统自动化MCP工具技术架构图

架构分层说明

1. 用户交互层

  • 最终用户:通过自然语言与AI助手交互
  • MCP客户端:Claude Desktop等支持MCP的应用
  • 授权机制:关键的安全屏障,确保用户对每次操作有完全控制权

2. MCP服务器层 - 业务逻辑核心

src/server.js
├── 工具路由分发:根据请求类型路由到相应工具
├── 登录管理:系统认证状态维护
├── 数据操作:单条/批量新增的业务逻辑
├── 会话管理:浏览器会话生命周期管理
└── 错误处理:统一的异常处理机制

3. 浏览器自动化层 - 技术实现核心

浏览器引擎 (Playwright)
├── 浏览器控制器:Chromium实例管理
├── 智能表单填充器:字段识别与填充策略
│   ├── 选择器匹配:智能定位表单元素
│   ├── 类型识别:输入框/下拉框/单选框识别
│   └── 填充策略:基于字段类型的值填充逻辑
└── 认证管理器:登录流程自动化├── 凭证安全:敏感信息处理└── 状态验证:登录成功检测

4. 目标系统层 - 外部依赖

  • B端系统接口:标准的Web应用架构
  • 数据流:从表单提交到数据持久化的完整链路
  • 响应处理:成功/错误状态的标准处理

5. 配置与数据流层 - 可维护性保障

配置管理
├── 客户端配置:MCP服务器注册信息
├── 系统配置:浏览器参数、超时设置
├── 监控日志:操作审计和性能监控
└── 错误恢复:重试机制和故障转移

关键技术特性

🛡️ 安全设计

  • 用户操作前授权确认
  • 凭证信息的安全传递
  • 操作范围的最小权限原则

🔧 智能适配

  • 动态表单元素识别
  • 多选择器回退机制
  • 自适应等待策略

📊 可观测性

  • 完整的操作日志
  • 性能指标监控
  • 错误追踪和报告

🔄 容错处理

  • 网络异常重试
  • 元素查找超时处理
  • 操作失败回滚机制

这个架构确保了工具的可扩展性、安全性和稳定性,为B端系统自动化提供了坚实的技术基础。


🤖 从 0 到 1:基于 Node.js 和 Playwright 开发智能体 MCP 工具

🎯 项目目标与技术选型

业务背景: 自动化处理非结构化数据源(例如邮件、聊天记录、外部网站数据)到 B 端系统(如内部 CRM 或 ERP)的条目新增工作,模拟人工操作,解决系统无 API 接口的问题。

核心技术栈:

模块技术/工具目的
后端框架Node.js (Express)构建高性能的 API 入口和任务管理服务。
浏览器自动化Playwright模拟人工登录、导航、输入、点击等复杂 UI 交互。
AI 智能层OpenAI / Gemini API核心数据抽取,动态生成 Playwright 脚本
异步处理BullMQ (基于 Redis)隔离耗时的浏览器和 AI 任务,确保系统高并发和稳定性。
开发工具Trae / Cursor加速代码生成、调试和重构,特别是复杂的自动化脚本。

🏗️ 第一步:项目初始化与依赖安装

# 创建项目文件夹
mkdir ai-browser-mcp
cd ai-browser-mcp# 初始化 Node.js 项目
npm init -y# 安装核心依赖
npm install express dotenv @bull-mq ioredis playwright openai# 安装 Playwright 浏览器驱动
npx playwright install

🧠 第二步:构建 AI 驱动的“脚本生成器”

传统浏览器自动化的最大痛点是 Selector Fragility(选择器脆弱性)。页面 UI 一旦改变,脚本就会失效。我们的 AI-MCP 核心在于利用 LLM 的能力,根据任务需求和系统描述,动态生成或修复 Playwright 脚本片段

1. 定义 AI 任务和 Schema

我们将 AI 分为两层:数据抽取(结构化数据)和行为生成(生成可执行代码)。

// src/services/aiSchema.js
// B 端系统条目新增的通用数据结构
const LEAD_SCHEMA = {type: "object",properties: {// ... 结构与上一个回答类似,用于数据抽取}
};// Playwright 脚本生成指令的上下文
const SCRIPT_PROMPT = `你是一名高级 Playwright 自动化工程师。任务:将抽取到的 JSON 数据 {DATA} 输入到目标 B 端系统。目标页面URL: {URL}。B端系统操作步骤描述: {DESCRIPTION}请生成一个异步函数 'executeAutomation(page, data)' 的 JavaScript 代码片段,其中包含登录、导航、以及使用 'page.fill()' 和 'page.click()' 的全部步骤。不要包含 try/catch 或其他辅助函数,只返回核心的异步代码块。
`;

2. 实现 AI 脚本生成器

利用 LLM 的代码生成能力:

// src/services/aiScriptGenerator.js
const { OpenAI } = require('openai');
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });async function generatePlaywrightScript(data, url, description) {const prompt = SCRIPT_PROMPT.replace("{DATA}", JSON.stringify(data, null, 2)).replace("{URL}", url).replace("{DESCRIPTION}", description);try {const completion = await openai.chat.completions.create({model: "gpt-4-turbo-preview", // 选用代码生成能力强的模型messages: [{ role: "system", content: "你是一个 Playwright 脚本生成器,只输出 JavaScript 代码,无需解释。" },{ role: "user", content: prompt }],temperature: 0.1 // 降低随机性,提高稳定性});let script = completion.choices[0].message.content;// 清理代码块标记,只返回纯代码script = script.replace(/```javascript\n|```/g, '').trim();// 验证脚本是否包含预期的函数签名 (Trae/Cursor 可以帮助快速编写这个验证逻辑)if (!script.startsWith('async function executeAutomation(page, data)')) {throw new Error("AI 返回的脚本格式不正确。");}return script;} catch (error) {console.error("AI 脚本生成失败:", error);throw new Error("无法生成有效的自动化脚本。");}
}module.exports = { generatePlaywrightScript };

⏳ 第三步:异步任务与 Playwright 执行器

浏览器自动化是典型的 I/O 密集型且耗时的任务,必须使用异步队列和 Worker 来处理。

1. 队列设置 (Queue Setup)

设置 BullMQ 队列和 Redis 连接,与上一个回答类似。

2. Playwright Worker 执行器

这是 MCP 逻辑的核心:动态加载并执行 AI 生成的代码。

// src/workers/automationWorker.js
const { Worker, Job } = require('bullmq');
const { connection } = require('../queue/queueSetup');
const playwright = require('playwright');
const { generatePlaywrightScript } = require('../services/aiScriptGenerator');
const { extractLeadData } = require('../services/aiExtractor'); // 沿用上一回答的数据抽取服务const BIZ_SYSTEM_URL = 'http://b-system.example.com/login'; // 示例 B 端系统地址const worker = new Worker('automationQueue', async (job) => {const { rawInput, source, systemDescription } = job.data;// 1. AI 抽取数据const extractedData = await extractLeadData(rawInput, source);// 2. AI 生成 Playwright 脚本 (根据抽取到的数据和系统描述)const scriptCode = await generatePlaywrightScript(extractedData, BIZ_SYSTEM_URL, systemDescription);let browser;try {// 3. 启动 Playwright 浏览器browser = await playwright.chromium.launch({ headless: true, // 生产环境使用无头模式timeout: 60000 // 60秒启动超时});const page = await browser.newPage();// 4. 动态执行 AI 生成的脚本// 注意:eval/new Function 存在安全风险,生产环境需确保 AI 输出的隔离和验证const fn = new Function('page', 'data', scriptCode);// 调用执行函数,传入 page 实例和抽取到的数据await fn(page, extractedData); // 5. 可选:截图作为执行成功的凭证const screenshotPath = `screenshots/success_${job.id}.png`;await page.screenshot({ path: screenshotPath });console.log(`[Success] 任务 ${job.id} 成功完成,截图已保存。`);return { status: 'success', screenshot: screenshotPath, entryData: extractedData };} catch (error) {console.error(`[Failure] 任务 ${job.id} 自动化失败:`, error.message);// 失败时截图,帮助调试if (browser) {const errorScreenshotPath = `screenshots/fail_${job.id}.png`;const page = (await browser.pages())[0];await page.screenshot({ path: errorScreenshotPath });return { status: 'failed', error: error.message, screenshot: errorScreenshotPath };}throw error; // 重新抛出错误,让 BullMQ 记录失败} finally {if (browser) {await browser.close();}}
}, { connection });console.log("Playwright Automation Worker 已启动...");

🌐 第四步:Express API 入口

提供一个 API 接口用于接收任务。

// src/server.js (部分更新)
// ... (引入 express, dotenv, setupQueue)app.post('/api/submit-automation-task', async (req, res) => {const { raw_input,      // 原始输入 (如邮件正文)source,         // 来源 (如 'Customer Email')system_desc     // **关键:B端系统的操作描述**} = req.body;if (!raw_input || !system_desc) {return res.status(400).send({ message: "缺少输入或 B 端系统操作描述。" });}try {// 将任务推入 Playwright 自动化队列const job = await addAutomationTask({ rawInput: raw_input, source, systemDescription: system_desc });res.status(202).send({ message: 'AI 自动化任务已接收,正在后台执行。',jobId: job.id});} catch (error) {console.error('API 提交任务失败:', error);res.status(500).send({ message: '内部服务器错误,任务未能进入队列。' });}
});

🚀 第五步:使用 Trae/Cursor 加速开发与调试

1. 代码生成与优化 (Trae/Cursor)

当使用 Trae 或 Cursor 时,你可以利用其 AI 辅助能力来处理复杂的、重复性的代码:

  • BullMQ 模板: 快速生成 queueSetup.jsautomationWorker.js 的基本结构,包括 Redis 连接和 Worker 监听逻辑。
  • Playwright 脚本生成器的迭代: 当你发现 AI 生成的 Playwright 脚本不够稳定时,可以直接在 Cursor 中圈选 aiScriptGenerator.js 的代码,并要求 AI “根据 Playwright 的最佳实践,添加更健壮的选择器(如 page.getByLabelpage.getByRole)”

2. 调试与修复

浏览器自动化调试是耗时的工作。

  • 隔离模式: 在 Cursor 中,你可以快速将 automationWorker.js 中的逻辑抽取到一个独立的测试文件中,并运行 “非 Headless” 模式:

    // 在本地调试时将 headless 设为 false
    browser = await playwright.chromium.launch({ headless: false, slowMo: 50 // 慢动作有助于观察
    });
    
  • AI 辅助修复: 当脚本执行失败并返回截图时,你可以将错误截图和失败时的上下文日志粘贴给 Trae/Cursor,让它分析并修复 generatePlaywrightScript 中使用的基础选择器模板登录逻辑。例如:“我发现 B 端系统的登录按钮类名变了,请更新生成器中的登录步骤。”

总结与下一步

这个 AI-MCP 工具是一个强大的结合体:

  1. AI 驱动的数据抽取:将非结构化文本转化为规范 JSON。
  2. AI 驱动的行为生成:动态创建(或修复)浏览器自动化脚本。
  3. Playwright 的可靠执行:稳定模拟用户交互。
  4. 异步队列的高效性:隔离耗时操作,确保 MCP 接口高可用。

B端系统自动化MCP工具开发指南

项目概述

我们将开发一个基于浏览器自动化技术的MCP工具,用于自动在B端系统中新增数据条目。这个工具将模拟用户在浏览器中的操作,实现系统登录、导航到目标页面、填写表单和提交数据。

技术选型

  • MCP SDK: @modelcontextprotocol/sdk-node
  • 浏览器自动化: playwright (比Puppeteer更强大)
  • 运行环境: Node.js 18+

第一步:项目初始化

1.1 创建项目目录结构

mkdir b-system-automation-mcp
cd b-system-automation-mcp
npm init -y

1.2 安装依赖

# 安装MCP SDK
npm install @modelcontextprotocol/sdk-node# 安装浏览器自动化工具
npm install playwright# 安装类型定义(如果用TypeScript)
npm install --save-dev @types/node typescript

1.3 项目结构

b-system-automation-mcp/
├── src/
│   ├── server.js          # MCP服务器主文件
│   ├── browser/           # 浏览器自动化模块
│   │   ├── browser.js     # 浏览器控制
│   │   ├── auth.js        # 认证处理
│   │   └── form-filler.js # 表单填充
│   └── config/
│       └── default.json   # 配置文件
├── package.json
└── README.md

第二步:核心代码实现

2.1 MCP服务器主文件 (src/server.js)

const { Server } = require('@modelcontextprotocol/sdk-node/server.js');
const { StdioServerTransport } = require('@modelcontextprotocol/sdk-node/stdio.js');
const {CallToolRequestSchema,ToolSchema,ListToolsRequestSchema,
} = require('@modelcontextprotocol/sdk-node/types.js');// 导入浏览器自动化模块
const { BrowserAutomation } = require('./browser/browser.js');class BSystemAutomationServer {constructor() {this.server = new Server({name: 'b-system-automation',version: '1.0.0',},{capabilities: {tools: {},},});this.browser = new BrowserAutomation();this.isLoggedIn = false;this.setupToolHandlers();}setupToolHandlers() {// 提供工具列表this.server.setRequestHandler(ListToolsRequestSchema, async () => {return {tools: [{name: 'login_to_system',description: '登录到B端管理系统',inputSchema: {type: 'object',properties: {systemUrl: {type: 'string',description: '系统登录地址'},username: {type: 'string',description: '用户名'},password: {type: 'string',description: '密码'},usernameSelector: {type: 'string',description: '用户名输入框CSS选择器,默认: input[type="text"], input[name*="user"]',default: 'input[type="text"], input[name*="user"]'},passwordSelector: {type: 'string',description: '密码输入框CSS选择器,默认: input[type="password"]',default: 'input[type="password"]'},submitSelector: {type: 'string',description: '提交按钮CSS选择器,默认: button[type="submit"], input[type="submit"]',default: 'button[type="submit"], input[type="submit"]'}},required: ['systemUrl', 'username', 'password']}},{name: 'add_data_entry',description: '在B端系统中新增数据条目',inputSchema: {type: 'object',properties: {targetUrl: {type: 'string',description: '数据新增页面的URL'},formData: {type: 'object',description: '要填写的表单数据,键值对形式',additionalProperties: {type: 'string'}},formSelectors: {type: 'object',description: '表单元素选择器映射',properties: {submitButton: {type: 'string',description: '提交按钮选择器',default: 'button[type="submit"], input[type="submit"]'},successIndicator: {type: 'string',description: '成功提示元素选择器',default: '.success, .alert-success'}}}},required: ['targetUrl', 'formData']}},{name: 'batch_add_entries',description: '批量新增多个数据条目',inputSchema: {type: 'object',properties: {targetUrl: {type: 'string',description: '数据新增页面的URL'},entries: {type: 'array',description: '要新增的条目数组',items: {type: 'object',additionalProperties: {type: 'string'}}},delayBetweenEntries: {type: 'number',description: '条目之间的延迟(毫秒)',default: 1000}},required: ['targetUrl', 'entries']}},{name: 'close_browser',description: '关闭浏览器实例,释放资源',inputSchema: {type: 'object',properties: {}}}],};});// 处理工具调用this.server.setRequestHandler(CallToolRequestSchema, async (request) => {const { name, arguments: args } = request.params;try {switch (name) {case 'login_to_system':return await this.handleLogin(args);case 'add_data_entry':return await this.handleAddEntry(args);case 'batch_add_entries':return await this.handleBatchAdd(args);case 'close_browser':return await this.handleCloseBrowser();default:throw new Error(`未知的工具: ${name}`);}} catch (error) {return {content: [{type: 'text',text: `操作失败: ${error.message}`}],isError: true};}});}async handleLogin(args) {const {systemUrl,username,password,usernameSelector = 'input[type="text"], input[name*="user"]',passwordSelector = 'input[type="password"]',submitSelector = 'button[type="submit"], input[type="submit"]'} = args;await this.browser.launch();const result = await this.browser.login(systemUrl,username,password,{ usernameSelector, passwordSelector, submitSelector });this.isLoggedIn = result.success;return {content: [{type: 'text',text: result.success ? `登录成功: ${result.message}`: `登录失败: ${result.message}`}]};}async handleAddEntry(args) {if (!this.isLoggedIn) {throw new Error('请先登录系统');}const { targetUrl, formData, formSelectors = {} } = args;const result = await this.browser.fillForm(targetUrl, formData, formSelectors);return {content: [{type: 'text',text: result.success? `数据新增成功: ${result.message}`: `数据新增失败: ${result.message}`}]};}async handleBatchAdd(args) {if (!this.isLoggedIn) {throw new Error('请先登录系统');}const { targetUrl, entries, delayBetweenEntries = 1000 } = args;const results = [];for (let i = 0; i < entries.length; i++) {const entry = entries[i];const result = await this.browser.fillForm(targetUrl, entry);results.push({index: i + 1,success: result.success,message: result.message});// 添加延迟,避免操作过快被系统检测if (i < entries.length - 1) {await new Promise(resolve => setTimeout(resolve, delayBetweenEntries));}}const successCount = results.filter(r => r.success).length;return {content: [{type: 'text',text: `批量操作完成: 成功 ${successCount}/${entries.length}`},{type: 'text',text: `详细结果: ${JSON.stringify(results, null, 2)}`}]};}async handleCloseBrowser() {await this.browser.close();this.isLoggedIn = false;return {content: [{type: 'text',text: '浏览器已关闭,资源已释放'}]};}async connect() {const transport = new StdioServerTransport();await this.server.connect(transport);console.error('B端系统自动化MCP服务器已启动');}
}// 启动服务器
async function main() {const server = new BSystemAutomationServer();await server.connect();
}main().catch(console.error);

2.2 浏览器自动化模块 (src/browser/browser.js)

const { chromium } = require('playwright');class BrowserAutomation {constructor() {this.browser = null;this.context = null;this.page = null;this.isLaunched = false;}async launch() {if (this.isLaunched) {return;}this.browser = await chromium.launch({ headless: false, // 设置为true可无头运行slowMo: 100 // 操作延迟,便于观察});this.context = await this.browser.newContext({viewport: { width: 1280, height: 720 },userAgent: 'MCP-Bot/1.0'});this.page = await this.context.newPage();this.isLaunched = true;console.error('浏览器已启动');}async login(systemUrl, username, password, selectors = {}) {if (!this.isLaunched) {throw new Error('浏览器未启动');}try {await this.page.goto(systemUrl, { waitUntil: 'networkidle' });// 等待页面加载await this.page.waitForTimeout(2000);// 填写用户名await this.page.click(selectors.usernameSelector);await this.page.fill(selectors.usernameSelector, username);// 填写密码await this.page.click(selectors.passwordSelector);await this.page.fill(selectors.passwordSelector, password);// 点击登录await this.page.click(selectors.submitSelector);// 等待登录完成await this.page.waitForTimeout(3000);// 检查是否登录成功(根据URL变化或页面元素)const currentUrl = this.page.url();if (currentUrl.includes('dashboard') || currentUrl !== systemUrl) {return { success: true, message: '登录成功' };} else {// 检查是否有错误提示const errorElement = await this.page.$('.error, .alert-danger');if (errorElement) {const errorText = await errorElement.textContent();return { success: false, message: errorText };}return { success: false, message: '登录状态未知' };}} catch (error) {return { success: false, message: error.message };}}async fillForm(targetUrl, formData, selectors = {}) {if (!this.isLaunched) {throw new Error('浏览器未启动');}try {await this.page.goto(targetUrl, { waitUntil: 'networkidle' });await this.page.waitForTimeout(2000);// 智能填写表单for (const [fieldName, fieldValue] of Object.entries(formData)) {await this.intelligentFormFill(fieldName, fieldValue);}// 提交表单const submitSelector = selectors.submitButton || 'button[type="submit"], input[type="submit"]';await this.page.click(submitSelector);await this.page.waitForTimeout(3000);// 检查提交结果const successSelector = selectors.successIndicator || '.success, .alert-success';const successElement = await this.page.$(successSelector);if (successElement) {return { success: true, message: '数据新增成功' };} else {// 检查错误const errorElement = await this.page.$('.error, .alert-danger');if (errorElement) {const errorText = await errorElement.textContent();return { success: false, message: errorText };}return { success: true, message: '操作完成(成功状态未检测)' };}} catch (error) {return { success: false, message: error.message };}}async intelligentFormFill(fieldName, value) {// 智能匹配表单字段const possibleSelectors = [`input[name*="${fieldName.toLowerCase()}"]`,`textarea[name*="${fieldName.toLowerCase()}"]`,`[placeholder*="${fieldName}"]`,`input[type="text"]:nth-of-type(${this.getFieldIndex(fieldName)})`];for (const selector of possibleSelectors) {try {const element = await this.page.$(selector);if (element) {await element.click();await element.fill(value);return true;}} catch (error) {// 继续尝试下一个选择器continue;}}// 如果智能匹配失败,尝试手动输入到焦点元素await this.page.keyboard.type(value);return false;}getFieldIndex(fieldName) {// 根据常见字段名返回可能的索引const commonFields = {'name': 1, 'title': 1, 'username': 1,'description': 2, 'content': 2,'email': 3, 'phone': 4, 'address': 5};return commonFields[fieldName.toLowerCase()] || 1;}async close() {if (this.browser) {await this.browser.close();this.isLaunched = false;console.error('浏览器已关闭');}}
}module.exports = { BrowserAutomation };

第三步:配置MCP客户端

3.1 创建Claude Desktop配置文件

在Claude配置目录创建 claude_desktop_config.json

{"mcpServers": {"b-system-automation": {"command": "node","args": ["/绝对路径/到/你的/项目/src/server.js"]}}
}

3.2 配置说明

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

第四步:使用示例

4.1 基本使用流程

// 通过MCP客户端调用工具的示例参数
const loginParams = {systemUrl: "https://your-b-system.com/login",username: "your_username",password: "your_password",usernameSelector: "input[name='username']",passwordSelector: "input[name='password']",submitSelector: "button.login-btn"
};const addEntryParams = {targetUrl: "https://your-b-system.com/data/add",formData: {name: "测试产品",description: "这是一个测试产品描述",category: "电子产品",price: "299.99",stock: "100"},formSelectors: {submitButton: "button[type='submit']",successIndicator: ".alert-success"}
};

4.2 在Cursor/Trae中的使用

在支持MCP的AI客户端中,你可以这样使用:

"请调用b-system-automation工具,帮我登录到管理系统并新增一个产品条目。"

系统会自动调用相应的工具并请求你的授权。


第五步:高级功能与优化

5.1 错误处理与重试机制

// 在browser.js中添加重试逻辑
async function withRetry(operation, maxRetries = 3) {for (let attempt = 1; attempt <= maxRetries; attempt++) {try {return await operation();} catch (error) {if (attempt === maxRetries) throw error;await this.page.waitForTimeout(1000 * attempt);}}
}

5.2 配置文件支持

创建 src/config/default.json

{"browser": {"headless": false,"slowMo": 100,"timeout": 30000},"selectors": {"username": "input[type='text']","password": "input[type='password']","submit": "button[type='submit']"}
}

5.3 安全考虑

  1. 敏感信息处理: 不要硬编码密码,使用环境变量
  2. 会话管理: 实现自动重新登录机制
  3. 速率限制: 避免操作过快触发反爬虫机制

第六步:测试与调试

6.1 本地测试脚本

创建 test.js 进行本地测试:

const { BrowserAutomation } = require('./src/browser/browser.js');async function test() {const browser = new BrowserAutomation();try {await browser.launch();// 测试登录const loginResult = await browser.login('https://example.com/login','testuser','testpass');console.log('Login result:', loginResult);} finally {await browser.close();}
}test();

6.2 调试技巧

  1. 设置 headless: false 观察浏览器操作
  2. 使用 slowMo 参数减慢操作速度
  3. 添加详细的日志记录

项目总结

这个MCP工具提供了完整的B端系统自动化能力,主要特点:

  1. 模块化设计: 浏览器自动化与MCP协议分离
  2. 智能表单填充: 自动匹配表单字段
  3. 批量操作支持: 支持大量数据自动录入
  4. 错误处理: 完善的异常处理和重试机制
  5. 可扩展性: 易于添加新的系统适配

通过这个工具,你可以实现各种B端系统的自动化数据管理,大大提升工作效率。记得根据实际目标系统调整选择器和操作逻辑。

http://www.dtcms.com/a/601267.html

相关文章:

  • 外贸整合营销网站如何快速开发手机app
  • 谢赛宁×李飞飞×LeCun联手重磅|Cambrian-S:「视频空间超感知」新范式,实现真正持续视频感知
  • 在服务器网站上做跳转网站运营推广方式
  • Ansible 安装与入门
  • VMMap 学习笔记(8.7):命令行模式与自动抓取——无界面采集内存证据的正确姿势
  • 大型网站服务器得多少钱app大全免费软件排行榜
  • AXI-5.3.2~5.3.5
  • Anaconda安装与配置:构建人工智能开发环境
  • 从入门到精通:周志华《机器学习》第一、二章深度解析
  • 网站建设品牌策划装修设计软件排名
  • 社区投稿 | Oinone应用于整车制造供应链决策
  • 加强网站建设的制度网站网址怎么找
  • 【Git】Git04:分支管理
  • R语言用什么编译器 | 如何选择最适合你的R语言开发环境
  • cuda12 cudnn9 tensorflow 显卡加速
  • 网站建设目标的文字嘉兴企业网站排名
  • 手机网站开发语言选择怎么能在网上卖货
  • 编程算法学习
  • 在Beego框架中创建Services层
  • PPT插入的音乐怎么让它播放到某一页就停?
  • 打包,压缩解压,上传下载
  • 【动态规划】专题完结,题单汇总
  • 微信公众好第三方网站怎么做wordpress订阅关闭
  • 网站logo上传建筑室内设计软件
  • Linux环境下Nginx核心总结与密码验证实践
  • python实战:装饰模式详解
  • 一个基于TCP/IP接收数据并通过API推送数据的小工具
  • 经典网站建设案例wordpress讨论区插件
  • 具身记忆大展拳脚
  • Java语言编译器 | 深入了解Java编译过程与优化技巧