陳帆的腳步在實驗室門口頓了一下。鑰匙還插在鎖孔裡,和剛才離開時一樣。他沒去碰門把手,而是直接推開了門。屋內隻有服務器風扇的低鳴,藍綠色的指示燈在機箱上規律閃爍,像某種沉睡中的呼吸。
屏幕中央彈著一個紅色警告框:主數據源中斷。時間戳是淩晨三點零七分。
李陽已經坐在終端前,手指懸在鍵盤上方,盯著一行不斷刷新的日誌。“試了三次重連,全都卡在驗證碼那步。”他聲音很平,但眼底有血絲,“新的驗證碼是動態生成的,字符扭曲,背景還有乾擾線。正則匹配完全失效。”
張遠從椅子上直起身子,手裡捏著一張打印紙,上麵密密麻麻貼滿了截圖。“這是昨晚到今早抓下來的兩百多個驗證碼樣本。”他把紙拍在桌上,“全是亂碼一樣的組合,K7X9、P2M5、R8V3……沒有一個是重複的。”
陳帆走到主控台前,調出網頁快照。放大後的驗證碼圖像布滿噪點,字母邊緣被故意拉伸變形,像是被人隨手塗改過。他盯著看了幾秒,轉身打開自己的筆記本,插入U盤,把權限配置文件暫時擱在一旁。
“不能繞開,那就打穿它。”他說,“我們自己做一個識彆模塊。”
李陽抬頭:“不用現成庫?”
“校園網進不來第三方框架,就算能下,授權問題也過不了關。”陳帆敲了幾行命令,調出係統架構圖,“而且這次不隻是破解,是要把識彆能力嵌進爬蟲流程裡,形成閉環。從請求頁麵開始,到截取圖像、分析字符、填表提交,全程自動化。”
張遠搓了把臉:“也就是說,咱們得從頭寫個識圖程序?”
“不是寫整個OCR。”李陽忽然開口,眼睛亮了起來,“我們可以做輕量級處理。先灰度化,再二值化,把顏色信息去掉,隻留黑白結構。然後用連通域分析剝離噪點——這些乾擾線通常是斷續的,真正的字符是閉合區域。”
陳帆點頭:“接著用垂直投影法切分字符。每個字母占據的空間寬度不同,但大致可分段。”
“我來搭流程。”李陽迅速新建項目目錄,“輸入是截圖,輸出是四字符文本。中間加一層校驗,如果識彆結果不符合格式規則,自動重新抓取。”
“樣本呢?”張遠問。
“你負責收集和標注。”陳帆打開一個共享文件夾,“每張圖對應一個文本文件,寫明正確答案。兩千張起步,越多越好。”
張遠立刻掏出舊手機,連上電腦。“我用這個拍網頁,一次生成一個新驗證碼。拍完馬上標,不重樣。”
分工落定,三人各自進入狀態。
李陽埋頭編寫圖像預處理函數。他先測試灰度轉換算法,將一張帶背景紋理的驗證碼轉為單通道圖像,再通過閾值分割實現二值化。屏幕上原本雜亂的顏色塊變成了清晰的黑字白底,但噪點仍然粘連在字母邊緣。
“得加形態學操作。”他低聲自語,引入腐蝕與膨脹處理,逐步剝離附著物。幾分鐘後,一個原本模糊的“Q”終於顯現出完整輪廓。
張遠那邊節奏更快。手機每隔十秒就自動截圖一次,他一邊查看圖像質量,一邊手動輸入答案。到了中午,桌麵上已經堆了三十多頁標注表。他的手指發酸,視線也開始發花,但動作沒停。
“這玩意兒越來越難。”他揉了揉眼睛,指著屏幕上一組新樣本,“你看這個‘B’,右邊豎筆被拉長成波浪形,差點跟下一個字符連上了。”
“那就調整切割窗口大小。”李陽接過來看了一眼,“改成自適應分割,根據字符密度動態劃分區間。”
下午四點,第一個完整識彆流程跑通。程序成功從截圖中提取出“N6H2”,並自動填入表單字段。但他們很快發現,成功率隻有不到三成。大部分失敗出現在字符粘連或斷裂的情況下。
“需要更多訓練樣本。”李陽說,“尤其是那些極端變形的例子。”
張遠立刻加大采集頻率。他把手機固定在支架上,設置定時截圖,自己則一邊核對結果,一邊整理錯誤類型。到晚上九點,標注總量突破一千五百張。他的眼皮沉重得幾乎睜不開,但還是堅持把最後一批異常樣本歸類完畢。
“給你。”他把U盤遞給李陽,“這裡麵全是難辨彆的,比如‘0’和‘O’混在一起,或者‘I’和‘1’幾乎一模一樣。”
李陽接過U盤,導入測試集,重新訓練模型參數。他在識彆邏輯中加入容錯機製:當某個字符置信度低於閾值時,觸發二次采樣,並結合上下文推測最可能的結果。
淩晨兩點十七分,新版識彆模塊完成編譯。
陳帆啟動集成測試。程序自動打開模擬瀏覽器,加載目標網頁,截取驗證碼區域,調用本地模型進行解析,隨後將識彆結果填入登錄表單並提交請求。
第一輪,失敗。
第二輪,失敗。
第三輪,成功返回JSON數據包。
“通了!”張遠猛地站起身,撞翻了桌邊的水杯,水灑在鍵盤上也沒顧得上去擦。
李陽緊盯著控製台滾動的日誌,聲音壓得很低,卻止不住顫抖:“第21次嘗試成功……第22次……第23次……”
一條條綠色日誌持續刷屏。係統連續發起37次抓取任務,其中35次成功獲取目標數據,準確率94.6%。
陳帆看著後台重新流動起來的數據流,深色界麵上,一條條行情記錄如溪水般湧入數據庫。補錄進程已經開始,三年的曆史缺口正在被快速填補。
“這不是爬蟲了。”他輕聲說,“是穿上裝甲的戰士。”
李陽靠回椅背,耳機裡循環播放著他剛寫的錯誤分析腳本。他閉著眼,手指還在無意識地敲擊桌麵,仿佛仍在調試代碼。
張遠趴在桌上睡著了,手邊攤著最後一張未歸檔的標注表,紙角卷起,上麵用紅筆圈出一個特彆扭曲的“W”,旁邊寫著:“這種也算字母?”
陳帆將U盤重新插入主機,開始導入新成員權限配置文件。剛點擊確認,主屏幕右下角突然跳出一條高優先級警報:
國債轉債溢價率異常波動,五隻樣本券平均偏離理論價達2.7%,趨勢曲線呈加速上揚態勢。
他手指停在鼠標上,目光鎖定那條陡升的折線。
服務器陣列的指示燈依舊規律閃爍,數據流穩定,爬蟲程序正常運行。
他抬起左手,按下了通訊內線的呼叫鍵。