首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >#WorkBuddy# 那些没有 API 的老平台后台,我用「看得见」的 GUI 自动化交给了 WorkBuddy

#WorkBuddy# 那些没有 API 的老平台后台,我用「看得见」的 GUI 自动化交给了 WorkBuddy

原创
作者头像
用户12829777
发布于 2026-10-19 23:29:25
发布于 2026-10-19 23:29:25
160
举报

一、先说清楚我要解决什么(输入材料)

我在运营一个今日头条的情感类账号。这个活儿的特点是:操作极简单,但频次高、必须每天做,而且全在网页后台点鼠标。

每天要做的事大概这几件:

  1. 发一篇微头条或图文(编辑器里填标题、正文、封面、勾「引用AI」声明)
  2. 第二天去列表页读每条作品的「展现 / 阅读 / 点赞 / 评论」四个数字,记进复盘表
  3. 数据不对劲时,删掉某条作品
  4. 新号期还要反复确认「创作权益」有没有解锁

这些动作加起来一天二十分钟,但有个致命问题:它必须人肉盯着。 头条号后台没有给普通创作者的开放接口,抓包拿到的接口要带一堆动态签名和 cookie,为这点事去逆向不值得。

我最初的想法是让 WorkBuddy 直接帮我操作浏览器。试了之后发现两条路都不通:

  • 另开一个无痕浏览器:后台要登录,无痕里是空账号,扫码登录一次就得重来,而且平台风控对「新环境登录」很敏感。
  • 纯靠 API 调:签名会过期,维护成本比我手动点还高。

最后走通的方案是第三条:在已经登录好的真实 Chrome 里,用物理鼠标键盘去点。 由 WorkBuddy 负责「看」和「判断」,一段几十行的 Python 负责「动手」。

这样做的最大好处是:在平台眼里,这就是我本人在操作。 没有新设备、没有新环境、没有异常请求特征。


二、为什么这条路值得走(对比)

方案

登录态

风控风险

维护成本

适合场景

官方 API / 开放平台

需申请

无

低

平台开放了接口

requests 逆向接口

需自己维护

中高

高

接口简单稳定

Playwright / Selenium

需自己存 cookie

中(自动化指纹明显)

中

批量、可无人值守

GUI 自动化(本方案)

复用本人已登录窗口

低

低

低频、高价值、老后台

关键判断标准只有一条:这件事一天只做几次,但必须做对。 一天几十次的任务,GUI 自动化的速度是瓶颈;一天三五次的任务,它几乎是最优解。


三、环境与 WorkBuddy 配置

3.1 需要的环境

  • Windows(本文用 Windows 11 + 4K 屏,3440×1440,这个分辨率后面会变成一个坑)
  • Python 3.13
  • 三个包:pyautogui、pyperclip、pillow
代码语言:bash
复制
pip install pyautogui pyperclip pillow

3.2 目录约定(很重要)

我在项目根目录定死了一套约定,并且写进了 WorkBuddy 的工作区记忆里,让它每次执行都遵守:

代码语言:bash
复制
D:\头条号运营\
├── 01_头像素材\      # 图片素材
├── 02_内容文案\      # 选题、成稿
├── 03_数据复盘\      # 数据导出、复盘表
├── 04_投稿\          # 对外发布的稿子
├── _tools\           # 自动化脚本(wb.py 等)
└── _cache\           # 截图临时目录,用完即删

这一步看着无关紧要,其实是整套东西能稳定跑下去的基础:脚本产出的截图、日志、记录,必须落在固定位置,否则 WorkBuddy 下次执行时找不到上下文,就会自己乱猜。

3.3 让 WorkBuddy 记住「怎么操作」

我没有每次都把操作步骤重新讲一遍,而是在工作区记忆里写成了硬规则,例如:

AI 声明不在「预览并发布」弹窗里,而在编辑器底部「共 N 字 | 发文设置 ⌄」→ 展开面板最下方「作品声明」→ 勾选「引用AI」 浏览器窗口不够宽时,底栏左侧控件会被右侧面板挤到窗外,按 F11 全屏 不要用缩放后的截图目测坐标,改用像素颜色扫描算中心点 发布后列表不刷新,必须重新导航到列表页才会出现新条目

这几条规则的价值,比代码本身大。 因为它们是我踩坑踩出来的,写进去之后,WorkBuddy 下一次执行就不会在同一个地方再摔一次。


四、工具层:70 行的 wb.py

整件事的核心不是「让 AI 会点鼠标」,而是给 AI 一套稳定的感官和手。这套东西我抽成了 wb.py:

代码语言:python
复制
"""GUI helpers: 截图 + 鼠标键盘控制。所有截图落在 _cache/"""
import ctypes
import os
import time

# 关键:声明 DPI 感知,否则 Windows 会做缩放,坐标全错
try:
    ctypes.windll.shcore.SetProcessDpiAwareness(2)
except Exception:
    pass

import pyautogui
import pyperclip
from PIL import ImageGrab

pyautogui.FAILSAFE = True   # 鼠标甩到左上角可紧急中止
pyautogui.PAUSE = 0.35

CACHE = r"D:\头条号运营\_cache"
os.makedirs(CACHE, exist_ok=True)


def shot(name="s.png", box=None, scale=1.0):
    """截全屏;box 传裁剪区域;scale 放大便于读数"""
    img = ImageGrab.grab(all_screens=True)
    if box:
        img = img.crop(box)
    if scale != 1.0:
        w, h = img.size
        img = img.resize((int(w * scale), int(h * scale)))
    path = os.path.join(CACHE, name)
    img.save(path)
    print(path, img.size)
    return path


def click(x, y, pause=0.6):
    pyautogui.click(int(x), int(y))
    time.sleep(pause)


def paste(text):
    """中文必须走剪贴板,pyautogui.typewrite 输不了中文"""
    pyperclip.copy(text)
    time.sleep(0.2)
    pyautogui.hotkey("ctrl", "v")
    time.sleep(0.5)


def hot(key):
    pyautogui.hotkey(*key.split("+"))
    time.sleep(0.5)


def focus(title_kw="Chrome"):
    """按标题关键字把窗口提到前台(比 pyautogui 的窗口操作可靠)"""
    u = ctypes.windll.user32
    found = []

    def cb(h, l):
        if u.IsWindowVisible(h):
            n = u.GetWindowTextLengthW(h)
            if n:
                b = ctypes.create_unicode_buffer(n + 1)
                u.GetWindowTextW(h, b, n + 1)
                if title_kw in b.value:
                    found.append(h)
        return True

    cb_t = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_void_p)
    u.EnumWindows(cb_t(cb), 0)
    if not found:
        print("not found")
        return False
    h = found[0]
    u.ShowWindow(h, 3)          # 3 = 最大化
    try:
        u.SetForegroundWindow(h)
    except Exception:
        pass
    time.sleep(1.2)
    return True

三个要点:

  1. SetProcessDpiAwareness(2) 必须写在 pyautogui 导入之前。不写,Windows 会把坐标按 150% 缩放再交给你,你会发现自己点的位置永远比看到的位置偏一点。
  2. 中文一律走剪贴板。pyautogui.typewrite() 只认 ASCII,直接输入中文会丢字或者乱码。
  3. focus() 用 win32 的 EnumWindows 按窗口标题找,比 pygetwindow 稳,因为它能处理标题被截断、窗口被最小化的情况。

五、三个真实任务

任务一:发文并勾上「引用AI」

这是最容易出错的一件事,因为AI 声明不在你以为的地方。

第一版我让 WorkBuddy 去「预览并发布」弹窗里找,它找了半天没找到,最后在编辑器底部发现入口:

编辑器底部 →「共 1234 字 | 发文设置 ⌄」→ 展开 → 最下方「作品声明」→ 勾选「引用AI」

定位这个控件的代码:

代码语言:python
复制
import wb

wb.focus("Chrome")
wb.hot("f11")                      # 全屏,否则底栏被右侧面板挤到窗外
wb.hot("ctrl+l")
wb.paste("https://mp.toutiao.com/profile_v4/graphic/publish")
wb.hot("Return")

注意右侧那个 头条创作助手 面板——它是隐形杀手。窗口不够宽时,它会把编辑器的底栏往左挤,导致「发文设置」「封面」「作品声明」全都跑到可视区外面。你在截图里看不到它们,会以为页面没有这些功能。按 F11 全屏之后它们才会出现。

任务二:每天读四个数字

微头条列表里,每条作品下面有一行字:

展现 4 · 阅读 0 · 点赞 0 · 评论 0

这行字在 4K 屏上只有 30 像素高,字宽不到 10 像素。直接截图给模型看,它会认错。正确做法是裁剪 + 放大:

代码语言:python
复制
import wb

wb.focus("Chrome")
wb.hot("ctrl+l")
wb.paste("https://mp.toutiao.com/profile_v4/weitoutiao")
wb.hot("Return")
import time; time.sleep(6)
wb.hot("f5")            # 刷新一次,避免读到缓存
time.sleep(5)

# 只裁那一行,放大 6 倍
wb.shot("read.png", box=(1010, 468, 1420, 498), scale=6.0)

放大 6 倍之后,4 / 0 / 0 / 0 就清清楚楚了。这一步的小技巧是:先截全屏,看整体布局;再按物理像素坐标裁局部;最后按需放大读数。 三张图,成本最低。

任务三:删掉一条作品

删除路径是「更多」→「删除作品」→ 弹窗确认。这里有个必须做对的判断:

代码语言:python
复制
from PIL import ImageGrab

im = ImageGrab.grab(all_screens=True).convert("RGB")
px = im.load()
xs, ys = [], []
for y in range(520, 730):
    for x in range(1450, 2010):
        r, g, b = px[x, y]
        if r > 200 and g < 110 and b < 110:   # 玫瑰红 #F04141
            xs.append(x); ys.append(y)
if xs:
    cx, cy = (min(xs) + max(xs)) // 2, (min(ys) + max(ys)) // 2
    wb.click(cx, cy)

不要用「截图里目测的位置乘以缩放比」去算坐标。 我在这上面浪费了大概两个小时。原因见下一节第一个坑。


六、七个真实的坑

坑 1:截图是物理像素,你看到的预览是缩放的

这是最坑的一个,没有之一。

我的屏幕是 3440×1440。shot() 截出来的 PNG 就是 3440×1440 物理像素。但你在对话里预览这张图时,它被渲染成 1080 宽——缩放比 3.16 倍。

于是:你在预览里看到一个按钮在 (400, 200),你以为真实坐标是 (400, 200),实际是 (1266, 633)。点下去,什么都没发生,然后你开始怀疑人生。

正确做法只有两个:

  • 按物理像素给裁剪框:wb.shot(box=(1010, 468, 1420, 498), scale=6.0),然后只看裁剪结果,不再换算。
  • 或者用颜色扫描直接算中心点,完全不依赖肉眼。

坑 2:按钮要按颜色找,不要按位置找

同一个按钮,在不同状态下位置会变(列表少一条,下面所有按钮上移 116 像素)。

稳定的定位方式是扫像素:

代码语言:python
复制
def find_rect(box, pred):
    """在 box 里找出满足 pred 的像素包围盒,返回中心点"""
    from PIL import ImageGrab
    im = ImageGrab.grab(all_screens=True).convert("RGB")
    px = im.load()
    xs, ys = [], []
    for y in range(box[1], box[3]):
        for x in range(box[0], box[2]):
            if pred(*px[x, y]):
                xs.append(x); ys.append(y)
    if not xs:
        return None
    return ((min(xs) + max(xs)) // 2, (min(ys) + max(ys)) // 2)


# 玫瑰红的「确定」按钮
pos = find_rect((1450, 520, 2010, 730), lambda r, g, b: r > 200 and g < 110 and b < 110)

但要注意:蓝色链接文字也会被「蓝色按钮」的判定误伤。我判过一次「打开 WorkBuddy」的按钮,结果定位到了旁边那句蓝色 URL 上,白点了一下。所以谓词要收紧,或者先聚簇再取最大连通块。

坑 3:外部协议弹窗会吃掉所有点击

在网页上点「去完成」,如果它要唤起本地客户端,Chrome 会弹一个拦截条:

要打开 WorkBuddy 吗? https://www.workbuddy.cn 想打开此应用

这个弹窗是模态的,会挡掉整页的点击。 后续所有 click() 都失效,而且你不会收到任何报错——脚本照跑,只是什么都没发生。

解决:勾上「始终允许」,然后点「打开」。一次搞定,以后不再弹。

坑 4:发布成功后列表不刷新

点完「确认发布」,页面回到列表,列表还是旧的——看不到新文章,草稿箱计数已经清零了。

我第一次以为发布失败,又发了一遍,然后账号里出现了两篇一样的稿子。

正确判断是:重新导航到列表页 URL,新条目才会出现。所以判定发布成功的条件是:

草稿消失 + 刷新后列表出现新条目 = 发布成功

坑 5:F11 全屏之后再 F11 退出

全屏是为了让底栏控件进可视区。但脚本跑完要退出全屏,否则用户回来发现浏览器莫名全屏了,会以为出故障。养成配对习惯。

坑 6:中文输入必须走剪贴板

pyautogui.typewrite("测试") 会静默失败。pyperclip.copy() + ctrl+v 才是正解。另外粘贴前要先 click() 把焦点落进输入框,并且截图确认光标真的在里面——我遇到过一次点击落空,粘贴的内容进了页面搜索框。

坑 7:不要写脚本文件跑中文路径(Windows)

在 Windows 上写 .ps1/.bat 带中文路径,编码会把文件名搞乱。要么全部用 execute_command 直接调用,要么路径里避不开中文时用 Python 处理(Python 3 默认 UTF-8,稳)。


七、产出物与效果

跑通之后,这套东西的实际收益是:

事项

之前

现在

发一篇图文(含勾 AI 声明)

8—10 分钟

2 分钟,我只需要审稿

每日读 4 个数据指标

3 分钟

40 秒,且自动写进复盘表

删一条作品

1 分钟

30 秒

判断「账号有没有被限流」

靠猜

有数据,有记录,能对比

更有价值的是它变成了一个可复用的判据。举个真实例子:

新号发了三篇图文,全部「展现 0 · 阅读 0」。我第一反应是内容有问题,准备改稿。但让脚本去读一条几十字的微头条探针,40 分钟后拿到 展现 4 / 阅读 0——说明推荐通道是通的,问题在图文本身,不在账号。如果没有这套自动读数,我会误判方向,然后在错误的地方改一整周。


八、边界:什么时候别用这条路

说说我踩过的反例,免得你走弯路:

  1. 需要一天跑几十次的,别用。 每次都截全屏 + 图像识别,一次要 2—5 秒。批量任务用 Playwright。
  2. 涉及不可逆操作的,必须加二次确认。 我删作品那次,是先扫描出红色「确定」按钮的像素中心,再单独执行点击,中间隔了一次人工确认。自动点「确定删除」这种事,出事没有后悔药。
  3. 涉及额度的,先问再做。 改名、实名认证这类有次数限制的操作,绝不能让脚本自己决定。
  4. 别用它去对抗平台。 这套东西的定位是「替我做我本来就要做的重复劳动」,不是「帮我批量生产内容刷量」。我给自己定的规则是:一天最多发一篇,题材必须换。这不只是策略,也是账号能活下来的前提。

九、写在最后

这套东西能跑起来,靠的不是「AI 会点鼠标」这件事本身——那部分代码只有 70 行,谁都能写。

真正难的是:AI 不知道「引用AI」的勾选框藏在编辑器底部,不知道发布后列表不会刷新,不知道 4K 屏上截图会大 3.16 倍。 这些全都要靠人踩一遍,然后写进它的记忆里。

所以我现在的用法是:我负责踩坑和定规则,它负责每天重复执行。 踩坑只能踩一次,后面的活儿都交给它。

如果你也有一个「天天要点、但没有 API」的老后台,这套思路可以直接搬。真正要改的只有两处:wb.py 不用动,动的是你踩出来的那几条规则。


本文所有代码均来自实际运行中的脚本,非示意代码。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、先说清楚我要解决什么(输入材料)
  • 二、为什么这条路值得走(对比)
  • 三、环境与 WorkBuddy 配置
    • 3.1 需要的环境
    • 3.2 目录约定(很重要)
    • 3.3 让 WorkBuddy 记住「怎么操作」
  • 四、工具层:70 行的 wb.py
  • 五、三个真实任务
    • 任务一:发文并勾上「引用AI」
    • 任务二:每天读四个数字
    • 任务三:删掉一条作品
  • 六、七个真实的坑
    • 坑 1:截图是物理像素,你看到的预览是缩放的
    • 坑 2:按钮要按颜色找,不要按位置找
    • 坑 3:外部协议弹窗会吃掉所有点击
    • 坑 4:发布成功后列表不刷新
    • 坑 5:F11 全屏之后再 F11 退出
    • 坑 6:中文输入必须走剪贴板
    • 坑 7:不要写脚本文件跑中文路径(Windows)
  • 七、产出物与效果
  • 八、边界:什么时候别用这条路
  • 九、写在最后
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档