自动化打开紫鸟店铺
1. 使用工具 Accessibility为自动化测试和工具提供标准化 UI 访问接口微软 UI AutomationUIA正是基于 Accessibility 技术构建的。它暴露了应用程序界面元素的属性、结构和事件下载路径https://accessibilityinsights.io/downloads/2. 使用的库 uiautomation3. 正文干货import pyperclip import time # import pyautogui import uiautomation import win32api import win32con import win32gui from pynput.keyboard import Controller, Key keyboard Controller() # 特殊键名映射表 SPECIAL_KEYS { backspace: Key.backspace, ctrl: Key.ctrl, alt: Key.alt, shift: Key.shift, enter: Key.enter, tab: Key.tab, esc: Key.esc, space: Key.space, delete: Key.delete, up: Key.up, down: Key.down, left: Key.left, right: Key.right, } def press_hotkey(*keys): 模拟按下并释放一组按键。 支持单键如 backspace或组合键如 ctrl, a。 组合键时会按住前面的修饰键然后按下并释放主键最后释放修饰键。 示例 press_hotkey(ctrl, a) # CtrlA press_hotkey(backspace) # 退格键 press_hotkey(ctrl, v) # CtrlV press_hotkey(ctrl, shift, a) # CtrlShiftA多个修饰键 if not keys: return # 分离修饰键Ctrl/Alt/Shift和最后一个主键 modifiers [key for key in keys[:-1] if key in (ctrl, alt, shift)] main_key keys[-1] # 将所有需要按下的键修饰符主键转换为 pynput 可识别的格式 all_keys [] for k in modifiers: all_keys.append(SPECIAL_KEYS.get(k, k)) all_keys.append(SPECIAL_KEYS.get(main_key, main_key)) # 按下所有修饰键顺序不重要按住即可 for k in all_keys[:-1]: keyboard.press(k) # 按下并释放主键 keyboard.press(all_keys[-1]) keyboard.release(all_keys[-1]) # 释放所有修饰键逆序释放更规范 for k in reversed(all_keys[:-1]): keyboard.release(k) def find_windows_by_class_and_title(class_nameNone, title_keywordNone): # 根据窗口类名和标题查找窗口句柄 hwnd_list [] def enum_window_callback(hwnd, _): current_class win32gui.GetClassName(hwnd) title win32gui.GetWindowText(hwnd) # print(f窗口类名{current_class}, 标题{title}) if class_name: if current_class class_name and title_keyword in title: hwnd_list.append(hwnd) else: if title_keyword in title: hwnd_list.append(hwnd) return True win32gui.EnumWindows(enum_window_callback, 0) return hwnd_list def click_location(control, y_add_num0, x_add_num0, textNone): 点击某处或者输入文本 Rect: (775,501,1113,535) left, top, right, bottom :param control: :param y_add_num: 对y坐标进行调节 :param x_add_num: 对x坐标进行调节 :param text: rect control.BoundingRectangle x (rect.left rect.right) // 2 y ((rect.top rect.bottom) // 2) win32api.SetCursorPos((x x_add_num, y y_add_num)) # 按下左键 win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0) # 松开左键 win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0) time.sleep(1) if text: # pyautogui.hotkey(ctrl, a) # pyautogui.press(backspace) # pyperclip.copy(text) # pyautogui.hotkey(ctrl, v) press_hotkey(ctrl, a) press_hotkey(backspace) pyperclip.copy(text) press_hotkey(ctrl, v) def get_window_all_component(control): buttons [] for child in control.GetChildren(): # if child.ControlTypeName DataItemControl: # buttons.append(child) if child.Exists(): buttons.append(child) # 递归查找 buttons.extend(get_window_all_component(child)) return buttons def close_tip(): # 关闭提示窗口 time.sleep(2) doc_windows find_windows_by_class_and_title(Chrome_WidgetWin_1, 提示) if len(doc_windows) 0: print(f未成功获取窗口控制对象) return window4 uiautomation.ControlFromHandle(doc_windows[0]) # print(f成功获取窗口控制对象4: {window4}) enter_btn window4.ButtonControl(Name确定) enter_btn.Click() # 1. 控制紫鸟浏览器窗口 def setup_1(): doc_windows find_windows_by_class_and_title(Chrome_WidgetWin_1, 紫鸟浏览器专业版) window1 uiautomation.ControlFromHandle(doc_windows[0]) if window1.Exists(): print(f成功获取窗口控制对象: {window1.Name}) return window1 else: return None # 4. 获取店铺列表 def get_platform_lists(window1): platform_lists [] while True: all_buttons get_window_all_component(window1) for li in all_buttons: if not li or not hasattr(li, ControlTypeName): continue if not hasattr(li.GetParentControl(), ControlTypeName): continue # if li.ControlTypeName CustomControl and li.GetParentControl().ControlTypeName TableControl: if li.ControlTypeName ! CustomControl and li.GetParentControl().ControlTypeName ! TableControl: continue window_list get_window_all_component(li) # print(window_list:, len(window_list)) dic {} for control in window_list: text control.Name if 电商后台 in text and str(text).strip() ! 电商后台: # print(text:, text) shop_name text.replace(电商后台, ).strip() # print(shop_name:, shop_name) dic[shop_name] shop_name if 所属平台 in text and str(text).strip() ! 所属平台: # print(text1:, text) platform_name text.split(登录凭证)[0].replace(所属平台, ).replace(:, ).strip() # print(platform_name:, platform_name) dic[platform_name] platform_name if 启动 in text and control.ControlTypeName ButtonControl: rect control.BoundingRectangle dic[rect] (rect.xcenter(), rect.ycenter()) if dic and dic.get(shop_name) and dic not in platform_lists: platform_lists.append(dic) # 点击下一页 right_button window1.ButtonControl(Nameright) if not right_button.Exists(0.5, 0.5) or not right_button.IsEnabled: break else: right_button.Click() time.sleep(2) # print(platform_lists:, platform_lists) # print(platform_lists:, len(platform_lists)) return platform_lists def loop_search_shop(window1, shop_name): # 点击搜索店铺 请输入关键词 批量搜索用或空格隔开 # 点击账号管理 account_manage window1.HyperlinkControl(RegexName账号管理) # 账号管理点击失败则通过坐标进行点击 click_location(account_manage) time.sleep(1.5) # 输入框 search_editcontrol window1.EditControl(RegexName请输入关键词) # 搜索按钮 # search_btn_control get_search_btn(window1, search_editcontrol) search_btn_control window1.ButtonControl(Namesearch) search_editcontrol.SendKeys({Ctrl}a, waitTime1) search_editcontrol.SendKeys({del}, waitTime1) search_editcontrol.SendKeys(shop_name) time.sleep(0.5) search_btn_control.Click() # 6. 启动店铺 def start_button(window1, shop_name): print(fwindow1:{window1} shop_name:{shop_name}) lists get_platform_lists(window1) # print(lists:, lists) flag False for li in lists: if shop_name not in li.get(shop_name): continue if not li.get(rect): continue x, y li[rect] win32api.SetCursorPos((x, y)) # 按下左键 win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0) # 松开左键 win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0) flag True if flag: return True else: return False # start_shop_control window1.ButtonControl(Name启动) # if start_shop_control.Exists(): # start_shop_control.Click() # return True # else: # return False # 7. 打开账号 def get_browser_object(shop_name): # 获取紫鸟浏览器对象 # shop_name Shunwei Health doc_windows find_windows_by_class_and_title(Chrome_WidgetWin_1, shop_name) if len(doc_windows) 0: return False window2 uiautomation.ControlFromHandle(doc_windows[0]) if window2.Exists(): print(f成功获取窗口控制对象2: {window2}) # window2 uiautomation.PaneControl(RegexName紫鸟浏览器专业版Superbrower, ClassNameChrome_WidgetWin_1) return window2 def open_shop(shop_name, times_num180): # 回到打开账号页面 for i in range(times_num): time.sleep(1) window2 get_browser_object(shop_name) if not window2: continue tabItemControl window2.TabItemControl(RegexName紫鸟浏览器专业版Superbrower) if tabItemControl.Exists(): tabItemControl.Click() time.sleep(2) openshop window2.ButtonControl(Name打开账号) if openshop.Exists(): openshop.Click() return window2 return False def close_browser(shop_name): :param shop_name: 传入需要关闭的店铺名 :return: # 关闭紫鸟浏览器 doc_windows find_windows_by_class_and_title(Chrome_WidgetWin_1, shop_name) if not doc_windows: return window2 uiautomation.ControlFromHandle(doc_windows[0]) if window2.Exists(): print(f成功获取窗口控制对象2: {window2}) # window2 uiautomation.PaneControl(RegexName紫鸟浏览器专业版Superbrower, ClassNameChrome_WidgetWin_1) # 关闭 win32gui.PostMessage(window2.NativeWindowHandle, win32con.WM_CLOSE, 0, 0) # restore_window(window_titleNone, hwndwindow2.NativeWindowHandle, statusclose, locationNone) time.sleep(1.5) # 关闭弹窗提示 close_tip() # 1. 获取紫鸟浏览器控制台窗口 window1 setup_1() # 2. 搜索店铺 shop_name xxxx loop_search_shop(window1, shop_name) # 3. 点击启动 start_button(window1, shop_name) # 4. 打开账号 open_shop(shop_name)