iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0

! 本篇文章將會介紹 截圖驗證:眼見為憑的自動化驗收,期望大家都能讓每一發自動化動作都留下可稽核、可除錯的證據 :D

你能看到這篇文章,代表昨晚 20:00 那串自動化動作成功了:開頁、填表、點發佈、輪詢 URL。但問題來了——我人在耍廢,沒人盯著螢幕,我要怎麼知道這些動作「真的發生了」,而不是腳本「以為」它們發生了?d17 收尾埋的伏筆今天兌現:把零星使用的 screenshot 升級成整條管線的標準配備。核心觀念一句話:log 告訴你腳本「認為」發生了什麼,截圖告訴你畫面上「實際」發生了什麼。無人值守的自動化系統,存證就是你的眼睛。

本篇目標

讀完這篇你會學到:

  • 截圖存證的命名紀律:天數+時間戳,讓證據自己排好隊
  • 三層驗證架構:log(腳本敘事)、URL/DOM(結構事實)、截圖(視覺事實)各司其職
  • 失敗自動留遺照:每個錯誤分支先拍照再喊救命,隔天早上對著照片驗屍

環境準備

  • d15 裝好的 agent-browser,加上 d16 的 ab() 綁定(session 綁 profile,iThelp 登入狀態常駐)
  • d17 的填表與等待策略——截圖時機正建立在「等對了再拍」之上
  • 一個專門放證據的資料夾,本系列用 /Users/benben/ai/automations/ironman/screenshots/
# 本篇沿用的綁定函式(d16)
ab() { agent-browser --session ithelp --profile "$HOME/.agent-browser-profiles/ithelp" "$@"; }

# 證據集中營:一個只放截圖的資料夾
mkdir -p /Users/benben/ai/automations/ironman/screenshots

主要內容

步驟一:命名紀律——讓證據自己排好隊

隨手拍隨手丟,等於沒拍。排程系統的截圖必須能回答三個問題:**哪一天?哪個流程?幾點幾分拍的?**本系列發文腳本的命名公式:

# publish.sh 的截圖路徑:天數 + 時分秒,同一天跑好幾輪也不會互相覆蓋
SHOT="$ROOT/screenshots/publish-d${DD}-$(date +%H%M%S).png"
"${AB[@]}" screenshot "$SHOT"

跑了一陣子之後,存證資料夾會長成這樣(節錄):

screenshots/
├── publish-d01-200330.png                    # 20:03:30 的發文現場
├── publish-d01-200330-turnstile-timeout.png  # token 逾時當下的失敗現場
├── publish-d01-manual-retry-success.png      # 人工搶救成功後補拍
└── publish-d02-200005.png

兩個好處。第一,ls 一眼看完時間軸,出事時間點直接對得上 log;第二,失敗變體用後綴(-turnstile-timeout)接在主檔名後面,同一輪的成功與失敗現場不會散落兩處。

再送一個冷知識:檔案大小是免費的健康檢查。正常頁面的截圖動輒數百 KB 起跳,哪天資料夾裡冒出一張個位數 KB 的,八成拍到的是錯誤頁或空白頁——先看圖再說話,別急著相信 log。

步驟二:三層驗證——log、DOM、截圖各管各的事實

小小小測驗:你知道 log 上寫「發佈成功」,其實只代表腳本自己這樣認為嗎?
答案:log 是單方面的敘事,exit 0 只證明腳本自己沒炸,不證明外部世界真的改變了。驗收要三層交叉:log 記錄腳本做了什麼、URL/eval 讀出頁面的結構事實、截圖保存畫面的視覺事實。三層對得起來,才叫眼見為憑。

以發佈驗收為例,三層長這樣:

# 第一層:log——腳本的敘事(何時點了什麼)
log "點擊發佈(第 ${attempt}/3 次)"

# 第二層:URL——頁面的結構事實(成功特徵:/articles/10xxxx 且沒有 /draft)
AFTER=$("${AB[@]}" get url 2>/dev/null)
[[ "$AFTER" =~ /articles/10[0-9]+$ ]] && PUBLISHED=1

# 第三層:screenshot——視覺事實,成功失敗都拍
"${AB[@]}" screenshot "$SHOT"

還記得 d17 的測驗嗎?「按下去的瞬間,資料還沒離開瀏覽器」。所以第二層的 URL 不是點完馬上讀,而是輪詢到伺服器回話為止:

# 送出後每 5 秒看一次 URL,最多等 30 秒(節錄自 publish.sh)
for i in $(seq 1 6); do
  sleep 5
  AFTER=$("${AB[@]}" get url 2>/dev/null)
  [[ "$AFTER" =~ /articles/10[0-9]+$ ]] && { PUBLISHED=1; break; }
done

三層的分工故意不重疊:log 給時間軸、URL 給機器可判讀的成功訊號(if 判斷用)、截圖給人眼複核(爭議時的仲裁者)。機器自動判斷靠第二層,人類事後驗屍靠第三層,誰也取代不了誰。

步驟三:失敗遺照——讓隔天早上好過一點

成功截圖是放彩帶,失敗截圖是救命繩。原則只有一條:每個 die 之前先拍照。失敗當下的頁面狀態是唯一現場,晚個幾秒 SPA 一跳版,什麼都不剩:

# publish.sh 的失敗分支:先存證,再喊救命(die 會附帶 Discord 告警)
if [[ -n "$PUBLISHED" ]]; then
  "${AB[@]}" screenshot "$SHOT"
  # …寫 flag、發成功通知…
else
  "${AB[@]}" screenshot "$SHOT"   # 失敗也要留遺照
  die "發佈重試 x3 後 URL 仍異常(${AFTER:-unknown}),請人工確認(截圖:${SHOT})"
fi
# Turnstile token 等不到,放棄前也補拍一張現場,後綴標記失敗原因
"${AB[@]}" screenshot "${SHOT%.png}-turnstile-timeout.png"

實際戰績分享一下:第一天晚上,iThelp 尖峰時段的發佈鈕按下去「靜默無反應」,log 停在「點擊發佈」就沒了下文。隔天早上對著存證資料夾的截圖和 log 交叉比對,確認頁面從頭到尾停在原地——「點擊後輪詢 URL、失敗自動重點」就是那晚之後補上的。d12 講的重試設計,靈感正是來自這張遺照。截圖存證不只是驗收工具,更是系統演化的養分:每張失敗現場照,都是下一版腳本的需求單。

常見問題 / 踩坑記錄

  • Q:截圖拍了,打開卻是一片空白或載入到一半?
    A:拍太早。screenshot 拍的是按下 Enter 那一瞬,SPA 還在渲染就是空白。解法跟 d17 同款:先 wait 目標元素再拍;導航之後想拍全貌,先 wait --load networkidle 等請求潮退掉。
  • Q:log 全綠、截圖看起來也正常,結果文章根本沒發出去?
    A:這是反過來的坑——截圖只能證明「畫面長怎樣」,不能證明「伺服器收下了什麼」。前端渲染成功不代表提交成功,驗收仍要以第二層的 URL/DOM 事實為準,截圖是輔助仲裁,不是唯一判準。三層缺一不可。
  • Q:截圖越積越多,磁碟和眼睛都受不了?
    A:留存分級——成功輪留當日最後一張即可,失敗現場照全保留;再用一行指令定期清場:
    # 只留最近 7 天的例行存證;失敗變體(-timeout、manual-retry)不在比對範圍,人工決定去留
    find "$ROOT/screenshots" -name "publish-d*-2*.png" -mtime +7 -delete
    

小結

  • 命名帶天數與時間戳、失敗變體用後綴,證據自己排好隊,ls 就是時間軸
  • 三層驗證:log 管敘事、URL/DOM 管機器判讀、截圖管人眼仲裁,互相對帳才叫驗收
  • 每個失敗分支先拍照再 die——遺照是除錯的起點,也是系統演化的養分
  • 檔案大小是免費健康檢查,留存分級讓存證可以長期經營

明日預告

下一篇我們要介紹「iT 邦幫忙發文實戰(上):編輯器解析」,終於要開箱本系列的最終戰場——拆解 iThelp 發文頁的結構、markdown 模式與分類選擇,把今天學的驗收招式帶進真實戰場,敬請期待!

參考資料:

有任何疑問但沒有 iT 邦幫忙帳號,或是想匿名提問?
歡迎到 https://dev.benben.me/q/Z5442T 提問或加油打氣,沒意外的話會在完賽之後一起回答 :D


上一篇
17 網頁填表實戰:教 Agent 填表格
下一篇
19 iT 邦幫忙發文實戰(上):編輯器解析
系列文
自我耍廢組:全自動化の鐵人 共 19 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言