首先寫(xiě)這個(gè)輔助的想法來(lái)源于之前微信小游戲“跳一跳”的輔助,使用到了adb來(lái)獲得手機(jī)的截圖等,而直播答題碰到不會(huì)的題遇到的困難主要就是沒(méi)時(shí)間去打字搜索,如果用同樣的方法得到了題目相關(guān)的文字信息自動(dòng)去搜索,成功率不就大大提高了嘛!再有很自然想到的一點(diǎn)就是一道題就3個(gè)選項(xiàng),3臺(tái)手機(jī)不就肯定能保證對(duì)一道嗎?因此如何快速操作多個(gè)手機(jī)模擬器也需要考慮。

Ref
人人都能看懂的“跳一跳”平民算法
沖頂大會(huì)也可以這樣玩
思路
開(kāi)啟多個(gè)手機(jī)模擬器,題目出現(xiàn)時(shí)啟動(dòng)Python程序,截圖并進(jìn)行OCR識(shí)別出文字,獲得各個(gè)選項(xiàng),接著對(duì)問(wèn)題以及各個(gè)選項(xiàng)分別進(jìn)行搜索,獲得前幾條搜索結(jié)果或者頁(yè)面數(shù)量等進(jìn)行判斷。
手機(jī)模擬器
在這里我用的是夜神手機(jī)模擬器,需要用到一個(gè)分辨率、DPI較高的模擬器用于OCR識(shí)別,剩下的配置盡量調(diào)低降低硬件開(kāi)銷(xiāo)。

adb
由于需要操作多個(gè)模擬器,我們首先需要知道如何連接各個(gè)模擬器。下載好adb包之后將其配置到環(huán)境變量中,然后adb devices查詢(xún)?cè)诰€(xiàn)的模擬器即可。然而有時(shí)候模擬器抽風(fēng),這個(gè)命令不起作用,這個(gè)時(shí)候需要先adb connect之后adb devices才有用(神設(shè)定),查看對(duì)應(yīng)安裝目錄,Nox\bin\debug.bat發(fā)現(xiàn)查找對(duì)應(yīng)模擬器的端口的方法是通過(guò)查找配置文件的5555端口實(shí)現(xiàn)的,依葫蘆畫(huà)瓢,來(lái)到Nox\bin\BignoxVMS查看各個(gè)副本的配置文件即可得到端口。這里總結(jié)一下用到的adb命令
//輸入adb可查看相關(guān)命令用法
//存在多個(gè)模擬器時(shí)需要用-s 指定操作的目標(biāo)
adb -s 127.0.0.1:62001 shell screencap -p /sdcard/n.png //截屏
adb -s 127.0.0.1:62001 pull /sdcard/n.png ./n.png //傳到電腦
//輸入adb shell input可查看相關(guān)命令用法
adb -s 127.0.0.1:62001 shell input tap x y //點(diǎn)擊(x,y)位置 用以點(diǎn)擊問(wèn)題的選項(xiàng)
確定題目與選項(xiàng)位置
經(jīng)過(guò)測(cè)試題目選項(xiàng)出現(xiàn)的位置是固定的,所以這里的問(wèn)題主要是將分辨率較高的模擬器上選項(xiàng)的位置對(duì)應(yīng)到低分辨率模擬器上。



從以上3張圖可以得到,分辨率相同的話(huà),題目區(qū)域大小與DPI成正比;DPI相同的話(huà),整個(gè)題目占用的像素點(diǎn)是相同的,題目區(qū)域大小與分辨率成反比。所以假如在480X800 DPI 160的模擬器上位置為(x,y),在240X400 DPI 60的模擬器上位置為(x,y*2*60/160)=(x,0.75y)

OCR
從固定位置中裁剪出問(wèn)題的圖像,再OCR識(shí)別為文字。這里用到的是tesseract-ocr,同樣需要配置環(huán)境變量。
系統(tǒng)變量下添加變量TESSDATA_PREFIX值為E:\Tesseract-OCR\tessdata
PATH下添加E:\Tesseract-OCR
還需要下載一個(gè)中文的數(shù)據(jù)包chi_sim.traineddata放到E:\Tesseract-OCR\tessdata
cmd下輸入tesseract test.png out.txt -l chi_sim生成的out.txt即為test.png的識(shí)別結(jié)果。需要注意這個(gè)test.png需要裁剪出待識(shí)別的區(qū)域,否則效果很差。
以上步驟成功后就可以用Python的pytesseract庫(kù)來(lái)返回結(jié)果了。
# 保存問(wèn)題
cropped_img = im[y_start:y_end, x_start:x_end]
cv2.imwrite("total.bmp" , cropped_img)
# 這里直接使用cropped_img不行,需要保存后再用PIL.Image打開(kāi)
text_result=pytesseract.image_to_string(Image.open("total.bmp"), lang="chi_sim"a
搜索策略
文本拿到就很好做了。預(yù)處理一下,然后用jieba獲取問(wèn)題的關(guān)鍵詞,再進(jìn)行搜索就行啦。這里我用的是根據(jù)搜索結(jié)果的數(shù)目來(lái)判斷的。這里請(qǐng)參考Wikipedia ——Pointwise mutual information
divided=text.replace(" ","").split("\n")
for i in divided:
if (i=="" or i=="\n"):
divided.remove(i)
question=divided[0]
del divided[0]
print("Question: %s"%question)
keywords=jieba.analyse.extract_tags(question,topK=2,withWeight=True)
print("Keywords:")
for i in keywords:
print i
totalweight=0
print ("Options: ")
print("\n".join(divided))
至于說(shuō)獲得搜索結(jié)果的數(shù)量,用一個(gè)Xpath來(lái)提取就好啦,相信大家寫(xiě)過(guò)爬蟲(chóng)之后很容易搞定的。
def get_search_nums(word):
# url="https://www.baidu.com/s?wd="+unicode(word,"utf-8")
#需要先轉(zhuǎn)為utf8才能進(jìn)行quote
url="https://www.baidu.com/s?wd="+urllib2.quote(word.encode("utf8"))
myheaders = {
'User-Agent': "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1"} # 瀏覽器請(qǐng)求頭
request=urllib2.Request(url,headers=myheaders)
response=urllib2.urlopen(request)
source=response.read()
html=etree.HTML(source)
result=html.xpath('''//*[@id="container"]/div[2]/div/div[2]/text()''')
result=result[0]
pattern=re.compile(u".*約([\d,]+)個(gè)")
#?。?!注意這里編碼和網(wǎng)頁(yè)返回的編碼要對(duì)應(yīng)才可以進(jìn)行查找??!大坑!
num=re.findall(pattern,result)
num=int(num[0].replace(",",""))
return num
結(jié)果
嗯…用這種PMI算法得到的結(jié)果還是不太靠譜,按照整個(gè)問(wèn)題來(lái)搜索AC選項(xiàng)差異不大,按照“電影”這個(gè)關(guān)鍵詞應(yīng)該選B……(正確答案是C)所以還是需要把搜索結(jié)果的前幾條給展示出來(lái)的。獲得正確答案之后朝著各個(gè)模擬器發(fā)送一個(gè)對(duì)應(yīng)位置的adb shell input去點(diǎn)擊選項(xiàng)即可。

改進(jìn)
- OCR效果太差導(dǎo)致搜索出錯(cuò)。如上圖的“第六藝術(shù)”識(shí)別為“第大藝術(shù)”
(啥玩意兒?。?/del>在Ref中的第二篇文章中用到了百度的OCR,不知道效果如何。 - 搜索多個(gè)頁(yè)面由于網(wǎng)絡(luò)速度的限制還是比較費(fèi)時(shí)間的。
- “跳一跳”中曾出現(xiàn)直接抓包分析并提交結(jié)果的方法,不知道在西瓜視頻里面能否用上。
- 使用多個(gè)模擬器測(cè)試的過(guò)程中發(fā)現(xiàn)各個(gè)問(wèn)題在模擬器上的出現(xiàn)是有時(shí)差的,延后出現(xiàn)對(duì)作答是有利的,能否在可接受的范圍內(nèi)加大這個(gè)時(shí)差?