This is the initial start page for the WebDriver server

本文提供了解决IE浏览器中保护模式和界面缩放问题的方法,只需取消保护模式设置,并将界面缩放调整至100%即可优化浏览体验。

解决方法->修改IE设置

  1. 将所有区域的保护模式勾选去掉即可
  2. 界面缩放设置为100% 

 

 

 

from appium import webdriver from appium.options.android import UiAutomator2Options from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from selenium.common.exceptions import TimeoutException, NoSuchElementException, ElementNotInteractableException import time # ==================== 目标应用配置 ==================== TARGET_PACKAGE = "com.brother.ptouch.iprintandlabel" TARGET_ACTIVITY = ".module.home.home.HomeActivity" # 主页 Activity APK_XPATH="C:/own/download/iPL5.3.10_signed.apk" MAX_RESTORE_ATTEMPTS = 2 MAX_HISTORY_REPEAT = 3 # ==================== 配置 Appium Options ==================== def create_driver(): options = UiAutomator2Options() options.set_capability("platformName", "Android") options.set_capability("deviceName", "emulator-5554") options.set_capability("automationName", "UiAutomator2") options.set_capability("noReset", False) options.set_capability("fullReset", False) options.set_capability("autoGrantPermissions", True) options.set_capability("newCommandTimeout", 600) options.set_capability("appPackage", TARGET_PACKAGE) options.set_capability("appActivity", TARGET_ACTIVITY) options.set_capability("app",APK_XPATH) driver = webdriver.Remote('http://localhost:4723', options=options) return driver class AndroidAppExplorer: def __init__(self, driver): self.driver = driver self.wait = WebDriverWait(driver, 10) self.clicked_identifiers = set() self.recovery_history = [] self.restore_attempt_count = 0 def get_current_state(self): try: current_package = self.driver.current_package current_activity = self.driver.current_activity return current_package, current_activity except: return None, None def is_in_target_app(self): pkg, _ = self.get_current_state() return pkg == TARGET_PACKAGE def wait_for_page_load(self, timeout=10): """等待页面加载完成""" try: WebDriverWait(self.driver, timeout).until( EC.presence_of_element_located(( 'xpath', '//android.view.ViewGroup[@displayed="true"] | ' '//android.widget.FrameLayout[@displayed="true"]' )) ) time.sleep(1) except Exception as e: print(f"⚠️ 页面等待超时或出错: {e}") def ensure_in_target_app(self): current_pkg, current_act = self.get_current_state() if not self.is_in_target_app(): print(f"⚠️ 当前不在目标应用 ({current_pkg}/{current_act}),正在尝试恢复...") current_key = (current_pkg, current_act) recent_entries = self.recovery_history[-10:] if recent_entries.count(current_key) >= MAX_HISTORY_REPEAT: print(f"🛑 已多次回到 {current_key},疑似陷入循环,停止恢复。") return False self.recovery_history.append(current_key) # 方法1:back 返回 if self.restore_attempt_count < MAX_RESTORE_ATTEMPTS: try: self.driver.back() time.sleep(1.5) if self.is_in_target_app(): print("✅ 通过 back 成功返回目标 App") self.restore_attempt_count = 0 self.wait_for_page_load(10) return True except: pass # 方法2:activate_app if self.restore_attempt_count < MAX_RESTORE_ATTEMPTS: try: self.driver.activate_app(TARGET_PACKAGE) print("🔁 正在通过 activate_app 恢复...") time.sleep(2) self.wait_for_page_load(10) if self.is_in_target_app(): print("✅ 成功激活目标 App") self.restore_attempt_count = 0 return True except Exception as e: print(f"❌ activate_app 失败: {e}") # 方法3:冷重启 if self.restore_attempt_count >= MAX_RESTORE_ATTEMPTS: try: print("💀 执行冷重启...") self.driver.terminate_app(TARGET_PACKAGE) time.sleep(2) self.driver.activate_app(TARGET_PACKAGE) time.sleep(5) self.wait_for_page_load(10) if self.is_in_target_app(): print("✅ 冷重启成功") self.restore_attempt_count = 0 return True except Exception as e: print(f"❌ 冷重启失败: {e}") self.restore_attempt_count += 1 if self.restore_attempt_count > 5: print("🛑 恢复尝试过多,终止运行") return False return True else: self.recovery_history.append((current_pkg, current_act)) self.wait_for_page_load(1) return True def get_element_identifier(self, element): try: attrs = [ element.get_attribute("resource-id") or "", element.get_attribute("text") or "", element.get_attribute("content-desc") or "", element.tag_name or "" ] return "/".join([a for a in attrs if a.strip()]) except: return "unknown_element" def find_clickable_elements(self): xpath = """ //*[(@clickable='true' or @focusable='true') and @displayed='true' and @enabled='true'] """ try: elements = self.driver.find_elements('xpath', xpath) return [e for e in elements if e.is_displayed()] except Exception as e: print(f"❌ 查找元素失败: {e}") return [] def classify_elements(self, elements): bottom_nav_container = "com.brother.ptouch.iprintandlabel:id/bottomNavigationView" nav_ids = [ "com.brother.ptouch.iprintandlabel:id/navigation_my_label", "com.brother.ptouch.iprintandlabel:id/navigation_setting", "com.brother.ptouch.iprintandlabel:id/navigation_buy" ] nav_elements = [] non_nav_elements = [] for elem in elements: try: res_id = elem.get_attribute("resource-id") or "" content_desc = elem.get_attribute("content-desc") or "" text = elem.get_attribute("text") or "" if (res_id == bottom_nav_container or res_id in nav_ids or content_desc in ["My Labels", "Settings", "Shop"]): nav_elements.append(elem) else: non_nav_elements.append(elem) except Exception as e: print(f"⚠️ 分类元素时异常: {e}") continue return nav_elements, non_nav_elements def handle_alert_popup(self): alert_xpath = "//*[contains(@text, 'OK') or contains(@text, '允许') or contains(@text, 'Allow')]" try: button = self.driver.find_element('xpath', alert_xpath) if button.is_displayed(): button.click() print("✅ 自动处理了弹窗") time.sleep(1) except: pass def is_interactive_input(self, element): """判断是否为可视且可交互的输入框""" try: if not element.is_displayed(): return False focusable = element.get_attribute("focusable") == "true" enabled = element.get_attribute("enabled") == "true" clazz = element.get_attribute("className") or "" is_edit_type = any(kw in clazz for kw in ["EditText", "TextInput"]) return focusable and enabled and is_edit_type except: return False def handle_input_field(self, input_elem): """处理输入框:显示提示、当前值,并让用户手动输入后读取结果""" hint = input_elem.get_attribute("hint") or "null" current_text = input_elem.get_attribute("text") or "" print(f"\n🔍 发现输入框") if hint != "null": print(f" 提示: '{hint}'") if current_text: print(f" 当前文本: '{current_text}'") print("请在设备上手动输入内容(3秒内完成)...") time.sleep(3) try: entered_value = input_elem.get_attribute("text") or "" # 使用 text 获取输入内容 except: entered_value = "" print(f"📝 用户在输入框中输入的内容为: '{entered_value}'") def click_element_safely(self, element): try: identifier = self.get_element_identifier(element) if identifier in self.clicked_identifiers: return False try: self.driver.execute_script("arguments[0].scrollIntoView(true);", element) except: pass time.sleep(0.5) element.click() print(f"🟢 点击成功: {identifier}") self.clicked_identifiers.add(identifier) time.sleep(1.5) return True except Exception as e: print(f"🔴 点击失败 {self.get_element_identifier(element)}: {str(e)}") return False def is_fully_active(self, button): """判断按钮是否处于完全激活状态""" try: alpha = button.get_attribute("alpha") if alpha and float(alpha) < 0.8: return False except: pass try: class_name = button.get_attribute("class") if "disabled" in class_name.lower(): return False except: pass return True def handle_user_agreement(self): print("🔍 检测是否需要接受用户协议...") try: initial_activity = self.driver.current_activity except: initial_activity = None try: # 等待协议页面出现 WebDriverWait(self.driver, 10).until( EC.presence_of_element_located(( 'xpath', '//*[contains(@text, "License") or contains(@text, "Agreement") or contains(@text, "user")]' )) ) print("📄 检测到【用户协议】页面") def find_agree_button(): candidates = [ '//*[@text="I agree"]', '//*[@text="Agree"]', '//*[contains(@resource-id, "yes")]', '//*[contains(@resource-id, "agree")]', ] for xpath in candidates: try: btn = self.driver.find_element(By.XPATH, xpath) if btn.is_displayed() and btn.is_enabled() and self.is_fully_active(btn): return btn except: continue return None # 先尝试直接点击 agree_btn = find_agree_button() if agree_btn: print("✅ 发现可点击的【I agree】按钮") current_act_before = self.driver.current_activity agree_btn.click() time.sleep(3) if self.driver.current_activity != current_act_before: print("✅ 已跳转至新页面,协议处理完成") return True else: print("🟡 点击了【I agree】但未跳转,可能需滚动") # 开始滚动 print("👇 协议未读完,开始滚动...") last_click_failed_id = None for i in range(10): size = self.driver.get_window_size() x = size['width'] // 2 y_start = int(size['height'] * 0.8) y_end = int(size['height'] * 0.3) self.driver.swipe(x, y_start, x, y_end, 600) time.sleep(1) if i<6: continue agree_btn = find_agree_button() if not agree_btn: continue # 防止重复点击同一个无效按钮 if hasattr(agree_btn, 'id') and agree_btn.id == last_click_failed_id: continue print(f"✅ 第 {i + 1} 次滑动后发现可点击的【I agree】按钮") current_act_before = self.driver.current_activity agree_btn.click() print("📌 正在等待页面跳转...") time.sleep(3) try: WebDriverWait(self.driver, 10).until( lambda d: d.current_activity != current_act_before ) print("✅ 成功跳转出协议页面") return True except: print(f"❌ 第 {i + 1} 次滑动后点击【I agree】无效,仍在原页面") last_click_failed_id = agree_btn.id # 记录失败 ID # 兜底:UiAutomator 强制查找 clickable=true 的按钮 try: final_btn = self.driver.find_element( By.ANDROID_UIAUTOMATOR, 'new UiSelector().textContains("agree").enabled(true).clickable(true)' ) final_btn.click() print("🪄 使用 UiAutomator 强制点击【I agree】") time.sleep(3) if self.driver.current_activity != initial_activity: print("✅ 强制点击后成功跳转") return True except Exception as e: print(f"❌ UiAutomator 点击失败: {e}") return False except TimeoutException: print("🟢 未检测到用户协议页面,跳过") return True except Exception as e: print(f"⚠️ 用户协议处理异常: {type(e).__name__}: {e}") return False def handle_printer_selection(self): """ 处理打印机选择页面 基于设备型号关键词自动选择一台打印机并点击 Done """ print("🖨️ 检测是否进入【打印机选择】页面...") device_keywords = ["PT-", "QL-", "TD-", "HL-", "MFC-"] try: # 等待至少一个设备型号文本出现 WebDriverWait(self.driver, 10).until( lambda d: any( any(kw in (elem.get_attribute("text") or "") for kw in device_keywords) for elem in d.find_elements( By.XPATH, '//android.widget.TextView[@text and string-length(@text) > 5]' ) ) ) print("✅ 进入打印机选择页面") # 查找所有 TextView 并匹配设备型号 candidates = self.driver.find_elements( By.XPATH, '//android.widget.TextView[@text and string-length(@text) > 5]' ) selected = False for elem in candidates: text = elem.get_attribute("text") or "" if any(kw in text for kw in device_keywords): try: elem.click() print(f"✅ 选择了打印机: {text}") time.sleep(1.5) selected = True break except Exception as e: print(f"🟡 无法点击 {text}: {str(e)}") selected = True # 可能已选中 break if not selected: print("ℹ️ 未找到新设备可点击(可能已有默认选中)") # 点击 Done 按钮(双条件定位增强兼容性) done_xpath = ( '//android.widget.Button[@text="Done"] | ' '//android.widget.Button[@resource-id="com.brother.ptouch.iprintandlabel:id/wel_download_templates"]' ) done_btn = WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.XPATH, done_xpath)) ) done_btn.click() print("✅ 成功点击【Done】进入主界面") time.sleep(3) return True except TimeoutException: print("🟢 未检测到打印机选择页面,跳过") return True except Exception as e: print(f"⚠️ 打印机选择失败: {type(e).__name__}: {e}") return False def explore_page(self): print("\n🔄 开始探索当前页面...") while True: if not self.ensure_in_target_app(): print("❌ 无法恢复至目标应用,退出。") break self.handle_alert_popup() elements = self.find_clickable_elements() if not elements: print("📭 当前页面无可点击元素。") try: self.driver.back() print("🔙 返回上一页...") time.sleep(2) continue except: break nav_elements, non_nav_elements = self.classify_elements(elements) print(f"📌 导航栏元素数量: {len(nav_elements)}, 非导航栏元素数量: {len(non_nav_elements)}") # === 第一阶段:优先点击导航栏元素 === navigated = False for elem in nav_elements: if self.is_interactive_input(elem): self.handle_input_field(elem) continue if self.click_element_safely(elem): navigated = True break if navigated: continue # === 第二阶段:点击非导航栏元素 === any_clicked = False for elem in non_nav_elements: if self.is_interactive_input(elem): self.handle_input_field(elem) continue if self.click_element_safely(elem): any_clicked = True break if not any_clicked: print("✅ 当前页面所有可点击元素已处理完毕。") try: self.driver.back() print("🔙 返回上一页继续探索...") time.sleep(2) except: print("🔚 无法返回,探索结束。") break time.sleep(1) def run(self): print("🚀 启动 Android 应用探索器...") try: # === 新增:前置初始化流程 === print("\n🔧 正在处理初始化流程...") # 等待应用启动 time.sleep(5) # 处理用户协议 if not self.handle_user_agreement(): print("❌ 用户协议处理失败,但仍继续...") else: print("✅ 用户协议处理完成") # 处理打印机选择 if not self.handle_printer_selection(): print("⚠️ 打印机选择未完成,可能已在主界面") # else: # print("✅ 打印机选择完成,已进入主界面") # === 开始主探索流程 === self.explore_page() except KeyboardInterrupt: print("\n👋 用户中断执行。") except Exception as e: print(f"💥 执行过程中发生错误: {type(e).__name__}: {e}") finally: print("🔚 自动化结束。") # =============== 主程序入口 =============== if __name__ == "__main__": driver = create_driver() explorer = AndroidAppExplorer(driver) try: explorer.run() finally: driver.terminate_app(TARGET_PACKAGE) driver.remove_app(TARGET_PACKAGE) driver.quit() 每次运行点击的内容都不一样,有时会有输入框有时没有(运行5次可能有1次),获取元素不全,有些元素通过Bottom Sheet + ViewPager + GridView 分类懒加载的方式加载,不丢失功能的情况下解决我的问题
最新发布
10-18
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值