如何在百萬英雄直播答題中使用Python輔助?

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

效果圖

Ref

人人都能看懂的“跳一跳”平民算法
沖頂大會也可以這樣玩

思路

開啟多個手機模擬器,題目出現時啟動Python程序,截圖并進行OCR識別出文字,獲得各個選項,接著對問題以及各個選項分別進行搜索,獲得前幾條搜索結果或者頁面數量等進行判斷。

手機模擬器

在這里我用的是夜神手機模擬器,需要用到一個分辨率、DPI較高的模擬器用于OCR識別,剩下的配置盡量調低降低硬件開銷。


手機模擬器多開

adb

由于需要操作多個模擬器,我們首先需要知道如何連接各個模擬器。下載好adb包之后將其配置到環境變量中,然后adb devices查詢在線的模擬器即可。然而有時候模擬器抽風,這個命令不起作用,這個時候需要先adb connect之后adb devices才有用(神設定),查看對應安裝目錄,Nox\bin\debug.bat發現查找對應模擬器的端口的方法是通過查找配置文件的5555端口實現的,依葫蘆畫瓢,來到Nox\bin\BignoxVMS查看各個副本的配置文件即可得到端口。這里總結一下用到的adb命令

//輸入adb可查看相關命令用法
 //存在多個模擬器時需要用-s 指定操作的目標
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可查看相關命令用法
adb -s 127.0.0.1:62001 shell input tap x y //點擊(x,y)位置 用以點擊問題的選項

確定題目與選項位置

經過測試題目選項出現的位置是固定的,所以這里的問題主要是將分辨率較高的模擬器上選項的位置對應到低分辨率模擬器上。

分辨率480X800 DPI 160

分辨率480X800 DPI 60

分辨率240X400 DPI 60

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

相對關系

OCR

從固定位置中裁剪出問題的圖像,再OCR識別為文字。這里用到的是tesseract-ocr,同樣需要配置環境變量。
系統變量下添加變量TESSDATA_PREFIX值為E:\Tesseract-OCR\tessdata
PATH下添加E:\Tesseract-OCR
還需要下載一個中文的數據包chi_sim.traineddata放到E:\Tesseract-OCR\tessdata
cmd下輸入tesseract test.png out.txt -l chi_sim生成的out.txt即為test.png的識別結果。需要注意這個test.png需要裁剪出待識別的區域,否則效果很差。
以上步驟成功后就可以用Python的pytesseract庫來返回結果了。

    # 保存問題
    cropped_img = im[y_start:y_end, x_start:x_end]
    cv2.imwrite("total.bmp" , cropped_img)
    # 這里直接使用cropped_img不行,需要保存后再用PIL.Image打開
    text_result=pytesseract.image_to_string(Image.open("total.bmp"), lang="chi_sim"a

搜索策略

文本拿到就很好做了。預處理一下,然后用jieba獲取問題的關鍵詞,再進行搜索就行啦。這里我用的是根據搜索結果的數目來判斷的。這里請參考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))

至于說獲得搜索結果的數量,用一個Xpath來提取就好啦,相信大家寫過爬蟲之后很容易搞定的。

def get_search_nums(word):
    # url="https://www.baidu.com/s?wd="+unicode(word,"utf-8")
    #需要先轉為utf8才能進行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"}  # 瀏覽器請求頭
    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,]+)個")
    #?。。∽⒁膺@里編碼和網頁返回的編碼要對應才可以進行查找!!大坑!
    num=re.findall(pattern,result)
    num=int(num[0].replace(",",""))
    return num

結果

嗯…用這種PMI算法得到的結果還是不太靠譜,按照整個問題來搜索AC選項差異不大,按照“電影”這個關鍵詞應該選B……(正確答案是C)所以還是需要把搜索結果的前幾條給展示出來的。獲得正確答案之后朝著各個模擬器發送一個對應位置的adb shell input去點擊選項即可。

答案應該為第七藝術

改進

  • OCR效果太差導致搜索出錯。如上圖的“第六藝術”識別為“第大藝術”(啥玩意兒啊)在Ref中的第二篇文章中用到了百度的OCR,不知道效果如何。
  • 搜索多個頁面由于網絡速度的限制還是比較費時間的。
  • “跳一跳”中曾出現直接抓包分析并提交結果的方法,不知道在西瓜視頻里面能否用上。
  • 使用多個模擬器測試的過程中發現各個問題在模擬器上的出現是有時差的,延后出現對作答是有利的,能否在可接受的范圍內加大這個時差?
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容