! 本篇文章將會介紹 iT 邦幫忙發文實戰(上):編輯器解析,期望大家都能在按下自動化開關之前,先把目標網頁的結構看個清楚 :D
昨天預告的「最終戰場開箱」今天兌現。前 18 天,Agent 已經學會寫文章(d03–d07)、被排程叫起床(d09–d12)、還會主動回報戰況(d13);瀏覽器的基本功——填表、等待、截圖驗證——也在 d15–d18 練完了一輪。萬事俱備,只欠把這些功夫全部砸在 iThelp 的發文頁上。但我有個鐵律:自動化之前,先當一分鐘的真人——手動走一次流程、把頁面結構摸清楚,才知道要跟哪些元件打交道。所以今天不按任何按鈕,先當偵察兵,把發文頁拆成三塊來看:入口與表單結構、markdown 編輯器、分類與 tag。明天再把拆下來的零件組裝成 publish.sh。
讀完這篇你會學到:
article_type 為什麼是防呆關鍵# 沿用的綁定函式(d16)
ab() { agent-browser --session ithelp --profile "$HOME/.agent-browser-profiles/ithelp" "$@"; }
# 開賽前校準:開發文頁、輸出 snapshot 與截圖(scripts/calibrate.sh 的 probe 子指令)
./scripts/calibrate.sh probe
# 產出:state/snapshot-*.txt(accessibility tree)、state/screenshot-*.png
# 之後找 selector 全靠這份事實,不要憑想像猜
iThelp 有兩種發文入口,長得像但身世完全不同:
https://ithelp.ithome.com.tw/articles/create?group=tech,進去要自己選分類群組https://ithelp.ithome.com.tw/2026ironman/create/9571——網址尾端的 9571 就是系列 id鐵人入口有三個行為差異,每一個都影響自動化設計:
article_type=ironman(一般文沒有)——這是文章計入賽事的識別第 3 點聽起來是貼心功能,對全自動系統卻是地雷:哪天入口重用到一篇一般草稿,文章發出去就不計賽事,等於白發。所以 publish.sh 的第一道守衛就是驗明正身:
# 鐵人賽模式確認:入口必須產出 article_type=ironman 的草稿(節錄自 publish.sh)
ATYPE=$("${AB[@]}" eval '(document.querySelector("input[name=article_type]")?.value||"none")' 2>/dev/null | tr -d '"')
if [[ "$ATYPE" != "ironman" ]]; then
die "此草稿 article_type=${ATYPE}(應為 ironman)——發文入口可能有誤,中止以免白發"
fi
順帶記一個會影響明天成敗的事實:這個頁面的 <form> 裡其實只有 _token 和 _method 兩個欄位,送出根本不是傳統 form submit,而是前端 JS 發 XHR。意思是——按了送出之後,頁面不會給你任何「成功」的表單回饋,成敗得另想辦法判斷。先記著,明天細講。
小小小測驗:你知道發文頁那個看似平平無奇的內文輸入框,其實早就被偷天換日過了嗎?
答案:HTML 原始碼裡只有一個textarea[name="description"],但頁面載入時 SimpleMDE 把它藏起來,換上自己的 CodeMirror 編輯器。你看到的編輯區根本不是那個 textarea——直接對 textarea fill,是寫給空氣看的。
校準時從 snapshot 拼出來的發文頁解剖圖,先奉上:
發文頁(2026ironman/create/9571)
├── input.post-header__title # 標題(name=subject),普通 input,fill 就好
├── textarea[name="description"] # 名義上的內文欄(被 SimpleMDE 藏起來的影子)
│ └── .CodeMirror # 真身:CodeMirror 實例,markdown 模式在這裡
├── .select2-search__field # tag 搜尋框(select2,不是原生 <select>)
│ └── .select2-selection__choice # 已掛上的 tag 們
├── input[name=article_type] # hidden:鐵人草稿 = ironman(防呆檢查點)
└── button.save-group__btn # 「儲存草稿」
└── ▲ .save-group__dropdown-toggle # 旁邊的 ▲ 展開下拉選單
└── ...--publish # 「發表文章」其實藏在這個下拉裡
SimpleMDE 是一套輕量 markdown 編輯器:工具列的粗體、連結、code 按鈕,實際行為就是幫你插入對應的 markdown 語法;編輯區本體由 CodeMirror 接手渲染。對人類是所見即所得的糖,對自動化則是一紙合約,三個條款:
.CodeMirror 是唯一真身:定位就抓這個 class# 主路線:用 CodeMirror API 直接 setValue,整篇 markdown 一次進場(節錄自 publish.sh)
BODY_JSON=$(jq -Rs . "$BODY_FILE") # markdown 檔先變成合法 JSON 字串
"${AB[@]}" eval "(function(){var cm=document.querySelector('.CodeMirror');
if(!cm||!cm.CodeMirror){return false}
cm.CodeMirror.setValue(${BODY_JSON});
return cm.CodeMirror.getValue().length})()" # 回傳字數 = 填入成功的佐證
# fallback:click 編輯區 + keyboard type(模擬真人打字,慢但通用)
"${AB[@]}" click 'textarea[name="description"]'
"${AB[@]}" keyboard type "$(cat "$BODY_FILE")"
順帶對手動發文的讀者說一句:這也解釋了為什麼可以直接把 markdown 原文貼進 iThelp 編輯器——工具列只是糖,語法才是本體。本系列從 generate.sh 剝掉 frontmatter 後的內文,就是一路 markdown 到底、原封不動進編輯器的。
分類這關,鐵人入口幫你省掉了:一般文要自己挑分類群組,鐵人入口則直接把文章掛在你的系列底下。所以腳本要處理的只剩 tag——而 tag 的互動元件是 select2,一個搜尋式下拉,不是原生 <select>,select 類指令對它完全無效。正確玩法是「逐個搜尋 + Enter 收貨」:
# select2 的正確玩法:填搜尋框 → 等下拉選項 → Enter(節錄自 publish.sh)
IFS=',' read -ra TAGARR <<<"${TAGS}" # TAGS="自動化,AI Agent,opencode"
for t in "${TAGARR[@]}"; do
"${AB[@]}" fill '.select2-search__field' "$t"
sleep 1 # 等匹配選項浮出來
"${AB[@]}" press Enter
done
三個坑先講在前面。第一,前綴匹配:搜尋「自動化」按 Enter,掛上來的會是下拉第一個匹配項「自動化測試」——想精確命中,得看著下拉清單點目標。第二,上限 5 個:鐵人流程自動加的「18th鐵人賽」也佔名額,自己想塞的其實只剩 4 個。第三,掛上不等於選對——這就輪到 d18 的三層驗證出場了,填完要讀出來對帳:
# 驗證:列出實際掛上的 tag,跟預期比對(節錄自 publish.sh)
"${AB[@]}" eval 'JSON.stringify([...document.querySelectorAll(".select2-selection__choice")].map(function(e){return e.textContent.replace(/[×✕]/g,"").trim()}))'
# 輸出範例:["18th鐵人賽","自動化測試","opencode"]
明天的 publish.sh 會把這段變成制式的 tag 驗證:缺了就發 Discord 警告,但不擋發佈——tag 少一個,不值得讓整篇文章卡關,這種「警告但放行」的分級就是 d12 講的防禦工事的縮影。
cm.CodeMirror.setValue();API 抓不到再 fallback 成 click + keyboard type。--headed 帶真 Chrome 才放行。而且這只是第一關——發佈動作後面還有 Cloudflare Turnstile,需要「真 Chrome + 停用 AutomationControlled + stealth init script」三件套,明天發佈實戰一併細講。article_type 防呆守門——綁錯直接中止不發。寧可漏發一篇,不要白發一篇。article_type=ironman、自動掛賽事 tag、草稿重用——入口選錯,一切白工<select>:搜尋 + Enter、上限 5 個、前綴匹配會送你意想不到的 tag下一篇我們要介紹「iT 邦幫忙發文實戰(下):一鍵全自動發文」,把今天拆解的所有零件組裝成 publish.sh:填表與驗證、dry-run 與 live 的模式切換、發佈重試與存證,一鍵到底,敬請期待!
參考資料:
有任何疑問但沒有 iT 邦幫忙帳號,或是想匿名提問?
歡迎到 https://dev.benben.me/q/Z5442T 提問或加油打氣,沒意外的話會在完賽之後一起回答 :D