最近在折腾一件事:让 AI 助手直接登录博客后台,帮我整理并发布文章。想法很朴素——内容采集、改写、排版都交给它,我只负责最后确认。
真动手才发现,这条路比想象中绕。前后踩了三个坑,记录一下,省得你重走。
第一个坑:curl 登录,看起来成功其实是假的
最直觉的做法是命令行模拟表单提交。Typecho 的登录表单长这样:
<form action="https://example.com/index.php/action/login?_=xxxx" method="post">
<input type="text" name="name" />
<input type="password" name="password" />
<input type="hidden" name="referer" value="https://example.com/admin/" />
</form>于是:
curl -c cookie.txt -L "https://example.com/admin/" -o login.html
TOKEN=$(grep -oE 'login\?_=[a-zA-Z0-9]+' login.html | head -1 | cut -d= -f2)
curl -b cookie.txt -c cookie.txt -L \
--data-urlencode "name=你的账号" \
--data-urlencode "password=你的密码" \
--data-urlencode "referer=https://example.com/admin/" \
"https://example.com/index.php/action/login?_=$TOKEN"诡异的是,返回 302 且 Location 指向后台——看着像成功了。但一访问后台,还是登录页。
关键判断点在这里:
curl ... -D - -o /dev/null "..." | grep -i "set-cookie"如果响应头里没有 Set-Cookie,那就是没登录成功,不管 Location 跳去哪。Typecho 登录成功一定下发 __typecho_authCode。
为什么失败?看完整响应头就能发现线索:
EO-Cache-Status: MISS
EO-LOG-UUID: 17057971157060924413站点在腾讯云 EdgeOne 这类 CDN 后面,非浏览器请求在会话层就被处理掉了,压根到不了 PHP。结论:curl 这条路对这类站点不通,别浪费时间调参数。
第二个坑:浏览器工具下载 Chromium 会超时
既然要真实浏览器,那就装自动化工具。agent-browser 这类 CLI 有个 install 子命令负责拉 Chromium,约 198MB。
实际结果:
40/198 MB (20%)
Retrying download (attempt 2/3)
Retrying download (attempt 3/3)
✗ Download error: operation timed out国内网络拉 Google 的 chrome-for-testing 源,超时是常态。不要在这步死等。
第三个坑:headless 模式也会被拦
绕过下载的办法:你机器上大概率已经有 Chrome 或 Edge,直接拿来用。
npm install playwright-coreplaywright-core 不含浏览器,只有驱动,几 MB,秒装。然后指向本机 Chrome:
const browser = await chromium.launch({
executablePath: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
headless: true
});结果访问后台直接 30 秒超时。改成 headless: false 立刻正常——同样的 cookie、同样的 URL。CDN 对无头浏览器同样有识别。
最终能跑通的方案
const { chromium } = require('playwright-core');
(async () => {
const browser = await chromium.launch({
executablePath: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
headless: false,
args: ['--disable-blink-features=AutomationControlled']
});
const ctx = await browser.newContext({
viewport: { width: 1280, height: 860 },
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
});
const page = await ctx.newPage();
await page.goto('https://example.com/admin/', { waitUntil: 'domcontentloaded' });
await page.fill('#name', '你的账号');
await page.fill('#password', '你的密码');
await page.check('#remember'); // 勾选记住登录,cookie 有效期更长
await page.click('button[type="submit"]');
await page.waitForTimeout(5000);
console.log(await page.title()); // 输出「网站概要」即为成功
await ctx.storageState({ path: 'state.json' }); // 保存登录态
await browser.close();
})();跑完看到标题变成「网站概要 - ...」,就算进去了。
storageState 这一步很值钱:登录态存成 state.json 后,后续所有脚本都这样复用,不用再输一次密码:
const ctx = await browser.newContext({ storageState: 'state.json' });发布文章:先摸清表单字段
不同主题的写作页字段差异不小(特别是加了自定义字段的主题)。先探测再填,别猜:
document.querySelectorAll('input[name], select[name], textarea[name]').forEach(el => {
console.log(el.tagName, el.name, el.type, el.id);
});Typecho 原生字段一般是这些:
title标题、text正文(Markdown)、slug别名category[]分类(checkbox,值是分类 ID)tags标签、date日期visibility可见性、allowComment允许评论
带自定义字段的主题还会有 fields[excerpt](摘要)、fields[imgst](封面图)、fields[catalog](目录开关)这类,按需填。
填完点「发布文章」提交:
await page.fill('#title', '文章标题');
await page.evaluate(t => { document.querySelector('#text').value = t; }, 正文内容);
await page.check('#category-1'); // 勾选分类,注意是 ID 不是名字
await page.fill('#tags', '标签1,标签2');
await page.click('button:has-text("发布文章")');
await page.waitForTimeout(5000);摘要里那句「注意是 ID 不是名字」是血泪:分类复选框的 id 是 category-1 这种,对应的是分类 ID,得先遍历一遍把 ID 和名称对应上:
document.querySelectorAll('input[name="category[]"]').forEach(el => {
const lab = document.querySelector(`label[for="${el.id}"]`);
console.log(el.value, lab ? lab.textContent.trim() : '?');
});几条安全提醒
折腾完回看,这几条比技术细节更重要:
- 密码不要写进任何笔记或脚本。登录一次拿到
state.json就够了,后续全靠它 state.json等同于账号权限,别提交到公开仓库,也别随手发给别人- 发文章前先看一眼草稿。自动化发错分类、发错时间,删起来很烦
- 采集来的内容要重写再发,直接搬运既有版权风险,也不利于收录
- 文章里涉及自己站点的后台路径、账号名,一律用
example.com之类替代
小结
三个坑其实指向同一件事:现代站点前面挡着 CDN,自动化得按真实浏览器的路子走。curl 走不通,无头浏览器走不通,用本机浏览器 + 有头模式反而最省事。
真正跑通之后,体验是值得的:内容整理、排版、配图、发布一气呵成,人只做最后的判断。这条路走顺了,后面就是批量化的事了。


