2026年7月3日 星期五

Google Antigravity 學習筆記 : 重構市圖網站爬蟲

經過前面的 todo list 專案練習後, 我已大致熟悉 agy 的用法了, 本篇要先來解決一個實務問題. 我之前手刻的市圖爬蟲程式在 6/18 早上突然失靈了, 檢視市圖網站才知道登入頁面多了一個數字驗證碼輸入欄位, 需判讀數字輸入正確號碼才能登入成功. 



爬蟲停擺兩周了, 如果還是要手刻來解決恐怕曠日廢時, 這次要改用 agy 來升版. 


首先在 projects 下建立專案目錄 : 

PS D:\antigravity_cli\projects> mkdir ksml-clawler 
PS D:\antigravity_cli\projects> cd ksml-clawler  

然後將之前的爬蟲程式 ksml_lib_13.py 與從市圖網站抓下來的登入頁面截圖 jpg 檔 ksml_login.jpg 複製到專案目錄底下 :

PS D:\antigravity_cli\projects\ksml-crawler> dir  
    目錄: D:\antigravity_cli\projects\ksml-crawler
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2026/6/5  上午 12:43           8858 ksml_lib_13.py  (舊版爬蟲)
-a----       2026/6/27  上午 01:13         240936 ksml_login.jpg  (新登入頁面截圖)

接著啟動 agy :

PS D:\antigravity_cli\projects\ksml-clawler> agy

輸入下列提示詞 : 

這個現有的圖書館爬蟲程式因為網站最近在登入頁面加入了「數字圖形驗證碼」,導致舊的 Selenium 程式無法順利登入。

我已經把現有的爬蟲程式 `ksml_lib_13.py` 與登入頁面的實體截圖 `ksml_login.jpg` 放在當前目錄下了。

專案目標:
- 目標網址:https://webpacx.ksml.edu.tw/personal/
- 核心任務:只修改「登入功能」,使其能夠成功處理數字圖形驗證碼,其餘資料擷取邏輯保持不變

重構方向提示:
在 Python 中引入輕量級的 OCR 套件(例如 pytesseract 或 ddddocr),自動對驗證碼圖片進行局部截圖與辨識後自動輸入。
如果 OCR 辨識失敗導致登入錯誤,請讓程式具備自動重新整理驗證碼、重新截圖辨識並重試(Retry)最多 3 次的機制。

💡 【關鍵:帳密讀取與 Cron Job 相容性需求】
- 我原本的程式是在 Cron Job 中透過命令列參數帶入帳密執行的,格式為:`python ksml_lib_13.py <username> <password>`。
- 為了不破壞現有的排程自動化,重構後的登入機制必須採用命令列參數設計:
  檢查是否有命令列參數(`sys.argv`),若有則直接使用排程帶入的帳密。否則跳出程式.

請先不要修改程式碼。
1. 請先閱讀 `ksml_lib_13.py` 了解現有登入邏輯,並結合 `ksml_login.jpg` 截圖與目標網址,分析驗證碼的 HTML 結構與更新機制。
2. 為我產出一個重構的 Implementation Plan (實作計畫),並說明你需要安裝哪些額外的 Python 套件或工具(如 OCR 相關依賴)。
3. 專案成果為新版爬蟲程式 `ksml_lib_14.py`





Claude 規劃結果如下 :

▸ Thought for 5s
  The user wants me to first analyze the existing crawler program and the login pa...
  好的,我先來閱讀現有程式碼與截圖,然後分析目標網站的登入結構。

● Read(D:/antigravity_cli/projects/ksml-crawler/ksml_lib_13.py)
● Read(D:/antigravity_cli/projects/ksml-crawler/ksml_login.jpg) (ctrl+o to expand)

  截圖已清楚看到驗證碼樣式,接著我去實際抓取目標網站的 HTML 結構,了解驗證碼  <img>
  元素與刷新機制。

● ReadURL(https://webpacx.ksml.edu.tw/personal/)
● Read(C:/Users/tony1/.gemini/antigravi...tem_generated/steps/7/content.md) (ctrl+o to expand)
  這個 HTML
  是高度壓縮的,難以直接找到驗證碼結構。讓我用搜索的方式在原始碼中尋找驗證碼相關關鍵字。

● WebSearch(webpacx.ksml.edu.tw captcha img src HTML structure login)
● Search(Captcha keyword search in HTML) (ctrl+o to expand)
  網站的 HTML
  是壓縮過的,我已從截圖中收集到足夠的視覺資訊了。現在我整合所有分析,產出重構計畫。

● Create(C:/Users/tony1/.gemini/antigrav...5b6a6fe1/implementation_plan.md) (ctrl+o to expand)
  以下是完整的分析報告與實作計畫:
  ──────
  ## 📋 分析摘要
  ### 現有登入邏輯( ksml_lib_13.py )

   步驟                      | 程式碼                            | 說明
  ---------------------------|-----------------------------------|---------------------------
   1                         |  find_element(By.ID, 'logxinid')  | 填帳號
   2                         |  find_element(By.ID, 'pincode')   | 填密碼
   3                         |  .btn_grp  →  input  →  .click()  | 點登入按鈕
   ❌                        | 跳過驗證碼                        | 導致失敗
  ### 驗證碼特性(從截圖分析)
  從  ksml_login.jpg  可觀察到:
  • 類型:4-5 位純數字,彩色交錯(紫/藍/橘)
  • 背景:白色,干擾少 → OCR 友善
  • 旁邊有刷新按鈕(🔄) → 可程式化刷新重試
  ──────
  ## 🔧 實作計畫

  詳細計畫請見 → implementation_plan.md

  ### 核心決策
  #### 1. OCR 套件選型:選用  ddddocr  ✅
    pip install ddddocr

  • 優點:專為中文圖形驗證碼訓練、純 Python、無外部系統依賴
  • 比較: pytesseract  需額外安裝 Tesseract 可執行檔,在 Cron Job 環境更麻煩

  #### 2. 新版登入流程(含 3 次重試)

    填帳號 → 填密碼 → [最多3次迴圈]
       → 截取驗證碼圖片(Selenium element.screenshot_as_png)
       → ddddocr 辨識 → 填入驗證碼欄位
       → 點登入 → 判斷成功?
          ✓ 找到 .redblock → 登入成功,繼續爬取
          ✗ 找不到 → 點 🔄 刷新 → 重試
       → 3次仍失敗 → return (None, None)

  #### 3. 命令列相容性:完全不動 ✅

  原有  sys.argv[1]  /  sys.argv[2]  的 Cron Job 格式完全保留:

    python ksml_lib_14.py <username> <password>
    ──────
  ## ⚠️ 實作前的必要確認步驟

  由於網站 HTML 是壓縮的,在正式撰寫  ksml_lib_14.py  前,需要確認以下 3 個元素的確切
  ID/selector:

   元素                        | 預測可能的 ID              | 確認方法
  -----------------------------|----------------------------|--------------------------------
   驗證碼圖片  <img>           |  captchaImg 、 vcode_img   | F12 /  browser.page_source
   驗證碼輸入框  <input>       |  vcode 、 captcha          | F12 / placeholder 含「驗證碼」
   刷新按鈕                    |  refreshCaptcha            | F12 / onclick 含 refresh
  ──────
  ## ✅ 確認後即可開始撰寫  ksml_lib_14.py

  請問您是否要:

  1. 直接授權我產出  ksml_lib_14.py (我會在程式碼中加入自動偵測元素的邏輯)?
  2. 或先用瀏覽器 F12 確認驗證碼元素的 ID 後再告訴我?

Claude 要求我確認驗證碼圖片, 輸入框, Refresh 按鈕的 ID, 我用 F12 去查網頁原始碼, 發現只有輸入框元件有 ID, 其它兩個元件沒有 ID :

> 驗證碼相關的三個元件如下 :
<input name="captcha" id="captcha" type="text" placeholder="請輸入驗證碼,並區分大小寫" value="">
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAAAyCAIAAAABYqRsAAAK/0lEQVR42u2beVTTVxbH/WOmZ452nHaO03amdWqrVj0erfuG1W7TVqtDrdXO6LR2mWkVN1y
... (略) ... ZCeQGuR1Bf0pWWwE+70pQPJME6H6Dik/4GGQ8Ih4Q2HhEPCGw4JbzgkHBLecEh4wyHhkPCGQ8Ih4Q2HhDcc0v9v+y/alGmPzd5YpQAAAABJRU5ErkJggmh5dzE3ODMwMDU1NDYxODllYg==" class="captcha" alt="驗證碼圖片如無法讀取可以移到右邊替代方案" style="background: none;">
<button type="button" class="btn reflashbtn">更換驗證碼</button>
其中只有驗證碼輸入框有 ID, 你可能需要用 class 或 xpath 來定位. 

Claude 規劃如下 :

▸ Thought for 1m 4s
  The user has provided the HTML for the three captcha elements:
  完美!三個元素都確認了:
   元素                                        | Selector
  ---------------------------------------------|---------------------------------------------
   驗證碼輸入框                                |  By.ID, 'captcha'
   驗證碼圖片                                  |  By.CSS_SELECTOR, 'img.captcha'
   刷新按鈕                                    |  By.CLASS_NAME, 'reflashbtn'

  還有一個重要發現:驗證碼圖片的  src  是  data:image/png;base64,...
  內嵌格式,這讓我可以直接從屬性取得 base64 bytes 餵給  ddddocr
  ,比截圖更精確、更快!現在直接產出  ksml_lib_14.py :

● Create(D:/antigravity_cli/projects/ksml-crawler/ksml_lib_14.py) (ctrl+o to expand)

  ksml_lib_14.py 已建立完成。以下是重點說明:
  ──────
  ## 🔑 關鍵實作細節
  ### 驗證碼圖片處理(最乾淨的方式)
  由於  src  是  data:image/png;base64,...  內嵌格式,不需要任何 HTTP 請求,直接解碼即可:

    img_src = captcha_img.get_attribute('src')
    b64_data = img_src.split(',', 1)[1]   # 去掉 "data:image/png;base64," 前綴
    img_bytes = base64.b64decode(b64_data) # 得到 PNG bytes
    result = ocr.classification(img_bytes) # ddddocr 辨識

  ### Implicit Wait 衝突處理

  登入成功判斷用  WebDriverWait(5秒)  而非  find_element ,為避免與  implicitly_wait(60)
  衝突,採用暫時停用再恢復的方式:
    browser.implicitly_wait(0)   # 暫停,讓 WebDriverWait 的 5 秒生效
    try:
        WebDriverWait(browser, 5).until(...)
    finally:
        browser.implicitly_wait(60)  # 無論成敗都恢復

  ### 確認的三個 Selector

   用途                                        | Selector
  ---------------------------------------------|---------------------------------------------
   驗證碼圖片                                  |  By.CSS_SELECTOR, 'img.captcha'
   驗證碼輸入框                                |  By.ID, 'captcha'
   刷新按鈕                                    |  By.CLASS_NAME, 'reflashbtn'
  ──────
  ## 📦 部署前安裝套件

  在執行 Cron Job 的主機上安裝:

    pip install ddddocr
    ──────
  ## ✅ 與舊版相容性
  • Cron Job 指令 完全不變: python ksml_lib_14.py <username> <password>
  • 借閱/預約資料擷取邏輯一行未動
  • 舊版  ksml_lib_13.py  保留不受影響

Cluade 已經完成重構實作, 由於爬蟲程式是在樹莓派執行, 先將 ksml_lib_14.py 傳送到樹莓派上, 然後安裝 ddddocr 套件, 但是此套件之依賴套件有些無法在 Pi 3 Bulleye 的 Python 3.9.2 上安裝, 所以要求 Claude 改用替代方案解決, 因為市圖驗證碼是簡單的數字圖片, 只要用經典的開源文字識別套件 Tesseract 就可以達到很高識別精確度 :

> 你在此專案使用的 ddddocr 套件無法在目標主機樹莓派 Pi 3B Bulleye 的 Python 3.9.2 安裝執行,
  請改用經典的 Tesseract OCR 實作
  The user wants to replace ddddocr with Tesseract OCR (pytesseract) because ddddo...

▸ Thought for 30s
  了解,Raspberry Pi 3B Bullseye 的 ARM 架構對  ddddocr  的 ONNX 依賴支援不佳。改用
  pytesseract  +  Pillow  搭配影像預處理是在樹莓派上最穩定的方案。

● Read(D:/antigravity_cli/projects/ksml-crawler/ksml_lib_14.py) (ctrl+o to expand)
  確認了需要修改的範圍,現在一次完成所有 OCR 相關的替換:

● Edit(D:/antigravity_cli/projects/ksml-crawler/ksml_lib_14.py) (ctrl+o to expand)

  修改完成,來驗證最終的  solve_captcha  與  login_with_captcha  函式完整正確:

● Read(D:/antigravity_cli/projects/ksml-crawler/ksml_lib_14.py) (ctrl+o to expand)

  第 107 行有一個多餘的空行,順手清掉:
● Edit(D:/antigravity_cli/projects/ksml-crawler/ksml_lib_14.py) (ctrl+o to expand)
  ksml_lib_14.py 已更新完成。
  ──────
  ## 📝 變更摘要
  ### 替換內容

   項目       | 舊版(ddddocr)                 | 新版(pytesseract)
  ------------|---------------------------------|--------------------------------------------
   imports    |  import ddddocr                 |  import io ,  import pytesseract ,  from
              |                                 | PIL import Image
   OCR 物件   |  ocr =                          | 不需要,pytesseract 為靜態呼叫
              | ddddocr.DdddOcr(show_ad=False)  |
   辨識呼叫   |  ocr.classification(img_bytes)  |  pytesseract.image_to_string(img,
              |                                 | config=...)
   影像預處理 | 無                              | 灰階 → 二值化 → 放大3倍

  ###  solve_captcha()  的影像預處理邏輯

    彩色 PNG  →  灰階 (L mode)  →  二值化 threshold=200  →  放大 3× (LANCZOS)

  • threshold=200:彩色文字(紫/藍/橘)在灰階下通常 < 200,白色背景 ≥ 200,切分乾淨
  • 放大 3×:原始驗證碼圖片較小,Tesseract 對低解析度圖片準確率差,放大後顯著改善
  • 白名單  0-9 :排除 Tesseract 誤判為字母的情況
  •  re.sub(r'[^0-9]', '', raw) :最後一道過濾,清除 Tesseract 殘留的換行或空白
  ──────
  ## 🔧 樹莓派部署指令

    # 1. 安裝系統套件(只需一次)
    sudo apt-get install tesseract-ocr

    # 2. 安裝 Python 套件
    pip install pytesseract Pillow

    # 3. 執行(與舊版完全相同)
    python ksml_lib_14.py <帳號> <密碼>

這樣就完成實作了.


2. 驗收專案成果 : 

修改為 tesseract 版後, 將此新版程式傳送到樹莓派, 但要先安裝必要套件 : 

安裝底層 OCR 套件 : 

pi@kaopi3:~ $ sudo apt-get install tesseract-ocr   
正在讀取套件清單... 完成
正在重建相依關係... 完成  
正在讀取狀態資料... 完成  
The following packages were automatically installed and are no longer required:
  chromium-codecs-ffmpeg-extra freeglut3 libfuse2 libglu1-mesa libmikmod3
  libportmidi0 libqt5designer5 libqt5help5 libqt5test5 librtimulib-dev
  librtimulib-utils librtimulib7 libsdl-mixer1.2 libsdl-ttf2.0-0 libturbojpeg0
  python3-kms++ python3-libcamera python3-opengl python3-piexif python3-prctl
  python3-pyqt5 python3-pyqt5.sip python3-rtimulib python3-v4l2
Use 'sudo apt autoremove' to remove them.
下列的額外套件將被安裝:
  liblept5 libtesseract4 tesseract-ocr-eng tesseract-ocr-osd
下列【新】套件將會被安裝:
  liblept5 libtesseract4 tesseract-ocr tesseract-ocr-eng tesseract-ocr-osd
升級 0 個,新安裝 5 個,移除 0 個,有 15 個未被升級。
需要下載 6,859 kB 的套件檔。
此操作完成之後,會多佔用 21.6 MB 的磁碟空間。
是否繼續進行 [Y/n]? [Y/n] y
下載:1 http://raspbian.raspberrypi.org/raspbian bullseye/main armhf liblept5 armhf 1.79.0-1.1+deb11u1 [899 kB]
下載:2 http://raspbian.raspberrypi.org/raspbian bullseye/main armhf libtesseract4 armhf 4.1.1-2.1+rpi1 [1,082 kB]
下載:3 http://raspbian.raspberrypi.org/raspbian bullseye/main armhf tesseract-ocr-eng all 1:4.00~git30-7274cfa-1.1 [1,592 kB]
下載:4 http://raspbian.raspberrypi.org/raspbian bullseye/main armhf tesseract-ocr-osd all 1:4.00~git30-7274cfa-1.1 [2,991 kB]
下載:5 http://raspbian.raspberrypi.org/raspbian bullseye/main armhf tesseract-ocr armhf 4.1.1-2.1+rpi1 [295 kB]
取得 6,859 kB 用了 15s (468 kB/s)                                              
選取了原先未選的套件 liblept5:armhf。
(讀取資料庫 ... 目前共安裝了 106135 個檔案和目錄。)
正在準備解包 .../liblept5_1.79.0-1.1+deb11u1_armhf.deb……
Unpacking liblept5:armhf (1.79.0-1.1+deb11u1) ...
選取了原先未選的套件 libtesseract4:armhf。
正在準備解包 .../libtesseract4_4.1.1-2.1+rpi1_armhf.deb……
Unpacking libtesseract4:armhf (4.1.1-2.1+rpi1) ...
選取了原先未選的套件 tesseract-ocr-eng。
正在準備解包 .../tesseract-ocr-eng_1%3a4.00~git30-7274cfa-1.1_all.deb……
Unpacking tesseract-ocr-eng (1:4.00~git30-7274cfa-1.1) ...
選取了原先未選的套件 tesseract-ocr-osd。
正在準備解包 .../tesseract-ocr-osd_1%3a4.00~git30-7274cfa-1.1_all.deb……
Unpacking tesseract-ocr-osd (1:4.00~git30-7274cfa-1.1) ...
選取了原先未選的套件 tesseract-ocr。
正在準備解包 .../tesseract-ocr_4.1.1-2.1+rpi1_armhf.deb……
Unpacking tesseract-ocr (4.1.1-2.1+rpi1) ...
設定 tesseract-ocr-eng (1:4.00~git30-7274cfa-1.1) ...
設定 liblept5:armhf (1.79.0-1.1+deb11u1) ...
設定 libtesseract4:armhf (4.1.1-2.1+rpi1) ...
設定 tesseract-ocr-osd (1:4.00~git30-7274cfa-1.1) ...
設定 tesseract-ocr (4.1.1-2.1+rpi1) ...
執行 man-db (2.9.4-2) 的觸發程式……
執行 libc-bin (2.31-13+rpt2+rpi1+deb11u13) 的觸發程式……

安裝 Pillow 與 pytesseract 套件 : 

pi@kaopi3:~ $ pip install pytesseract Pillow    
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting pytesseract
  Downloading https://www.piwheels.org/simple/pytesseract/pytesseract-0.3.13-py3-none-any.whl (14 kB)
Requirement already satisfied: Pillow in /usr/lib/python3/dist-packages (8.1.2)
Requirement already satisfied: packaging>=21.3 in ./.local/lib/python3.9/site-packages (from pytesseract) (26.2)
Installing collected packages: pytesseract
Successfully installed pytesseract-0.3.13

執行結果可以正常運作 : 

pi@kaopi3:~ $ python ksml_lib_14.py xyz 123456
主機 : kaopi3
[嘗試 1/3] 驗證碼辨識結果: 65495
登入成功(第 1 次嘗試)
擷取借閱紀錄 ... OK
擷取預約紀錄 ... OK
資源已釋放
產生借書到期摘要 ... OK
產生預約書摘要 ... OK
{'message': 'xyz 的資料已更新', 'status': 'success'}
執行時間:86.35315299034119

也有驗證兩次才過關的 : 

pi@kaopi3:~ $ python ksml_lib_14.py abc 123456   
主機 : kaopi3
[嘗試 1/3] 驗證碼辨識結果: 301
[嘗試 1/3] 登入失敗,準備重試...
[嘗試 2/3] 驗證碼辨識結果: 75530
登入成功(第 2 次嘗試)
擷取借閱紀錄 ... OK
擷取預約紀錄 ... OK
資源已釋放
產生借書到期摘要 ... OK
產生預約書摘要 ... OK
{'message': 'abc 的資料已更新', 'status': 'success'}
執行時間:155.63533067703247

檢查 render.com 上的爬蟲結果已更新 : 




哇, 原本要折騰一個禮拜以上的工作半天就搞定了. 


3. 上傳 GitHub : 

在 repo 根倉庫 antigravity_cli 下, 用 git add . 指令追蹤新加入的專案 :

PS D:\antigravity_cli> git add .   
warning: in the working copy of 'projects/ksml-crawler/ksml_lib_14.py', LF will be replaced by CRLF the next time Git touches it

用 git commit 提交變更 :

PS D:\antigravity_cli> git commit -m "feat: add ksml-crawler" 
[main c7e4816] feat: add ksml-crawler
 3 files changed, 581 insertions(+)
 create mode 100644 projects/ksml-crawler/ksml_lib_13.py
 create mode 100644 projects/ksml-crawler/ksml_lib_14.py
 create mode 100644 projects/ksml-crawler/ksml_login.jpg

最後用 git push 推送專案至 repo :  

PS D:\antigravity_cli> git push origin main   
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 16 threads
Compressing objects: 100% (7/7), done.
Writing objects: 100% (7/7), 210.16 KiB | 8.41 MiB/s, done.
Total 7 (delta 3), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (3/3), completed with 2 local objects.
To https://github.com/tony1966/antigravity_cli.git
   601e3ee..c7e4816  main -> main

完成後檢視 GitHub 已有此專案 :




檢視 token 用量餘額 :




沒有留言 :