小爬在之前的兩篇文章 【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的自動化就又前進了一大步。
歡迎掃碼關注我的公眾號 獲取更多爬蟲、數據分析的知識!