赞
踩
近期学校要求强制去微信公众号小程序进行:安全知识答题,连续5天,每次25题,时间由10分钟逐级递减,感觉题量有点大,而且每天都要答题,所以我就在想有没有什么办法能进行自动答题,同时也可以来锻炼自己的技术,经过不断踩坑失败后,终于成功了。效果如下图所示:
1.selenium自动化:首先我想到的就是,能不能分享小程序链接,在网页做,可以用selenium来实现,不过我发现这个小程序链接根本就分享不了,这种方法直接pass。
2.抓包:可以用一些抓包软件来抓包解析请求分析参数,但是难度肯定极高,而且说不定一堆反爬,鉴于我逆向基础较差这种方法pass
3.用模拟器模拟手机,然后登陆微信进行操作,最后发现模拟器登陆微信打不开这个做题小程序,不知道是不是反扒措施,非常恼火。
山穷水尽疑无路,柳暗花明又一村。经过一次次碰壁,我最终选择了一个最笨的办法:
1.使用投屏软件将手机投屏到电脑屏幕上
2.使用python的pyautogui库,将电脑截屏,刚好把题目截下来
3.使用百度api图片文字识别技术,将截屏的问题识别出来
4.获取题库,采用正则表达式将问题和答案保存进字典里
5.根据问题和字典获取答案
这里我使用的软件是todesk,手机和电脑各下载一个,就可以投屏了
其中(x1,y1)是左上角坐标, (x2,y2)是右下角坐标
- import pyautogui
-
-
- # 获取屏幕截图
- def save_screen(x1, y1, x2, y2):
- screenshot = pyautogui.screenshot()
- cropped_screenshot = screenshot.crop((x1, y1, x2, y2))
- cropped_screenshot.save('cropped_screenshot.png')
-
-
- save_screen(66, 400, 730, 1500)
这里需要去百度智能云-登录注册账号,创建应用,把下面的api_key和secret_key换成自己的就行了
- import base64
- import urllib
- import requests
-
- API_KEY = "您的API_KEY "
- SECRET_KEY = "您的SECRET_KEY "
-
-
- def get_text(filaPath):
- url = "https://aip.baidubce.com/rest/2.0/ocr/v1/accurate?access_token=" + get_access_token()
- image = get_file_content_as_base64(filaPath, True)
- payload = f'image={image}'
- headers = {
- 'Content-Type': 'application/x-www-form-urlencoded',
- 'Accept': 'application/json'
- }
- response = requests.request("POST", url, headers=headers, data=payload)
- return response.text
-
-
- def get_file_content_as_base64(path, urlencoded=False):
- """
- 获取文件base64编码
- :param path: 文件路径
- :param urlencoded: 是否对结果进行urlencoded
- :return: base64编码信息
- """
- with open(path, "rb") as f:
- content = base64.b64encode(f.read()).decode("utf8")
- if urlencoded:
- content = urllib.parse.quote_plus(content)
- return content
-
-
- def get_access_token():
- """
- 使用 AK,SK 生成鉴权签名(Access Token)
- :return: access_token,或是None(如果错误)
- """
- url = "https://aip.baidubce.com/oauth/2.0/token"
- params = {"grant_type": "client_credentials", "client_id": API_KEY, "client_secret": SECRET_KEY}
- return str(requests.post(url, params=params).json().get("access_token"))
-
-
- get_text("images/2.png")
这是一份题库,里面涵盖了绝大部分原题,不会写代码的把文字复制进文档,按ctrl+f搜索题也可以查到题和答案。
也可以将下列文字保存进txt文件,然后使用正则表达式进行解析,由于题库格式问题我踩了不少坑,我列举一下我踩过的坑:
我开始发现的规律是每道题都以 题号(数字)+、开头 ,以 ()。结尾,所以我开始写的正则表达式就是 res = re.findall('\d{1,3}、(.*?)()。', text, re.S) 但是很快我就发现,匹配的少了几十道题,误差较大,在我仔细核对后发现:
第一个坑:
问题:有些题不以()。为结尾,这就导致很多题没匹配上
解决方法:在我的一番观察后发现,虽然很多题空不在最后,但是每到题结束后肯定有选项吧,第一个选项就是选项A,改进后的代码: res = re.findall('\d{1,3}、(.*?)A', text, re.S) 确实多匹配到很多数据,但是还是有遗漏。
第二个坑:
问题:有些题不以 题号(数字)+、开头,比如有的是题号(数字)+. 或者题号(数字)+空格
解决方法:res = re.findall('\d{1,3}[、\s \\.](.*?)A', text, re.S),这样不论是以 点 顿号 还是空格开头,都能匹配上了。
第三个坑:
问题:有些题的选项部分甚至没有ABCD,只有简单的换行,而且题目还有几十个,看到这我一下子就懵了,没办法题库质量太差了。
解决方法:res = re.findall('\d{1,3}[、\s \\.](.*?)答案', text, re.S),这样即使有的题没有abcd选项,至少有“参考答案”这几个字是全的(有的参考两个字都没有,所以我匹配的是答案)这样一下就能直接匹配一整道题,答案的匹配就容易很多了answer= re.findall('答案.*?(\w{1,7}\s', text, re.S)因为答案有单选和多选,而且有的多选很恶心中间放空格。
第三个坑:
问题:有些问题没有题号,导致无法匹配
解决方法:这下真的没办法了,还好不是很多,只能一个一个去加了,如果大家有什么好办法欢迎来评论区讨论。
这里我将识别文本重新拼接了一下,可以直接返回第count张图片的问题
- def get_question(count):
- url = "https://aip.baidubce.com/rest/2.0/ocr/v1/accurate?access_token=" + get_access_token()
- image = get_file_content_as_base64(f"images/{count}.png", True)
- payload = f'image={image}'
- headers = {
- 'Content-Type': 'application/x-www-form-urlencoded',
- 'Accept': 'application/json'
- }
- response = requests.request("POST", url, headers=headers, data=payload)
- data = json.loads(response.text)
- words_result = data['words_result']
- question = ''
- flag = False
- for result in words_result:
- words = result['words']
- if '【判断】' in words or '【多选】' in words or '【单选】' in words:
- flag = True
- if flag:
- question += words
- if '()' in words:
- flag = False
- print('问题:', question)
- return question
结果如下:
主函数如下:
1.首先加载保存好的字典
2.为了能够持续识别,采用while True循环
3.屏幕区域截图
4.通过截图获取问题
5.为了防止截图识别到的文字和题库有细微差别,这里我选取了识别到的问题中两个关键词,判断题库里的问题是否同时包含这俩关键词,如果同时包含,说明大概率就是这道题,输出问题以及结果就行。
优化思路:可以选用机器学习里的知识,求 截图问题的文本 和 所有题库里的问题 做相关性分析,进行排序找到相似度最高的,输出答案。
- def main():
- # 加载题库
- dan_xuan = load_answer('构建题库/单选.json')
- duo_xuan = load_answer('构建题库/多选.json')
- pan_duan = load_answer('构建题库/判断.json')
- x1, y1, x2, y2 = 66, 400, 730, 1500
- count = 0
- while True:
- s = input('是否继续?')
- count += 1
- save_screen(x1, y1, x2, y2, count)
- q = get_question(count)
- s1 = q[8:11]
- ss = q[-8:-4]
- print('关键词: ', s1, ss)
- print()
- if '【单选】' in q:
- for k, v in dan_xuan.items():
- if ss in k and s1 in k:
- print('搜索结果:'+k, '答案:'+v)
- if '【多选】' in q:
- for k, v in duo_xuan.items():
- if ss in k and s1 in k:
- print('搜索结果:' + k, '答案:' + v)
- if '【判断】' in q:
- for k, v in pan_duan.items():
- if ss in k and s1 in k:
- print('搜索结果:'+k, '答案:'+v)
- print('\n\n\n')
- import base64
- import json
- import urllib
- import pyautogui
- import requests
-
- API_KEY = "您的API_KEY "
- SECRET_KEY = "您的SECRET_KEY "
-
-
- def get_file_content_as_base64(path, urlencoded=False):
- with open(path, "rb") as f:
- content = base64.b64encode(f.read()).decode("utf8")
- if urlencoded:
- content = urllib.parse.quote_plus(content)
- return content
-
-
- def get_access_token():
- url = "https://aip.baidubce.com/oauth/2.0/token"
- params = {"grant_type": "client_credentials", "client_id": API_KEY, "client_secret": SECRET_KEY}
- return str(requests.post(url, params=params).json().get("access_token"))
-
-
- # 获取屏幕截图
- def save_screen(x1, y1, x2, y2, i):
- screenshot = pyautogui.screenshot()
- cropped_screenshot = screenshot.crop((x1, y1, x2, y2))
- cropped_screenshot.save(f'images/{i}.png')
-
-
- #加载json文件读取为字典
- def load_answer(fileName):
- with open(fileName, 'r') as file:
- data_dict = json.load(file)
- return data_dict
-
-
- #获取第i张图片的 问题 的文本
- def get_question(count):
- url = "https://aip.baidubce.com/rest/2.0/ocr/v1/accurate?access_token=" + get_access_token()
- image = get_file_content_as_base64(f"images/{count}.png", True)
- payload = f'image={image}'
- headers = {
- 'Content-Type': 'application/x-www-form-urlencoded',
- 'Accept': 'application/json'
- }
- response = requests.request("POST", url, headers=headers, data=payload)
- data = json.loads(response.text)
- words_result = data['words_result']
- question = ''
- flag = False
- for result in words_result:
- words = result['words']
- if '【判断】' in words or '【多选】' in words or '【单选】' in words:
- flag = True
- if flag:
- question += words
- if '()' in words:
- flag = False
- print('问题:', question)
- return question
-
-
- def main():
- # 加载题库
- dan_xuan = load_answer('构建题库/单选.json')
- duo_xuan = load_answer('构建题库/多选.json')
- pan_duan = load_answer('构建题库/判断.json')
- x1, y1, x2, y2 = 66, 400, 730, 1500
- count = 0
- while True:
- s = input('是否继续?')
- count += 1
- save_screen(x1, y1, x2, y2, count)
- q = get_question(count)
- s1 = q[8:11]
- ss = q[-8:-4]
- print('关键词: ', s1, ss)
- print()
- if '【单选】' in q:
- for k, v in dan_xuan.items():
- if ss in k and s1 in k:
- print('搜索结果:'+k, '答案:'+v)
- if '【多选】' in q:
- for k, v in duo_xuan.items():
- if ss in k and s1 in k:
- print('搜索结果:' + k, '答案:' + v)
- if '【判断】' in q:
- for k, v in pan_duan.items():
- if ss in k and s1 in k:
- print('搜索结果:'+k, '答案:'+v)
- print('\n\n\n')
-
-
- if __name__ == '__main__':
- main()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。