小爬在之前的兩篇文章 【python辦公自動化系列之金蝶K3自動登入(一)】、【python辦公自動化系列之金蝶K3自動登入(二)】帶大家系統搞定了K3客戶端的自動登入難題,但是搞定【自動登入】只是我們軟體自動化的第一步,我們還要搞定之後的傳參、下載報表資料、切換賬號登入等一系列實際的業務問題。
由於K3軟體在開發過程中,使用了大量自繪製的元件、控制元件,這些控制元件都無法通過SPY++或者Inspect等軟體檢測到,使得我們苦心學習的FindWindow、SendMessage等一系列win32API語法都失去了戰鬥力和用武之地。舉個例子,看下圖:
假如我們想要下載【科目餘額表】,則登入賬套後,我們需要陸續滑鼠左鍵單擊主控臺對應的【財務會計】、【總賬】、【財務報表】,最後滑鼠左鍵雙擊【01016 科目餘額表】元素,才能進入【科目餘額表】報表介面,而上面的這些元素都是ThunderRT6PictureBoxDC 類,看到類名中有picture關鍵字,其實你就該放棄FindWindow來定位這種元素的想法了。
我們可以怎麼做呢?一種方法是小爬後續要重點講到的【基於圖片識別元素並點選】,不過這裡我更想講講另外一個討巧的辦法。
K3的每個報表都有助記碼,類似於SAP的T-CODE。比如此處的【科目餘額表】報表,其助記碼就是01016,我們可以通過K3提供的助記碼查詢功能快速到達報表介面,如下圖(見K3主介面右上角):
我們如果可以定點陣圖中的textBox控制元件,對其賦值:助記碼,然後模擬傳送【回車】,一樣可以開啟對應的報表,顯然這條路快速且可行:
有了思路,程式碼只是水到渠成的事兒,小爬下面的程式碼示例供參考:
1 def sendAssistCode(mainK3Hwnd,assistCode): 2 '''假定已經找到K3主介面的控制程式碼且作為入口引數,然後找到助記碼視窗,傳送特定助記碼,直接去對應的功能報表''' 3 assistCodeHwnd=0 4 while assistCodeHwnd==0: 5 time.sleep(0.2) 6 '''''' 7 ABSActiveBarDockHWnd=win32gui.FindWindowEx(mainK3Hwnd,0,"ABSActiveBarDockWnd","DockTop") 8 ThunderRT6PictureBoHwnd1=win32gui.FindWindowEx(ABSActiveBarDockHWnd,0,"ThunderRT6PictureBoxDC","") 9 ThunderRT6PictureBoHwnd2=win32gui.FindWindowEx(ABSActiveBarDockHWnd,ThunderRT6PictureBoHwnd1,"ThunderRT6PictureBoxDC","") 10 assistCodeHwnd=win32gui.FindWindowEx(ThunderRT6PictureBoHwnd2,0,"ThunderRT6TextBox","") 11 '''在k3主介面輸入助記碼並登入特定報表窗''' 12 win32gui.SendMessage(assistCodeHwnd, win32con.WM_SETTEXT, None,assistCode) 13 win32gui.PostMessage(assistCodeHwnd, win32con.WM_KEYDOWN, win32con.VK_RETURN, 0) 14 time.sleep(0.01) 15 win32gui.PostMessage(assistCodeHwnd, win32con.WM_KEYUP, win32con.VK_RETURN, 0)
進入報表介面後,K3會彈出【過濾條件】視窗,等待使用者輸入條件,以便載入符合條件的報表資料,以【科目餘額表】的過濾條件為例,如下圖:
經過SPY++捕獲不難發現,該介面的所有元素基本都是winform元件,可以用win32gui庫來搞定其傳參和自動化操作,不過其過程相對複雜,需要逐一去捕獲並操作,閒話少說,小爬直接上示例程式碼:
1 def subjectBalanceFilter(conditionFlag1,conditionFlag2,conditionFlag3,conditionFlag4,conditionFlag5, 2 conditionFlag6,conditionFlag7,conditionFlag8,subjectLevel,subjectYearFrom,subjectMonthFrom,subjectYearTo,subjectMonthTo): 3 '''設定科目餘額表過濾條件,等待不超過10秒,捕獲【過濾條件】視窗,如果仍未出現,可能是出現了【異常彈窗】''' 4 filterConditionHwnd=0 5 while filterConditionHwnd==0: 6 time.sleep(0.3) 7 filterConditionHwnd=win32gui.FindWindow('ThunderRT6FormDC',"過濾條件") 8 IsWindowVisible=0 9 while IsWindowVisible==0: 10 time.sleep(0.3) 11 filterConditionHwnd=win32gui.FindWindow('ThunderRT6FormDC',"過濾條件") 12 IsWindowVisible=win32gui.IsWindowVisible(filterConditionHwnd) 13 print("已找到【科目餘額表】過濾條件視窗") 14 time.sleep(0.5) 15 subjectLevelHwnd=0 16 while subjectLevelHwnd==0: 17 time.sleep(0.3) 18 userControlDcHwnd=win32gui.FindWindowEx(filterConditionHwnd,0,"ThunderRT6UserControlDC", None) # ThunderRT6UserControlDC 19 condition1Hwnd=win32gui.FindWindowEx(userControlDcHwnd,0,"ThunderRT6CheckBox", "顯示核算專案明細") # 顯示核算專案明細 checkBox 20 condition2Hwnd=win32gui.FindWindowEx(userControlDcHwnd,0,"ThunderRT6CheckBox", "包括未過賬憑證") # 包括未過賬憑證 checkBox 21 condition3Hwnd=win32gui.FindWindowEx(userControlDcHwnd,0,"ThunderRT6CheckBox", "包括餘額為零的科目") # 包括未過賬憑證 checkBox 22 condition4Hwnd=win32gui.FindWindowEx(userControlDcHwnd,0,"ThunderRT6CheckBox", "包括餘額借貸方合計") # 包括未過賬憑證 checkBox 23 condition5Hwnd=win32gui.FindWindowEx(userControlDcHwnd,0,"ThunderRT6CheckBox", "包括沒有業務發生的科目(期初、本年累計)") # 包括未過賬憑證 checkBox 24 condition6Hwnd=win32gui.FindWindowEx(userControlDcHwnd,0,"ThunderRT6CheckBox", "包括本期沒有發生額的科目") # 包括未過賬憑證 checkBox 25 condition7Hwnd=win32gui.FindWindowEx(userControlDcHwnd,0,"ThunderRT6CheckBox", "包括本年沒有發生額的科目") # 包括未過賬憑證 checkBox 26 condition8Hwnd=win32gui.FindWindowEx(userControlDcHwnd,0,"ThunderRT6CheckBox", "顯示禁用科目") # 包括未過賬憑證 checkBox 27 AdvancedBtn=win32gui.FindWindowEx(userControlDcHwnd,0,"ThunderRT6CommandButton", "高階>>") # 高階按鈕 28 accountingPeriodPreviousHwnd=win32gui.FindWindowEx(userControlDcHwnd,0,"ThunderRT6Frame", None) # 憑證期間 29 accountingPeriodHwnd=win32gui.FindWindowEx(userControlDcHwnd,accountingPeriodPreviousHwnd,"ThunderRT6Frame", None) # 憑證期間 30 parentYearFromHwnd=win32gui.FindWindowEx(accountingPeriodHwnd,0,"ThunderRT6UserControlDC", None) # YearFrom 31 yearFromHwnd=win32gui.FindWindowEx(parentYearFromHwnd,0,"ThunderRT6TextBox", None) # YearFrom 32 parentMonthToHwnd=win32gui.FindWindowEx(accountingPeriodHwnd,parentYearFromHwnd,"ThunderRT6UserControlDC", None) # MonthTo 33 monthToHwnd=win32gui.FindWindowEx(parentMonthToHwnd,0,"ThunderRT6TextBox", None) # MonthTo 34 parentMonthFromHwnd=win32gui.FindWindowEx(accountingPeriodHwnd,parentMonthToHwnd,"ThunderRT6UserControlDC", None) # MonthFrom 35 monthFromHwnd=win32gui.FindWindowEx(parentMonthFromHwnd,0,"ThunderRT6TextBox", None) # MonthFrom 36 parentYearToHwnd=win32gui.FindWindowEx(accountingPeriodHwnd,parentMonthFromHwnd,"ThunderRT6UserControlDC", None) # YearTo 37 yearToHwnd=win32gui.FindWindowEx(parentYearToHwnd,0,"ThunderRT6TextBox", None) # YearTo 38 granpaSubjectLevelHwnd=win32gui.FindWindowEx(userControlDcHwnd,accountingPeriodHwnd,"ThunderRT6Frame", None) # 科目級別 39 parentSubjectLevelHwnd=win32gui.FindWindowEx(granpaSubjectLevelHwnd,0,"ThunderRT6UserControlDC", None) # 科目級別 40 subjectLevelHwnd=win32gui.FindWindowEx(parentSubjectLevelHwnd,0,"ThunderRT6TextBox", None) # 科目級別 41 print("已找到【科目餘額表】過濾條件下各個控制元件元素") 42 '''點選 高階,展開更多checkbox項''' 43 time.sleep(0.2) 44 win32gui.SendMessage(AdvancedBtn,win32con.BM_CLICK,0,0) 45 time.sleep(0.1) 46 conditionHwndDic={condition1Hwnd:conditionFlag1,condition2Hwnd:conditionFlag2,condition3Hwnd:conditionFlag3,condition4Hwnd:conditionFlag4, 47 condition5Hwnd:conditionFlag5,condition6Hwnd:conditionFlag6,condition7Hwnd:conditionFlag7,condition8Hwnd:conditionFlag8} # 字典,key是conditionHwnd,value則是對應的checkbox狀態,為布林值 48 time.sleep(0.2) 49 50 '''根據checkbox配置,設定K3對應各個checkbox值''' 51 for conditionHwnd in conditionHwndDic: 52 conditionFlag=conditionHwndDic[conditionHwnd] 53 currentCheckFlag=win32gui.SendMessage(conditionHwnd, win32con.BM_GETCHECK) # 顯示K3系統當前特定checkbox的布林值 54 while currentCheckFlag!=conditionFlag: 55 time.sleep(0.2) 56 win32gui.PostMessage(conditionHwnd, win32con.BM_SETCHECK, conditionFlag,0) 57 time.sleep(0.1) 58 currentCheckFlag=win32gui.SendMessage(conditionHwnd, win32con.BM_GETCHECK) # 顯示K3系統當前特定checkbox的布林值 59 60 win32gui.SendMessage(subjectLevelHwnd, win32con.WM_SETTEXT, None,subjectLevel) # 設定科目級別 61 time.sleep(0.3) 62 win32api.SendMessage(yearFromHwnd, win32con.WM_SETTEXT, None,subjectYearFrom) 63 time.sleep(0.2) 64 win32api.SendMessage(monthFromHwnd, win32con.WM_SETTEXT, None,subjectMonthFrom) 65 time.sleep(0.2) 66 win32api.SendMessage(yearToHwnd, win32con.WM_SETTEXT, None,subjectYearTo) 67 time.sleep(0.2) 68 win32api.SendMessage(monthToHwnd, win32con.WM_SETTEXT, None,subjectMonthTo) 69 time.sleep(0.2) 70 71 '''給過濾條件視窗傳送回車,代表確定''' 72 time.sleep(1) 73 okBtnHwnd=win32gui.FindWindowEx(filterConditionHwnd,0,"ThunderRT6CommandButton","確定") 74 win32gui.PostMessage(okBtnHwnd,win32con.BM_CLICK,0,0)
有了這些,距離我們玩轉金蝶K3的自動化就又前進了一大步。
歡迎掃碼關注我的公眾號 獲取更多爬蟲、資料分析的知識!