python识别文字答题_头脑王者的Python答题助手——从OCR文字识别到Fiddler抓包

由于自己的专业不是学计算机的,所以只能利用课余时间自学Python。从上个暑假开始,写了大大小小的Python小程序,虽然大多数都比较简陋,但确实在每一次写代码的时候都能感受到编程的乐趣。

最近微信的小游戏占据了很多人的朋友圈,像跳一跳、头脑王者。自从上次体验了知乎大神写的跳一跳辅助程序之后,自己就有了做一个头脑王者答题助手的念头,一开始也是希望能够实现全自动答题,仿照跳一跳那个adb+Python的模式。看了网络上的一些教程,大多数教程都是比较简单的,没有完整代码,仅仅提供一个思路,那就自己动手丰衣足食吧~~

1.OCR文字识别

一开始接触到的就是OCR,经过百度谷歌之后,Python识别图片的文字需要pytesseract和PIL两个库,还需要识别引擎tesseract-ocr。前面两个库通过命令行安装就好了,然后tesseract可以在github下载,在安装的过程中记得要选择下载简体中文的语言包。安装完成之后,需要修改一下配置才能正常使用,找到你Python的安装路径,打开Python\Lib\site-packages\pytesseract\pytesseract.py,打开之后,作以下修改:

#tesseract_cmd = 'tesseract'

tesseract_cmd = 'C:/Program Files (x86)/Tesseract-OCR/tesseract.exe'

成功之后我们找一道题来试一下:

from PIL import Image

import pytesseract

question = pytesseract.image_to_string(Image.open('test.jpeg'),lang='chi_sim')

question = question.replace(' ','') #去除空格

print(question)

OK~虽然有一些错别字,但是至少是识别出来了。看到成功识别出题目之后,我就兴奋地去写接下去的代码,可是后来用不同的题目来测试代码的时候,才发现识别率是真的低,除了一开始兵马俑那道题,其他题测试出来全是乱码。无奈只能去谷歌提高识别率的方法,网络上都是说黑白图片、高分辨率图片的识别率会高一点。后来就加了一段修改图片的代码,都是运用了PIL这个库。

修改图片模式:

from PIL import Image

img = img.convert('1')

裁剪图片(从手机截图裁剪题目的部分):

from PIL import Image

p = Image.open(picname)

p_size = p.size #获得图片尺寸

t = p.crop((0,int(p_size[1])*0.25,p_size[0],int(p_size[1])*0.45)) #截取题目部分的图片,后两个数字要比前两个大

但是修改之后,识别率并没有明显的变化,大多数图片识别出来还是乱码,在停滞了一段时间之后(主要还是因为学习期末很多事做- -),突然想到修改图片的背景颜色和字体的颜色,经过多次检验,发现黄底黑字的识别率最高,颜色改了之后,大多数的题目都能识别出来了。

图片修改颜色:

from PIL import Image

t2 = t1.convert('RGB') #转rgb模式

for i in range(0,t2.size[0]):

for j in range(0,t2.size[1]):

r = t2.getpixel((i,j))[0]

g = t2.getpixel((i,j))[1]

b = t2.getpixel((i,j))[2]

if b>r and b>g and (r,g<100)and (b<210):

r=255

g=255

b=154 #背景蓝色变黄

elif (r,g,b>=180):

b=0 #白色字变黑

g=0

r=0

t2.putpixel((i,j), (r,g,b))

代码大概的思路是用ADB命令实时截取头脑王者的图片,然后处理图片,识别出题目和四个选项,用百度知道搜索题目,再用爬虫抓下答案,根据四个选项在答案中的出现次数,得出最佳选项。

完整代码:

from PIL import Image

import pytesseract

import requests

from bs4 import BeautifulSoup as BS

from urllib import parse

import datetime

import os

def open_pic(picname):

p = Image.open(picname)

p_size = p.size #获得图片尺寸

t = p.crop((0,int(p_size[1])*0.25,p_size[0],int(p_size[1])*0.45)) #截取题目部分的图片,后两个数字要比前两个大

t.save('./first_change.png')

t_size = t.size #获得截取后的图片尺寸

return t_size,p,t

def get_question(picsize,firstpic):

new_x = 0

new_y = 0

t = firstpic

for i in range(0,picsize[0]):

last_pixel = t.getpixel((i,0))[2]

for j in range(0,picsize[1]):

now_pixel = t.getpixel((i,j))[2]

if last_pixel < 190 and now_pixel > 200:

new_x = i-50

new_y = j-150

break

if new_x:

break #找到背景和文字刚刚转换的像素点

#背景变黄色,字体变黑色 t1 = t.crop((new_x,new_y,new_x+894,new_y+280))

t2 = t1.convert('RGB') #转rgb模式

for i in range(0,t2.size[0]):

for j in range(0,t2.size[1]):

r = t2.getpixel((i,j))[0]

g = t2.getpixel((i,j))[1]

b = t2.getpixel((i,j))[2]

if b>r and b>g and (r,g<100)and (b<210):

r=255

g=255

b=154 #背景蓝色变黄

elif (r,g,b>=180):

b=0 #白色字变黑

g=0

r=0

t2.putpixel((i,j), (r,g,b))

t2.save("./second_change.png")

question = pytesseract.image_to_string(Image.open('second_change.png'),lang='chi_sim') #分析题目

question = question.replace(' ','') #去除空格

question = question.replace('\n','') #去除换行

print(question)

return question

def get_choice(oldpic):

p = oldpic

p_size = p.size

c = p.crop((250,int(p_size[1])*11/20,850,int(p_size[1])*8/9)) #截取选项部分的图片,后两个数字要比前两个大

c1 = c.crop((0,0,600,691*1/6))

c2 = c.crop((0,160,600,300))

c3 = c.crop((0,360,600,500))

c4 = c.crop((0,550,600,691))

cc = [c1,c2,c3,c4]

choices = []

for h in cc:

for i in range(0,h.size[0]):

for j in range(0,h.size[1]):

r = h.getpixel((i,j))[0]

g = h.getpixel((i,j))[1]

b = h.getpixel((i,j))[2]

if b>r and b>g and (r,g<100)and (b<220):

r=0

g=0

b=0 #蓝色字变黑

elif (r,g,b>=160):

b=154 #白色背景变黄

g=255

r=255

h.putpixel((i,j), (r,g,b))

h.save("./ana_choice.png")

choice = pytesseract.image_to_string(Image.open("ana_choice.png"), lang='chi_sim') # 分析选项

choice = choice.replace(' ','')

#解决选项中有英文大写字母0的识别错误 if '0' in choice:

choice=choice.replace('0','O')

print (choice)

choices.append(choice)

return choices

def search_answer(question,choices):

ll = [0,10,20]

answer = []

for p in ll:

b = parse.quote(question.encode('gbk')) #转gbk码

url = 'https://zhidao.baidu.com/search?word=' + b + '&ie=gbk&site=-1&sites=0&date=0&pn=' + str(p) r = requests.get(url)

r.encoding = 'gbk' #网址转gbk编码

soup = BS(r.text, 'html.parser')

want = soup.find('div', id='wgt-list')

wants = want.find_all('dl', class_='dl')

for i in wants:

ans = i.find('dd', class_='dd answer').text

answer.append(ans)

choiceset = {}

choiceset['A'] = choices[0]

choiceset['B'] = choices[1]

choiceset['C'] = choices[2]

choiceset['D'] = choices[3]

for i in choiceset:

account = []

for j in answer:

if choiceset[i] in j:

account.append(j)

a = 0

for k in account:

a += 1

print('选' + i + '的可能性是' + str('%.2f' % (a * 100 / 30)) + '%')

def main(filename):

picsize = open_pic(filename)[0]

oldpic = open_pic(filename)[1]

firstpic = open_pic(filename)[2]

question = get_question(picsize,firstpic)

choices = get_choice(oldpic)

search_answer(question,choices)

if __name__ == '__main__':

start = datetime.datetime.now()

your = input('准备好了按y:')

if your == 'y':

os.system('adb shell screencap -p /sdcard/auto.png')

os.system('adb pull /sdcard/auto.png')

img = Image.open('auto.png')

img.convert('RGB')

img.save('auto.png')

main('auto.png')

end = datetime.datetime.now()

print ('本次一共花了'+str((end-start).seconds)+'秒')

尝试运行一下,发现运行时间太太太太长了,估计是图片识别会占用很长时间,每当我5个题目答完,第一题才刚刚分析出来,虽然过程中花了很多心思,但是这种效果肯定是没有实用性的,让人心酸。

2.Fiddler抓包

正打算放弃这个程序的时候,发现了Fiddler这个抓包工具,之前学爬虫的时候就听到过,但是那时候没认真研究。应用到这里刚刚好,通过Fiddler实时抓取头脑王者传输的数据,把数据保存下来给Python分析,接下来的事就简单得多了。

Fiddler手机抓包的教程网上有很多,重点是把传输的数据自动保存下来。使用Fiddler时最后设置成只看含有‘quiz’的url,不然会冒出很多无关的数据。

设置完之后玩一局游戏,软件中出现了五个新的数据,里面就包含了每一道题的信息。原来之前辛辛苦苦弄图片识别,现在这么容易就把题目和选项拿到手了。

接下来就是最重要的自动保存json数据,在软件中的‘FiddlerScript’--‘OnBeforeResponse’修改一下代码:

在原有的基础上加这段代码:

if(oSession.host == 'question.hortor.net'){

oSession.utilDecodeResponse(); //Decoding HTTP request in case it's gzip //Saving full request object (Including HTTP headers) oSession.SaveResponse('C:\\Users\\XXXX\\Desktop\\data\\response.txt',true);

//Saving just body oSession.SaveResponseBody('C:\\Users\\XXXX\\Desktop\\data\\responsebody.txt');

}

有了数据文件,接下来的事就交给Python了,直接贴代码:

import json

import time

from urllib import parse

import requests

from bs4 import BeautifulSoup as BS

def get_appinf(filename):

f = open(filename, 'r', encoding='utf-8')

try:

j = json.loads(f.read())

#判断数据文件是否有题目和选项 if 'quiz' in j['data'] and 'options' in j['data']:

num = j['data']['num']

quiz = j['data']['quiz']

print(('第'+str(num)+'题:'+quiz).center(50,'*')+'\n')

cho = j['data']['options']

else:

pass

return quiz,cho

except:

pass

f.close()

def search(question,choice):

pagenum = [0,10,20]

answer = []

for i in pagenum:

q = parse.quote(question.encode('gbk')) # 转gbk码

url = 'https://zhidao.baidu.com/search?word=' + q + '&ie=gbk&site=-1&sites=0&date=0&pn=' + str(i) requests.packages.urllib3.disable_warnings() # 忽视网页安全性问题

r = requests.get(url, verify=False) # 不验证证书

r.encoding = 'gbk' # 网址转gbk编码

soup = BS(r.text, 'html.parser')

want = soup.find('div', id='wgt-list')

wants = want.find_all('dl', class_='dl')

for i in wants:

ans = i.find('dd', class_='dd answer').text

answer.append(ans)

choiceset = {}

choiceset['A'] = choice[0]

choiceset['B'] = choice[1]

choiceset['C'] = choice[2]

choiceset['D'] = choice[3]

#计算四个选项在爬取百度答案中的出现次数 results = {}

for i in choiceset:

account = []

for j in answer:

if choiceset[i] in j:

account.append(j)

result = len(account)/30

results[i] = result

if i == 'D':

print(('选' + i + '的可能性是:%.2f%%' % (result * 100 )).center(50)+'\n')

else:

print(('选' + i + '的可能性是:%.2f%%' % (result * 100 )).center(50))

#选出数值最大元素的对应键 bestchoice = max(results.items(), key=lambda x: x[1])[0]

print (('此题最好选'+bestchoice).center(50,'-')+'\n\n\n')

def main():

try:

que,cho = get_appinf('C:/Users/XXXX/Desktop/data/responsebody.txt') #修改成你自己的保存位置

search(que,cho)

except:

pass

if __name__ == '__main__':

while True:

main()

time.sleep(2)

这次的程序实际效果比之前的好多了,在手机上的题目出来之前,Fiddler就能抓取到数据并通过Python找到答案,但是问题也是很明显,稍微复杂一点的题目百度也搜索不出来,还有反向题目(‘不属于’、‘不包括’‘不是’)的识别率也不高,偶尔也会被答题大神吊打,但是拿来娱乐一下其实也足够了,毕竟头脑王者不同什么登顶大会,答对题没有奖金。程序出来之后,花了大半个小时上了王者。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值