123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567 |
- # This sample code uses the Appium python client v2
- # pip install Appium-Python-Client
- # Then you can paste this into a file and simply run with Python
- import random
- import types
- from typing import Union
- from appium import webdriver
- from selenium.common import TimeoutException
- from selenium.webdriver.support.ui import WebDriverWait
- from appium.webdriver.common.appiumby import AppiumBy
- from datetime import datetime
- import time
- import os
- import sys
- import getopt
- # import winsound
- # For W3C actions
- from selenium.webdriver.common.action_chains import ActionChains
- from selenium.webdriver.common.actions import interaction
- from selenium.webdriver.common.actions.action_builder import ActionBuilder
- from selenium.webdriver.common.actions.pointer_input import PointerInput
- from selenium.webdriver.support import expected_conditions
- isProfession = False
- driver = ''
- # 起始id
- startId = 10001
- # 结束id
- endId = 11500
- # ignores
- ignores = []
- # indicate, higher priority than start-end for no-seq targets
- targets = []
- card_id = "16416117"
- card_pwd = "396254"
- phone = '13974787400'
- phone_pwd = 'Xj654123'
- hide_guide = False
- stop = False
- retry_times_max = 1 # 搜索loading时,间隔几次重新点击搜索
- retry_current = 0 # 暂时没有用途了
- batch_count = 2 # 有几个批次
- subject_default_top = True # 默认理科、物理
- plan_change_years = [2,3] # 计划取几年?从 2 开始
- profession_change_years = [1,2,3,4] # 专业取几年?从 2 开始
- # 登录方式 1 卡号 2 手机密码
- loginType = 1
- # 数据类型 1 招生计划 2 历年录取 3 两者都有
- dataType = 1
- def randomSleep():
- rd = random.choice([0, 0.5, 0.8, 0.75, 1, 2, 1.5, 1.2, 1.8])
- if rd: time.sleep(rd / 4.0)
- def init():
- global driver
- global isProfession
- print('程序启动')
- caps = {}
- caps["platformName"] = "Android"
- caps["appium:platformVersion"] = "6.0"
- caps["appium:deviceName"] = "emulator-5554"
- caps["appium:appPackage"] = "com.eagersoft.youzy.youzy"
- # .mvvm.ui.login.LoginAndRegisterSelectActivity
- # .mvvm.ui.college.FindCollegeActivity
- caps["appium:appActivity"] = ".mvvm.ui.login.LoginAndRegisterSelectActivity"
- caps["appium:resetKeyboard"] = True
- caps["appium:ensureWebviewsHavePages"] = True
- caps["appium:nativeWebScreenshot"] = True
- caps["appium:newCommandTimeout"] = 3600
- caps["appium:connectHardwareKeyboard"] = True
- driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", caps)
- # 不要混合使用隐式和显式等待。这样做会导致不可预测的等待时间。例如,将隐式等待设置为10秒,将显式等待设置为15秒, 可能会导致在20秒后发生超时。
- # driver.implicitly_wait(2)
- def start():
- init()
- login()
- def alarm():
- if sys.platform.startswith('win'):
- pass # winsound.Beep(1000, 3000)
- elif sys.platform.startswith('darwin'):
- os.system("say --voice=\"Mei-Jia\" 出错了,请尽快处理")
- def getForkRange():
- if targets:
- return targets
- else:
- return range(startId, endId + 1)
- def login():
- if loginType == 1:
- login_by_card()
- elif loginType == 2:
- login_by_phone()
- else:
- login_by_card()
- def login_success():
- # 弹出了验证窗口,请在20s内通过验证
- entrance_xpath = "/hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/" \
- "android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/" \
- "android.widget.FrameLayout/android.view.ViewGroup/androidx.viewpager.widget.ViewPager/" \
- "android.view.ViewGroup/android.widget.LinearLayout/android.widget.FrameLayout/" \
- "android.view.ViewGroup[1]/android.view.ViewGroup/android.view.ViewGroup[2]/" \
- "androidx.viewpager.widget.ViewPager/androidx.recyclerview.widget.RecyclerView/" \
- "android.view.ViewGroup[1]"
- modal_id = "com.eagersoft.youzy.youzy:id/click_close_dialog_activity_pop"
- try:
- def load(x):
- modal = find_ele_by_id(modal_id)
- if modal is not None:
- modal.click()
- return True
- else:
- if find_ele_by_xpath(entrance_xpath) is not None:
- return True
- else:
- return False
- return False
- except:
- print('超时了')
- sys.exit()
- alarm()
- WebDriverWait(driver, 120).until(load)
- # 登录成功并且关闭了弹窗
- try:
- # 进入学校列表
- entrance = find_ele_by_xpath(entrance_xpath)
- entrance.click()
- # 开始工作
- global_begin_time = time.time()
- college_range = getForkRange()
- for i in college_range:
- if i in ignores: continue
- current_no = college_range.index(i) + 1
- time_diff = time.time() - global_begin_time
- print('进度统计:', current_no, 'OF', len(college_range), 'IN', time_diff / 3600, 'Hours')
- if not stop:
- auto_click(i)
- else:
- print('结束')
- return
- time_diff = time.time() - global_begin_time
- current_speed1 = time_diff / current_no
- current_speed2 = current_no * 3600 / time_diff
- print('速率统计:', current_speed1, 'Seconds of Each.', current_speed2, 'Completed Per Hour.')
- # auto_click(startId)
- except Exception as e:
- print('错误:', repr(e))
- def login_by_card():
- el1 = find_ele_by_id("com.eagersoft.youzy.youzy:id/click_login_register_card")
- el1.click()
- el3 = find_ele_by_id("com.eagersoft.youzy.youzy:id/cardNumber")
- el3.send_keys(card_id)
- el4 = find_ele_by_id("com.eagersoft.youzy.youzy:id/password")
- el4.send_keys(card_pwd)
- el5 = find_ele_by_id("com.eagersoft.youzy.youzy:id/checkbox")
- el5.click()
- el6 = find_ele_by_id("com.eagersoft.youzy.youzy:id/click_next")
- el6.click()
- login_success()
- def login_by_phone():
- el2 = driver.find_element(by=AppiumBy.ID, value="com.eagersoft.youzy.youzy:id/click_login_register_mobile")
- el2.click()
- time.sleep(1)
- el3 = driver.find_element(by=AppiumBy.ID, value="com.eagersoft.youzy.youzy:id/click_login_by_password")
- el3.click()
- time.sleep(0.5)
- el4 = driver.find_element(by=AppiumBy.ID, value="com.eagersoft.youzy.youzy:id/account")
- el4.send_keys(phone)
- time.sleep(1)
- el5 = driver.find_element(by=AppiumBy.ID, value="com.eagersoft.youzy.youzy:id/password")
- el5.send_keys(phone_pwd)
- time.sleep(1)
- el6 = driver.find_element(by=AppiumBy.ID, value="com.eagersoft.youzy.youzy:id/checkbox")
- el6.click()
- time.sleep(1)
- el7 = driver.find_element(by=AppiumBy.ID, value="com.eagersoft.youzy.youzy:id/click_login_register_mobile")
- el7.click()
- time.sleep(1)
- login_success()
- def change_tab(tabIndex=1) -> bool:
- print('切换tab')
- # 540 740 1213
- # 招生计划
- el_plan = find_ele_by_xpath(
- "/hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/"
- "android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/"
- "android.widget.RelativeLayout/android.view.ViewGroup/android.view.ViewGroup[1]/"
- "android.widget.LinearLayout/android.widget.HorizontalScrollView/"
- "android.widget.LinearLayout/androidx.appcompat.app.ActionBar.Tab/"
- "android.view.ViewGroup/android.widget.TextView[contains(@text,'招生计划')]", 5)
- # print(el_plan)
- print(el_plan)
- change_success = False
- if tabIndex == 1:
- if el_plan is not None:
- el_plan.click()
- change_success = True
- elif tabIndex == 2:
- try:
- # 历年录取
- el_enter = find_ele_by_xpath(
- "/hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/"
- "android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/"
- "android.widget.RelativeLayout/android.view.ViewGroup/android.view.ViewGroup[1]/"
- "android.widget.LinearLayout/android.widget.HorizontalScrollView/"
- "android.widget.LinearLayout/androidx.appcompat.app.ActionBar.Tab/android.view.ViewGroup/"
- "android.widget.TextView[contains(@text,'历年录取')]", 5)
- # print(el_enter)
- if el_enter is not None:
- el_enter.click()
- change_success = True
- except:
- if el_plan is not None:
- el_plan.click()
- # 历年录取
- # time.sleep(1) # tab会横向滑动,稍等会
- el_enter = find_ele_by_xpath(
- "/hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/"
- "android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/"
- "android.widget.RelativeLayout/android.view.ViewGroup/android.view.ViewGroup[1]/"
- "android.widget.LinearLayout/android.widget.HorizontalScrollView/"
- "android.widget.LinearLayout/androidx.appcompat.app.ActionBar.Tab/android.view.ViewGroup/"
- "android.widget.TextView[contains(@text,'历年录取')]")
- # print(el_enter)
- if el_enter is not None:
- el_enter.click()
- change_success = True
- else:
- if el_plan is not None:
- el_plan.click()
- change_success = True
- return change_success
- def change_time(count: int = 2):
- print('切换年份')
- tv_year = find_ele_by_id("com.eagersoft.youzy.youzy:id/tv_year")
- tv_year.click()
- el_year = find_ele_by_xpath(f"/hierarchy/android.widget.FrameLayout/android.widget"
- f".LinearLayout/android.widget.FrameLayout/android.widget"
- f".FrameLayout/android.widget.FrameLayout/android.widget"
- f".FrameLayout/android.view.ViewGroup/android.widget"
- f".FrameLayout/android.view.ViewGroup/android.view."
- f"ViewGroup/android.widget.ScrollView/android.widget"
- f".LinearLayout/android.view.ViewGroup/android.widget"
- f".LinearLayout/androidx.recyclerview.widget"
- f".RecyclerView/android.view.ViewGroup[{count}]/android.widget.TextView")
- el_year.click()
- def open_profession():
- print('切换专业分数线')
- el_switch = find_ele_by_xpath(
- "/hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/"
- "android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/"
- "android.widget.RelativeLayout/android.view.ViewGroup/android.view.ViewGroup[1]/"
- "androidx.viewpager.widget.ViewPager/android.view.ViewGroup/android.widget.ScrollView/"
- "android.view.ViewGroup/android.widget.LinearLayout/android.view.View")
- el_switch_mid_x = el_switch.location['x'] + el_switch.size['width'] / 2
- el_switch_mid_y = el_switch.location['y'] + el_switch.size['height'] / 2
- print(el_switch, el_switch_mid_x, el_switch_mid_y)
- actions = ActionChains(driver)
- actions.w3c_actions = ActionBuilder(driver, mouse=PointerInput(interaction.POINTER_TOUCH, "touch"))
- actions.w3c_actions.pointer_action.move_to_location(el_switch_mid_x, el_switch_mid_y)
- actions.w3c_actions.pointer_action.pointer_down()
- actions.w3c_actions.pointer_action.pause(0.1)
- actions.w3c_actions.pointer_action.release()
- actions.perform()
- def change_batch(count):
- # 3个科目都点击一次
- print('打开批次面板', count)
- for index in range(count):
- # el_dropdown = find_ele_by_id("com.eagersoft.youzy.youzy:id/ll_select_college_batch")
- el_dropdown = WebDriverWait(driver, 5, 0.5).until(expected_conditions.presence_of_element_located(
- (AppiumBy.ID, "com.eagersoft.youzy.youzy:id/ll_select_college_batch")))
- if el_dropdown is not None:
- el_dropdown.click()
- else:
- # 表示元素有,但不可见,需要滚动至可见
- driver.execute_script("arguments[0].scrollIntoView();", el_dropdown)
- el_dropdown.click()
- batch_item_path = f"/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget" \
- f".FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget" \
- f".FrameLayout/android.view.ViewGroup/android.widget.FrameLayout/android.view" \
- f".ViewGroup/android.view.ViewGroup/android.widget.ScrollView/android.widget" \
- f".LinearLayout/android.view.ViewGroup/android.widget.LinearLayout/androidx" \
- f".recyclerview.widget.RecyclerView/android.view.ViewGroup[{index + 1}]"
- try:
- el_batch_item = find_ele_by_xpath(batch_item_path)
- el_batch_item.click()
- except Exception as ex:
- print('切换批次异常,索引', index, '乎略异常', ex)
- el_ddl_close = find_ele_by_id('com.eagersoft.youzy.youzy:id/iv_close')
- el_ddl_close.click()
- randomSleep()
- def cicle():
- change_batch(batch_count)
- # time.sleep(0.5)
- # 切换科目
- subtype_tab = find_ele_by_id("com.eagersoft.youzy.youzy:id/tv_subject_type")
- if subtype_tab is not None:
- subtype_tab.click()
- # time.sleep(0.5)
- subject_index = 1
- if subject_default_top: subject_index = 2
- subject_path = f"/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget" \
- f".FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget" \
- f".FrameLayout/android.view.ViewGroup/android.widget.FrameLayout/android.view.ViewGroup/android" \
- f".view.ViewGroup/android.widget.ScrollView/android.widget.LinearLayout/android.view" \
- f".ViewGroup/android.widget.LinearLayout/androidx.recyclerview.widget.RecyclerView/android" \
- f".view.ViewGroup[{subject_index}]"
- el29 = find_ele_by_xpath(subject_path)
- if el29 is not None:
- el29.click()
- # time.sleep(0.5)
- change_batch(batch_count - 1)
- # time.sleep(0.5)
- def get_data(tab_index, change_years):
- if change_tab(tab_index):
- # time.sleep(1)
- if tab_index == 2:
- open_profession()
- # time.sleep(1)
- if change_years[0] == 1: # 如果第 1 个是默认年份,直接开始批次切换
- cicle()
- for year in change_years:
- # 从第二个年份开始切换
- change_time(year)
- cicle()
- else:
- raise Exception("切换tab失败")
- def is_error() -> bool:
- try:
- return (WebDriverWait(driver, 0.5, 0.25).until(expected_conditions.visibility_of_element_located(
- (AppiumBy.ID, "com.eagersoft.youzy.youzy:id/errorViewRelativeLayout"))) or
- WebDriverWait(driver, 0.5, 0.25).until(
- expected_conditions.visibility_of_element_located(
- (AppiumBy.ID, "com.eagersoft.youzy.youzy:id/load_more_load_fail_view"))))
- except:
- return False
- def retry():
- try:
- retry_button = WebDriverWait(driver, 2, 0.5).until(
- expected_conditions.visibility_of_element_located(
- (AppiumBy.ID, "com.eagersoft.youzy.youzy:id/errorStateButton")))
- if retry_button is not None:
- retry_button.click()
- else:
- retry_button = WebDriverWait(driver, 2, 0.5).until(
- expected_conditions.visibility_of_element_located(
- (AppiumBy.ID, "com.eagersoft.youzy.youzy:id/tv_prompt")))
- if retry_button is not None:
- retry_button.click()
- except Exception as ex:
- print('retry ignored', ex)
- def is_loading() -> bool:
- try:
- return (WebDriverWait(driver, 0.5, 0.25).until(expected_conditions.visibility_of_element_located(
- (AppiumBy.ID, "com.eagersoft.youzy.youzy:id/image_loading"))) or
- WebDriverWait(driver, 0.5, 0.25).until(expected_conditions.visibility_of_element_located(
- (AppiumBy.ID, "com.eagersoft.youzy.youzy:id/load_more_load_end_view"))))
- except:
- return False
- def find_ele_by_id(ele_id: str, timeout: int = 2, poll_frequency: int = 0.5, deep: int = 0):
- loading = False
- error = False
- try:
- def find(x):
- nonlocal loading
- nonlocal error
- if is_error():
- print("error by id", x)
- error = True
- loading = False
- return False
- if is_loading():
- print("loading")
- loading = True
- error = False
- return False
- return x.find_element(by=AppiumBy.ID, value=ele_id) is not None
- WebDriverWait(driver, timeout, poll_frequency).until(find)
- return driver.find_element(by=AppiumBy.ID, value=ele_id)
- except:
- if loading:
- print("新一轮loading", deep)
- if retry_times_max > 0 and deep > 0 and deep % retry_times_max == 0:
- try:
- print('重新触发查询', deep, '%', retry_times_max)
- btn_search = driver.find_element(by=AppiumBy.ID, value="com.eagersoft.youzy.youzy:id/tv_search")
- btn_search.click()
- except Exception as e:
- print('重新触发查询 except', e)
- return find_ele_by_id(ele_id, timeout, poll_frequency, deep + 1)
- elif error:
- print("error,重试")
- retry()
- return find_ele_by_id(ele_id, timeout * 2, poll_frequency, deep + 1)
- else:
- return None
- def find_ele_by_xpath(ele_xpath: str, timeout: int = 2, poll_frequency: int = 0.5):
- loading = False
- error = False
- try:
- def find(x):
- nonlocal loading
- nonlocal error
- if is_error():
- print("error by path", x)
- error = True
- loading = False
- return False
- if is_loading():
- print("loading")
- loading = True
- error = False
- return False
- return x.find_element(by=AppiumBy.XPATH, value=ele_xpath)
- return WebDriverWait(driver, timeout, poll_frequency).until(find)
- except:
- if loading:
- print("新一轮,loading")
- return find_ele_by_xpath(ele_xpath, timeout, poll_frequency)
- elif error:
- print("新一轮,重试")
- retry()
- return find_ele_by_xpath(ele_xpath, timeout, poll_frequency)
- else:
- return None
- def find_college(college_id: Union[str, int]) -> Union[bool, 'WebElement']:
- el23 = find_ele_by_id("com.eagersoft.youzy.youzy:id/et_input")
- el23.send_keys(college_id)
- el24 = find_ele_by_id("com.eagersoft.youzy.youzy:id/tv_search")
- el24.click()
- empty_container = find_ele_by_id("com.eagersoft.youzy.youzy:id/emptyViewRelativeLayout", timeout=2)
- if empty_container is not None:
- return False
- else:
- college = find_ele_by_id("com.eagersoft.youzy.youzy:id/cl_parent")
- if college is not None:
- return college
- return False
- def back():
- el_back = find_ele_by_id("com.eagersoft.youzy.youzy:id/iv_back")
- if el_back is not None:
- print("返回页面")
- el_back.click()
- def auto_click(id):
- global hide_guide
- global stop
- global retry_current
- global dataType
- global loginType
- print('当前id:', id, datetime.now())
- # 开始搜索
- el_search = find_ele_by_id("com.eagersoft.youzy.youzy:id/click_search")
- if el_search is not None:
- el_search.click()
- college = find_college(id)
- if isinstance(college, bool):
- return
- else:
- college.click()
- retry_current = 0
- try:
- if not hide_guide:
- print('等待关闭引导')
- time.sleep(6)
- # actions = ActionChains(driver)
- # actions.w3c_actions = ActionBuilder(driver, mouse=PointerInput(interaction.POINTER_TOUCH, "touch"))
- # actions.w3c_actions.pointer_action.move_to_location(507, 1537)
- # actions.w3c_actions.pointer_action.pointer_down()
- # actions.w3c_actions.pointer_action.pause(0.1)
- # actions.w3c_actions.pointer_action.release()
- # actions.perform()
- hide_guide = True
- # time.sleep(1)
- if dataType == 1:
- get_data(1, plan_change_years)
- elif dataType == 2:
- get_data(2, profession_change_years)
- elif dataType == 3:
- get_data(1, plan_change_years)
- get_data(2, profession_change_years)
- # 回退两次,清除学校列表缓存
- back()
- back()
- except Exception as ex:
- print('get_data exception', ex)
- alarm()
- back()
- back()
- auto_click(id)
- if __name__ == '__main__':
- try:
- args = sys.argv[1:]
- longopts = ['loginType=', 'dataType=']
- shortopts = []
- options, args = getopt.getopt(args, shortopts, longopts)
- for name, value in options:
- print(name, value)
- if name in ('--loginType'):
- loginType = int(value)
- if name in '--dataType':
- dataType = int(value)
- except:
- print('运行参数解析错误')
- if dataType == 1:
- print('招生计划')
- elif dataType == 2:
- print('历年录取')
- else:
- print('招生计划和历年录取')
- start()
|