书接上文,百度盘搞定了,同样的思路闲鱼上试试。

痛点:Agent 戳不中闲鱼的按钮

让 AI Agent 替我在闲鱼填个草稿,听起来像是几行代码的事。实际跑起来发现三条路都不通:

  1. Hermes 自带的 browser 工具 — 闲鱼页面有完整的风控脚本(AWSC、Baxia),直接用框架级 browser 进场,经常被拦在验证码前面
  2. Computer Use / 截图点坐标 — 点过一次能成功,但换个屏、换个分辨率、甚至页面多加载了个模块就点偏了
  3. 直接调闲鱼接口 — 闲鱼用的是阿里 mtop 协议,请求带签名跟风控,光是抓包复现签名的成本就够受的

所以思路要变:不是让 Agent 假装成一个新浏览器去操作闲鱼,是让 Agent 用你已经登录过的 Chrome 去工作。

选型:bb-browser + 真实 Chrome 登录态

bb-browser 是一个 CLI 工具,核心能力:

  • 探测你电脑上已打开的 Chrome(或 Edge),找到 CDP 调试端口
  • 在浏览器页面上下文里执行 JS、操作 DOM、发 fetch
  • 支持 site adapter 机制——为每个站点写一个 JS adapter,封装成 bb-browser site <name>/<command> 这样的 CLI 命令
  • 支持 network requests 抓取当前 tab 的 XHR/Fetch 日志

跟 Computer Use 比,它的优势不是"更智能",而是"更底层":

  • 它在页面的 JS 上下文里执行,不是在外面临摹键盘鼠标
  • 它操作的是真实的 DOM 事件,不是图片坐标
  • 它复用的是你 Chrome 的真实登录态(cookie、token、风控指纹),不需要额外处理验证码

路线:先侦察,再填表单,再攻图片上传

闲鱼发布页的自动化不是一锤子买卖。我拆成了三个阶段:

P0:侦察 —— 弄明白发布页长什么样

先不写任何代码,只用 bb-browser 读页面状态。

确认已登录:

bb-browser tab list --json

输出:

{
  "tabs": [
    {"tab": "b85d", "url": "https://www.goofish.com/publish", "title": "发闲置_闲鱼"},
    {"tab": "3eca", "url": "https://www.goofish.com", "title": "闲鱼 - 闲不住?上闲鱼!"}
  ]
}

然后调 network 看发布页的接口结构:

bb-browser network requests goofish.com --json

发现了一串阿里 mtop 接口:

mtop.idle.pc.idleitem.preget
mtop.idle.web.user.page.nav
mtop.taobao.idlemessage.pc.loginuser.get
mtop.taobao.idle.local.poi.get
mtop.taobao.idlemessage.pc.redpoint.query
mtop.gaia.nodejs.gaia.idle.data.gw.v2.index.get

然后用 DOM 查询定位可交互字段:

字段 DOM 特征
宝贝描述 [contenteditable=true] + .editor--MtHPS94K
价格 input.ant-input 第一个
原价 input.ant-input 第二个
发货方式 input[type=radio] 第4个 = 无需邮寄
图片上传 input[type=file] display=none
发布按钮 button 文本包含"发布"

这一步的结论:

  • 发布页是 React + Ant Design,字段可以直接操作
  • 纯 DOM 操作可以填文本类和选择类字段
  • 图片上传是隐藏的 file input,纯 JS 操作不了
  • 直接调 API 发布不适合做 MVP,成本和风险都高

P0 花了大约 15 分钟。

P1a:填草稿 —— 文字、价格、发货方式

这一步就是写一个在页面上下文执行的 JS 函数,填描述、定价、选发货方式。

实现的核心代码,简化后长这样:

// 填描述
const editor = document.querySelector('[contenteditable=true]');
editor.focus();
editor.innerText = descriptionText;
editor.dispatchEvent(new InputEvent('input', {bubbles: true}));

// 填价格(React 管理的 input 需要走 native setter)
function setNativeValue(el, val) {
  const proto = Object.getPrototypeOf(el);
  const desc = Object.getOwnPropertyDescriptor(proto, 'value')
    || Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
  desc.set.call(el, String(val));
  el.dispatchEvent(new Event('input', {bubbles: true}));
  el.dispatchEvent(new Event('change', {bubbles: true}));
}
setNativeValue(priceInput, '50');

// 选发货方式(无需邮寄)
radios[3].click();

然后用 bb-browser 的 eval 把这个 JS 注入到发布页执行:

bb-browser eval "上述JS代码" --tab b85d --json

验证结果:

字段 结果
宝贝描述 已写入
价格 50
原价 99
发货方式 无需邮寄
发布按钮 仍为 disabled

发布按钮还是 disabled 状态,说明缺必填项——缺图片。

P1a 花费时间:约 10 分钟。

P1b:图片上传 —— 绕过浏览器安全限制

为什么自己写不了图片上传?因为浏览器安全策略:页面里的 JS 不能直接给隐藏的 file input 赋值本地路径。

但是 Chrome DevTools Protocol 有一个接口:

DOM.setFileInputFiles

它可以在浏览器开发者工具层面,直接把本地文件路径塞给任何一个 <input type=file>,不需要用户手动点文件选择器。

实现方式:写一个 Node 脚本,通过 WebSocket 连接 Chrome 的 CDP 端口,把图片注入到闲鱼发布页的 file input。

核心代码:

// 1. 连接 Chrome CDP WebSocket
const WebSocket = global.WebSocket;  // Node 18+
const ws = new WebSocket(target.webSocketDebuggerUrl);

// 2. 找到 file input
await cdpCall(ws, id++, 'DOM.querySelector', {
  nodeId: doc.root.nodeId,
  selector: 'input[type=file]'
});

// 3. 注入本地文件路径(关键一步)
await cdpCall(ws, id++, 'DOM.setFileInputFiles', {
  nodeId: q.nodeId,
  files: ['C:/path/to/cover.png']
});

执行命令:

node xianyu-set-file-input.js --tab b85d --file cover.png

注入后等待几秒再检查页面状态:

  • 原始文本 “添加首图” 变成了 “添加细节图”
  • 页面中出现了 mtopupload 的资源 URL

这说明图片已经进入闲鱼的上传链路,不只是本地选中了文件。

这一步有一个关键发现:bb-browser 的 adapter 机制不适合做图片上传,因为 adapter 运行在浏览器页面上下文,拿不到本地文件系统。所以图片上传需要单独写一个本地 CDP 脚本,保持它独立于 adapter。

P1b 花费时间:约 20 分钟。

沉淀为 adapter

闲鱼发布页的操作不是一次性实验,所以把它沉淀成 bb-browser 的 site adapter:

~/.bb-browser/bb-sites/xianyu/
├── inspect.js         # 检查发布页状态
├── fill-draft.js      # 填写草稿(不含图片)
└── README.md

使用方式:

# 检查发布页状态
bb-browser site xianyu/inspect --tab b85d --json

# 填写草稿
bb-browser site xianyu/fill-draft \
  --tab b85d \
  --description "商品描述..." \
  --price 50 \
  --originalPrice 99 \
  --shipping no-post \
  --json

# 图片上传(本地 CDP 脚本)
node scripts/xianyu-set-file-input.js \
  --tab b85d \
  --file cover.png

adapter 输出的 JSON 可以直接被后续工作流消费,不需要人类对着屏幕读状态。

效果对比

方式 稳定性 可控性 是否需要人工干预
Computer Use 截图点坐标 低(分辨率/页面变化就偏) 低(不知道点了什么) 每次调
bb-browser + adapter 高(DOM 操作不依赖布局) 高(返回值都是结构化 JSON) 只在最终发布前确认

当前边界

这套 MVP 明确了哪些该做、哪些不该做:

  • 填描述、定价、选发货:adapter 自动化
  • 图片上传:本地 CDP 脚本自动化
  • 最终点发布按钮:人工确认,不做自动化
  • 私信回复买家:人工处理,不做自动化
  • 确认收款:人工处理,不做自动化

这个分工的意义是:Agent 负责所有重复、可逆、出错了不怕的事情;人负责最后那一下有后果的确定。

总结

闲鱼发布草稿的自动化是一条典型的三层路径:

  1. 先用侦察手段搞清楚页面长什么样,而不是直接写代码
  2. 能用 DOM 操作的字段先做,图片上传这种绕不过去的再单独处理
  3. 把稳定动作抽象成 adapter,把底层的 DOM 操作封装成 JSON 输出

图片上传之所以值得单独说,是因为它展示了浏览器自动化的一个分水岭:纯页面 JS 能做的事和需要 CDP 才能做的事。分清楚这个边界,就能判断一个站点操作是 adapter 级别的,还是需要升级到本地脚本。

下一步就该全自动发布+自动化客服,明天接着折腾。


如果觉得这个方案对你有帮助,欢迎交流。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐