def _watch(self, video_count=None): g1, t1 = self.app.score["视听学习"] g2, t2 = self.app.score["视听学习时长"] if (g1 == t1) and (g2 == t2): logger.debug(f'视听学习时长积分已达成,无需重复收听') return logger.info("开始浏览百灵视频...") self.app.safe_click(rules['bailing_enter']) self.app.safe_click(rules['bailing_enter']) # 再点一次刷新短视频列表 self.app.safe_click(rules['video_first']) logger.info(f'预计观看视频 {video_count} 则') while video_count: video_count -= 1 video_delay = random.randint( self.view_delay, self.view_delay + min(10, self.video_count)) logger.info(f'正在观看视频 <{video_count}#> {video_delay} 秒进入下一则...') time.sleep(video_delay) self.app.swipe_up() else: logger.info(f'视听学习完毕,正在返回...') self.app.safe_back('video -> bailing') logger.debug(f'正在返回首页...') self.app.safe_click(rules['home_entry'])
def _search(self, content, options, exclude=''): # 职责 网上搜索 logger.debug(f'搜索 {content} <exclude = {exclude}>') logger.info(f"选项 {options}") content = re.sub(r'[\((]出题单位.*', "", content) if options[-1].startswith("以上") and chr(len(options)+64) not in exclude: logger.info(f'根据经验: {chr(len(options)+64)} 很可能是正确答案') return chr(len(options)+64) # url = quote('https://www.baidu.com/s?wd=' + content, safe=string.printable) url = quote("https://www.sogou.com/web?query=" + content, safe=string.printable) response = requests.get(url, headers=self.headers).text counts = [] for i, option in zip(['A', 'B', 'C', 'D', 'E', 'F'], options): count = response.count(option) counts.append((count, i)) logger.info(f'{i}. {option}: {count} 次') counts = sorted(counts, key=lambda x:x[0], reverse=True) counts = [x for x in counts if x[1] not in exclude] c, i = counts[0] if 0 == c: # 替换了百度引擎为搜狗引擎,结果全为零的机会应该会大幅降低 _, i = random.choice(counts) logger.info(f'搜索结果全0,随机一个 {i}') logger.info(f'根据网络搜索结果: {i} 很可能是正确答案') return i
def back_or_not(self, title): # return False g, t = self.score[title] if g == t: logger.debug(f'{title} 积分已达成,无需重复获取积分') return True return False
def swipe_down(self): # 向下滑动屏幕 self.driver.swipe(self.size['width'] * random.uniform(0.55, 0.65), self.size['height'] * random.uniform(0.25, 0.35), self.size['width'] * random.uniform(0.55, 0.65), self.size['height'] * random.uniform(0.65, 0.75), random.uniform(800, 1200)) logger.debug('向下滑动屏幕')
def swipe_left(self): # 向右滑动屏幕 self.driver.swipe(self.size['width'] * random.uniform(0.89, 0.98), self.size['height'] * random.uniform(0.75, 0.89), self.size['width'] * random.uniform(0.01, 0.11), self.size['height'] * random.uniform(0.75, 0.89), random.uniform(800, 1200)) logger.debug('向左滑动屏幕')
def _special(self): self.app.safe_click(rules["special_entry"]) self.app.safe_click(rules["special_current"]) time.sleep(5) for i in range(10): logger.debug(f'专项答题 第 {i+1} 题') try: category = self.app.driver.find_element_by_xpath( rules["special_category"]).get_attribute("name") print(category) except NoSuchElementException as e: logger.error(f'无法获取题目类型') raise e if "填空题 (10分)" == category: self.daily._blank() elif "单选题 (10分)" == category: self.daily._radio() elif "多选题 (10分)" == category: self.daily._check() else: logger.error(f"未知的题目类型: {category}") raise ('未知的题目类型') logger.debug(f'专项答题循环结束') self.app.safe_back('speical report -> special list') self.app.safe_back('special list -> quiz')
def _weekly(self): self.app.safe_click(rules["weekly_entry"]) titles = self.app.wait.until( EC.presence_of_all_elements_located( (By.XPATH, rules["weekly_titles"]))) states = self.app.wait.until( EC.presence_of_all_elements_located( (By.XPATH, rules["weekly_states"]))) # first, last = None, None for title, state in zip(titles, states): # if not first and title.location_in_view["y"]>0: # first = title if self.app.size["height"] - title.location_in_view["y"] < 10: logger.debug(f'屏幕内没有未作答试卷') break logger.debug( f'{title.get_attribute("name")} {state.get_attribute("name")}') if "未作答" == state.get_attribute("name"): logger.info(f'{title.get_attribute("name")}, 开始!') state.click() time.sleep(random.randint(5, 9)) self.daily._dispatch(5) # 这里直接采用每日答题 break self.app.safe_back('weekly report -> weekly list') self.app.safe_back('weekly list -> quiz')
def _share_once(self): if self.app.back_or_not("分享"): return logger.debug(f'好东西必须和好基友分享,走起,转起!') self.app.safe_click(rules['article_share']) self.app.safe_click(rules['article_share_xuexi']) time.sleep(3) self.app.safe_back('share -> article')
def find_elements(self, ele:str): logger.debug(f'find elements by xpath: {ele}') try: elements = self.driver.find_elements_by_xpath(ele) except NoSuchElementException as e: logger.error(f'找不到元素: {ele}') raise e return elements
def _music(self): logger.debug(f'正在打开《{self.radio_chanel}》...') self.app.safe_click( '//*[@resource-id="cn.xuexi.android:id/home_bottom_tab_button_mine"]' ) self.app.safe_click('//*[@text="听新闻广播"]') self.app.safe_click(f'//*[@text="{self.radio_chanel}"]') self.app.safe_click(rules['home_entry'])
def logout_or_not(self): if cfg.getboolean("prefers", "keep_alive"): logger.debug("无需自动注销账号") return self.safe_click(rules["mine_entry"]) self.safe_click(rules["setting_submit"]) self.safe_click(rules["logout_submit"]) self.safe_click(rules["logout_confirm"]) logger.info("已注销")
def __init__(self, app): self.app = app try: self.challenge_count = cfg.getint('prefers', 'challenge_count') except: self.challenge_count = random.randint( cfg.getint('prefers', 'challenge_count_min'), cfg.getint('prefers', 'challenge_count_max')) self.challenge_delay_bot = cfg.getint('prefers', 'challenge_delay_min') self.challenge_delay_top = cfg.getint('prefers', 'challenge_delay_max') logger.debug(f'挑战答题: {self.challenge_count}')
def __init__(self, app): self.app = app self.read_time = 361 self.volumn_title = cfg.get("prefers", "article_volumn_title") self.star_share_comments_count = cfg.getint( "prefers", "star_share_comments_count") self.titles = list() try: self.read_count = cfg.getint("prefers", "article_count") self.read_delay = 30 except: self.read_count = random.randint( cfg.getint('prefers', 'article_count_min'), cfg.getint('prefers', 'article_count_max')) self.read_delay = self.read_time // self.read_count + 1 logger.debug(f'阅读文章: {self.read_count}')
def _comments_once(self, title="好好学习,天天强国"): # return # 拒绝留言 if self.app.back_or_not("发表观点"): return logger.debug(f'哇塞,这么精彩的文章必须留个言再走!') self.app.safe_click(rules['article_comments']) edit_area = self.app.wait.until( EC.presence_of_element_located( (By.XPATH, rules['article_comments_edit']))) # edit_area = self.find_element(rules['article_comments_edit']) edit_area.send_keys(title) self.app.safe_click(rules['article_comments_publish']) time.sleep(2) self.app.safe_click(rules['article_comments_list']) self.app.safe_click(rules['article_comments_delete']) self.app.safe_click(rules['article_comments_delete_confirm'])
def weekly(self): ''' 每周答题 复用每日答题的方法,无法保证每次得满分,如不能接受,请将配置workdays设为0 ''' day_of_week = datetime.now().isoweekday() if str(day_of_week) not in self.workdays: logger.debug(f'今日不宜每周答题 {day_of_week} / {self.workdays}') return if self.app.back_or_not("每周答题"): return self.app.safe_click(rules['mine_entry']) self.app.safe_click(rules['quiz_entry']) time.sleep(3) self._weekly() self.app.safe_back('quiz -> mine') self.app.safe_back('mine -> home')
def _star_share_comments(self, title): logger.debug(f'哟哟,切克闹,收藏转发来一套') if random.random() < 0.33: self._comments_once(title) if random.random() < 0.5: self._star_once() self._share_once() else: self._share_once() self._star_once() else: if random.random() < 0.5: self._star_once() self._share_once() else: self._share_once() self._star_once() self._comments_once(title)
def __init__(self, app): self.app = app self.g, self.t = 0, 6 self.count_of_each_group = cfg.getint('prefers', 'daily_count_each_group') try: self.daily_count = cfg.getint('prefers', 'daily_count') self.daily_force = self.daily_count > 0 except: self.g, self.t = self.app.score["每日答题"] self.daily_count = self.t - self.g self.daily_force = False self.daily_delay_bot = cfg.getint('prefers', 'daily_delay_min') self.daily_delay_top = cfg.getint('prefers', 'daily_delay_max') self.delay_group_bot = cfg.getint('prefers', 'daily_group_delay_min') self.delay_group_top = cfg.getint('prefers', 'daily_group_delay_max') logger.debug(f"每日答题: {self.daily_count}")
def _dispatch(self, count_of_each_group): time.sleep(3) # 如果模拟器比较流畅,这里的延时可以适当调短 for i in range(count_of_each_group): logger.debug(f'正在答题 第 {i+1} / {count_of_each_group} 题') try: category = self.app.driver.find_element_by_xpath( rules["daily_category"]).get_attribute("name") except NoSuchElementException as e: logger.error(f'无法获取题目类型') raise e print(category) if "填空题" == category: self._blank() elif "单选题" == category: self._radio() elif "多选题" == category: self._check() else: logger.error(f"未知的题目类型: {category}")
def view_score(self): self.safe_click(rules['score_entry']) titles = ["登录", "我要选读文章", "视听学习", "视听学习时长", "每日答题", "每周答题", "专项答题", "挑战答题", "订阅", "分享", "发表观点", "本地频道"] try: score_list = self.wait.until(EC.presence_of_all_elements_located((By.XPATH, rules['score_list']))) # score_list = self.find_elements(rules["score_list"]) except: #ttt = self.wait.until(EC.presence_of_all_elements_located((By.XPATH, 'android:id/button2'))) self.driver.keyevent(4) score_list = self.wait.until(EC.presence_of_all_elements_located((By.XPATH, rules['score_list']))) for t, score in zip(titles, score_list): s = score.get_attribute("name") self.score[t] = tuple([int(x) for x in re.findall(r'\d+', s)]) print(self.score) for i in self.score: logger.debug(f'{i}, {self.score[i]}') self.safe_back('score -> home')
def _view_tips(self): content = "" try: tips_open = self.app.driver.find_element_by_xpath( rules["daily_tips_open"]) tips_open.click() except NoSuchElementException as e: logger.debug("没有可点击的【查看提示】按钮") return "" time.sleep(2) try: tips = self.app.wait.until( EC.presence_of_element_located( (By.XPATH, rules["daily_tips"]))) content = tips.get_attribute("name") logger.debug(f'提示 {content}') except NoSuchElementException as e: logger.error(f'无法查看提示内容') return "" time.sleep(2) try: tips_close = self.app.driver.find_element_by_xpath( rules["daily_tips_close"]) tips_close.click() except NoSuchElementException as e: logger.debug("没有可点击的【X】按钮") time.sleep(2) return content
def read(self): g, t = self.app.score["我要选读文章"] if t == g: logger.info(f'新闻阅读已达成,无需重复阅读') return logger.debug(f'正在进行新闻学习...') self._kaleidoscope() vol_not_found = True while vol_not_found: volumns = self.app.wait.until( EC.presence_of_all_elements_located( (By.XPATH, rules['article_volumn']))) # volumns = self.find_elements(rules['article_volumn']) first_vol = volumns[1] for vol in volumns: title = vol.get_attribute("name") logger.debug(title) if self.volumn_title == title: vol.click() vol_not_found = False break else: logger.debug(f'未找到 {self.volumn_title},左滑一屏') self.app.driver.scroll(vol, first_vol, duration=500) self._read(self.read_count, self.star_share_comments_count)
def start(): try: if random.random() > 0.5: logger.debug(f'视听学习优先') app.watch() app.music() shuffle([app.read, app.daily, app.challenge, app.weekly]) else: logger.debug(f'视听学习置后') app.music() shuffle([app.read, app.daily, app.challenge, app.weekly]) app.watch() app.logout_or_not() except: print("发生异常") return 1 else: return 0 # sys.exit(0)
def __init__(self, app): self.app = app self.has_bgm = cfg.get("prefers", "radio_switch") if "disable" == self.has_bgm: self.view_time = 1080 else: self.view_time = 360 self.radio_chanel = cfg.get("prefers", "radio_chanel") try: self.video_count = cfg.getint("prefers", "video_count") self.view_delay = 15 except: g, t = self.app.score["视听学习"] if t == g: self.video_count = 0 self.view_delay = random.randint(15, 30) else: self.video_count = random.randint( cfg.getint('prefers', 'video_count_min'), cfg.getint('prefers', 'video_count_max')) self.view_delay = self.view_time // self.video_count + 1 logger.debug(f'视听学习: {self.video_count}')
def _check(self): content = self.app.wait.until( EC.presence_of_element_located( (By.XPATH, rules["daily_content"]))).get_attribute("name") # content = self.find_element(rules["daily_content"]).get_attribute("name") option_elements = self.app.wait.until( EC.presence_of_all_elements_located( (By.XPATH, rules["daily_options"]))) # option_elements = self.driver.find_elements(rules["daily_options"]) options = [x.get_attribute("name") for x in option_elements] length_of_options = len(options) logger.info(f"多选题 {content}\n{options}") answer = self._verify("多选题", content, options) logger.debug(f'提交答案 {answer}') for k, option in zip(list("ABCDEFG"), option_elements): if k in answer: option.click() time.sleep(1) else: continue # 提交答案 self._submit() try: wrong_or_not = self.app.driver.find_element_by_xpath( rules["daily_wrong_or_not"]) right_answer = self.app.driver.find_element_by_xpath( rules["daily_answer"]).get_attribute("name") right_answer = re.sub(r'正确答案: ', '', right_answer) logger.info(f"答案 {right_answer}") # notes = self.driver.find_element_by_xpath(rules["daily_notes"]).get_attribute("name") # logger.debug(f"解析 {notes}") self._submit(2) localmodel.update_bank("多选题", content, options, right_answer, "", "") except: localmodel.update_bank("多选题", content, options, answer, "", "")
def music(self): if "disable" == self.has_bgm: logger.debug(f'广播开关 关闭') elif "enable" == self.has_bgm: logger.info(f'广播开关 开启') self._music() else: logger.debug(f'广播开关 默认') g, t = self.score["视听学习时长"] if g == t: logger.debug(f'视听学习时长积分已达成,无需重复收听') return else: self._music()
def _daily(self, num): self.app.safe_click(rules["daily_entry"]) while num: num -= 1 logger.info(f'每日答题 第 {num}# 组') self._dispatch(self.count_of_each_group) if not self.daily_force: score = self.app.wait.until( EC.presence_of_element_located( (By.XPATH, rules["daily_score"]))).get_attribute("name") # score = self.find_element(rules["daily_score"]).get_attribute("name") try: score = int(score) except: raise TypeError('integer required') self.g += score if self.g == self.t: logger.info(f"今日答题已完成,返回") break if num == 0: logger.debug(f'今日循环结束 <{self.g} / {self.t}>') break delay = random.randint(self.delay_group_bot, self.delay_group_top) logger.info(f'每日答题未完成 <{self.g} / {self.t}> {delay} 秒后再来一组') time.sleep(delay) self.app.safe_click(rules['daily_again']) continue else: logger.debug("应该不会执行本行代码") self.app.safe_back('daily -> quiz') try: back_confirm = self.app.driver.find_element_by_xpath( rules["daily_back_confirm"]) back_confirm.click() except: logger.debug(f"无需点击确认退出")
def _blank(self): contents = self.app.wait.until( EC.presence_of_all_elements_located( (By.XPATH, rules["daily_blank_content"]))) # contents = self.find_elements(rules["daily_blank_content"]) # content = " ".join([x.get_attribute("name") for x in contents]) logger.debug(f'len of blank contents is {len(contents)}') if 1 < len(contents): # 针对作妖的UI布局某一版 content, spaces = "", [] for item in contents: content_text = item.get_attribute("name") if "" != content_text: content += content_text else: length_of_spaces = len( item.find_elements(By.CLASS_NAME, "android.view.View")) - 1 spaces.append(length_of_spaces) content += " " * (length_of_spaces) else: # 针对作妖的UI布局某一版 contents = self.app.wait.until( EC.presence_of_all_elements_located( (By.XPATH, rules["daily_blank_container"]))) content, spaces, _spaces = "", [], 0 for item in contents: content_text = item.get_attribute("name") if "" != content_text: content += content_text if _spaces: spaces.append(_spaces) _spaces = 0 else: content += " " _spaces += 1 else: # for...else... # 如果填空处在最后,需要加一个判断 if _spaces: spaces.append(_spaces) logger.debug( f'[填空题] {content} [{" ".join([str(x) for x in spaces])}]') logger.debug(f'空格数 {spaces}') blank_edits = self.app.wait.until( EC.presence_of_all_elements_located( (By.XPATH, rules["daily_blank_edits"]))) # blank_edits = self.find_elements(rules["daily_blank_edits"]) length_of_edits = len(blank_edits) logger.info(f'填空题 {content}') answer = self._verify("填空题", content, []) # if not answer: words = (''.join( random.sample(string.ascii_letters + string.digits, 8)) for i in range(length_of_edits)) else: words = answer.split(" ") logger.debug(f'提交答案 {words}') for k, v in zip(blank_edits, words): k.send_keys(v) time.sleep(1) self._submit() try: wrong_or_not = self.app.driver.find_element_by_xpath( rules["daily_wrong_or_not"]) right_answer = self.app.driver.find_element_by_xpath( rules["daily_answer"]).get_attribute("name") answer = re.sub(r'正确答案: ', '', right_answer) logger.info(f"答案 {answer}") notes = self.app.driver.find_element_by_xpath( rules["daily_notes"]).get_attribute("name") logger.debug(f"解析 {notes}") self._submit(2) if 1 == length_of_edits: localmodel.update_bank('挑战题', content, [""], answer, '', notes) else: logger.error("多位置的填空题待验证正确性") localmodel.update_bank( '填空题', content, [""], self._blank_answer_divide(answer, spaces), '', notes) except: logger.debug("填空题回答正确")
def safe_click(self, ele:str): logger.debug(f'safe click {ele}') button = self.wait.until(EC.presence_of_element_located((By.XPATH, ele))) # button = self.find_element(ele) button.click() time.sleep(1) # 点击后延时1秒,如果模拟器渲染较慢,可以适当增大这个延时
def _verify(self, category, content, options): # 职责: 检索题库 查看提示 letters = list("ABCDEFGHIJKLMN") print('') print('题目类型:', category) print('题目内容:', content) print('选项:', options) mybank = None if len(options) > 1: mybank = localmodel.query(content, options[0], category) else: mybank = localmodel.query(content, '', category) if mybank and mybank.answer: logger.info(f'已知的正确答案: {mybank.answer}') return mybank.answer excludes = mybank["excludes"] if mybank else "" logger.info(f'题目类型: {category}') tips = self._view_tips() if not tips: logger.debug("本题没有提示") if "填空题" == category: return None elif "多选题" == category: return "ABCDEFG"[:len(options)] elif "单选题" == category: return self.app._search(content, options, excludes) else: logger.debug("题目类型非法") else: if "填空题" == category: dest = re.findall(r'.{0,2}\s+.{0,2}', content) logger.debug(f'dest: {dest}') if 1 == len(dest): dest = dest[0] logger.debug(f'单处填空题可以尝试正则匹配') pattern = re.sub(r'\s+', '(.+?)', dest) logger.debug(f'匹配模式 {pattern}') res = re.findall(pattern, tips) if 1 == len(res): return res[0] logger.debug(f'多处填空题难以预料结果,索性不处理') return None elif "多选题" == category: check_res = [ letter for letter, option in zip(letters, options) if option in tips ] if len(check_res) > 1: logger.debug(f'根据提示,可选项有: {check_res}') return "".join(check_res) return "ABCDEFG"[:len(options)] elif "单选题" == category: radio_in_tips, radio_out_tips = "", "" for letter, option in zip(letters, options): if option in tips: logger.debug(f'{option} in tips') radio_in_tips += letter else: logger.debug(f'{option} out tips') radio_out_tips += letter logger.debug(f'含 {radio_in_tips} 不含 {radio_out_tips}') if 1 == len(radio_in_tips) and radio_in_tips not in excludes: logger.debug(f'根据提示 {radio_in_tips}') return radio_in_tips if 1 == len(radio_out_tips) and radio_out_tips not in excludes: logger.debug(f'根据提示 {radio_out_tips}') return radio_out_tips return self.app._search(content, options, excludes) else: logger.debug("题目类型非法")
def login_or_not(self): # com.alibaba.android.user.login.SignUpWithPwdActivity time.sleep(10) # 首屏等待时间 try: home = self.driver.find_element_by_xpath(rules["home_entry"]) logger.debug(f'不需要登录') return except NoSuchElementException as e: logger.debug(self.driver.current_activity) logger.debug(f"非首页,先进行登录") if not self.username or not self.password: logger.error(f'未提供有效的username和password') logger.info(f'也许你可以通过下面的命令重新启动:') logger.info(f'\tpython -m xuexi -u "your_username" -p "your_password"') raise ValueError('需要提供登录的用户名和密钥,或者提前在App登录账号后运行本程序') username = self.wait.until(EC.presence_of_element_located(( By.XPATH, rules["login_username"] ))) password = self.wait.until(EC.presence_of_element_located(( By.XPATH, rules["login_password"] ))) username.send_keys(self.username) password.send_keys(self.password) self.safe_click(rules["login_submit"]) time.sleep(8) try: home = self.driver.find_element_by_xpath(rules["home_entry"]) logger.debug(f'无需点击同意条款按钮') return except NoSuchElementException as e: logger.debug(self.driver.current_activity) logger.debug(f"需要点击同意条款按钮") self.safe_click(rules["login_confirm"]) time.sleep(3)