为了追到小姐姐,我用 Python 制作了一个机器人
閱讀文本大概需要 15 分鐘。
1
目 標(biāo) 場(chǎng) 景
最近發(fā)現(xiàn)有一個(gè)微信好友,我的每一條朋友圈動(dòng)態(tài),無論什么時(shí)候發(fā)布,發(fā)布的什么內(nèi)容,點(diǎn)贊列表總有它的身影。
這不禁讓我陷入一種沉思,是否我也能做一個(gè)機(jī)器人,第一個(gè)時(shí)間給暗戀的小姐姐朋友圈點(diǎn)贊,是不是也能拉動(dòng)我們之間的距離。
作為技術(shù)人,肯定首先想的是如何實(shí)現(xiàn)的,實(shí)現(xiàn)這個(gè)功能的主流方案就下面 3 種,分別是:自動(dòng)化、無障礙服務(wù)、Xposed 插件。
本篇文章帶大家利用 Python 自動(dòng)化實(shí)現(xiàn)這一騷操作。
2
編 寫 代 碼
在開始編寫代碼之前,需要做下面的準(zhǔn)備工作
?Android 開發(fā)環(huán)境
本機(jī)安裝?Node.js
npm 命令安裝?Appium Server
安裝 Python 依賴包
百度情感分析 API
開啟 Appium 服務(wù)
下面通過 7 步完成這個(gè)功能,分別是:打開微信、進(jìn)入朋友圈入口、
首次滑動(dòng)處理、獲取每條動(dòng)態(tài)的內(nèi)容、操作點(diǎn)贊、可變數(shù)據(jù)參數(shù)化、異常處理。
第 1 步,打開微信
我們利用 adb 命令獲取微信應(yīng)用的包名及入口 Activity,通過數(shù)據(jù)線連接電腦,獲取到設(shè)備 id,編寫 Appium 配置文件。
#?配置文件 caps?=?{"platformName":?"Android","deviceName":?"ca2b3455",?????#?設(shè)備id"appPackage":?'com.tencent.mm',???#?微信包名"appActivity":?'com.tencent.mm.ui.LauncherUI',??#?微信入口Activity"autoGrantPermissions":?True,????"noReset":?True???#?不重置應(yīng)用 }然后,WebDriver 就能通過上面的配置文件打開微信 App 了。
#?根據(jù)配置文件,驅(qū)動(dòng)應(yīng)用打開 self.driver?=?webdriver.Remote("http://127.0.0.1:4723/wd/hub",?caps)#?隱式等待微信主頁完全加載 self.driver.implicitly_wait(10)第 2 步,進(jìn)入朋友圈入口
只需要找到首頁的「發(fā)現(xiàn)」Tab,執(zhí)行點(diǎn)擊操作,接著點(diǎn)擊「朋友圈」文本控件,即能進(jìn)入到朋友圈主界面。
由于從點(diǎn)擊到朋友圈頁面完全加載需要一個(gè)不確定的時(shí)間,這里使用一個(gè)顯式等待,直到朋友圈「動(dòng)態(tài)列表元素」加載可見。
def?__open_friend_circle(self):"""打開朋友圈:return:"""#?點(diǎn)擊發(fā)現(xiàn)Tabfind_element_by_id_and_text(self.driver,?self.tag_id["id_page_main_discover"],self.tag_text["discover"]).click()#?進(jìn)入朋友圈find_element_by_text(self.driver,?self.tag_text["friend_circle"]).click()def?__wait_for_appear(self,?id):"""等待某個(gè)元素出現(xiàn):param?id::return:"""#?顯式等待?30s,直到元素出現(xiàn)WebDriverWait(self.driver,?30).until(EC.visibility_of_element_located((By.ID,?id)))self.__wait_for_appear(self.tag_id['id_page_friend_circle_listview'])第 3 步,首次滑動(dòng)處理
由于屏幕分辨率的差異,部分小屏手機(jī)可能第一條動(dòng)態(tài)在界面上可能展示不全,直接處理會(huì)產(chǎn)生異常,為了保證處理的完整性,需要做一次滑動(dòng)預(yù)處理。
比如:下圖的第一條動(dòng)態(tài)只有發(fā)布者和發(fā)布內(nèi)容可見,發(fā)布時(shí)間不可見。
我們只需要拿到「第一條動(dòng)態(tài)元素」的 y 軸坐標(biāo),向上對(duì)應(yīng)的距離,這樣第一條動(dòng)態(tài)就完全展示出來了。
def?swipe_first(self,?id_listview):"""首次滑動(dòng):param?param::return:"""element_listview?=?self.driver.find_element_by_id(id_listview)#?由于動(dòng)態(tài)Item從ListView的第二子元素開始,獲取到第一個(gè)子元素的高度element_content?=?element_listview.find_element_by_class_name("android.widget.LinearLayout")#?獲取元素的屬性size?=?element_content.size#?滑動(dòng)一次#?由于滑動(dòng)因?yàn)榛瑒?dòng)速度存在誤差,這里滑動(dòng)距離需要做一下處理swipe_up_with_distance(self.driver,?size.get("height")?-?50,?1000)time.sleep(2)需要注意的是,由于滑動(dòng)過快時(shí),滑動(dòng)距離會(huì)存在誤差,這里對(duì)滑動(dòng)距離稍微做了一下處理。
第 4?步,獲取每條動(dòng)態(tài)的內(nèi)容
動(dòng)態(tài)的內(nèi)容分為純文本、其他(圖片、視頻、鏈接、音樂等)、文本+其他三種形式。
我們獲取到:動(dòng)態(tài)的發(fā)布者、發(fā)布時(shí)間、發(fā)布文本內(nèi)容。
def?__get_dynamic_content(self,?element):"""獲取動(dòng)態(tài)的類型:param?element::return:"""#?文字的id:#?注意:不確定是否存在的元素,要使用find_elements_**,否則會(huì)拋出異常element_titles?=?element.find_elements_by_id(self.tag_id['id_page_friend_circle_item_title'])#?好友名element_author?=?element.find_element_by_id(self.tag_id['id_page_friend_circle_item_friend_name'])#?發(fā)布時(shí)間#?注意:可能沒法找到,導(dǎo)致異常element_publish_time?=?element.find_element_by_id(self.tag_id['id_page_friend_circle_item_publish_time'])author_name?=?element_author.get_attribute("text")publish_time?=?element_publish_time.get_attribute("text")content?=?Noneif?len(element_titles)?>?0:content?=?element_titles[0].get_attribute('text')#?返回發(fā)布者、發(fā)布時(shí)間、發(fā)布內(nèi)容return?author_name,?publish_time,?content第 5?步,操作點(diǎn)贊
根據(jù)上面獲取的內(nèi)容,去判斷這條動(dòng)態(tài)是否值得我們?nèi)c(diǎn)贊。
如果本條動(dòng)態(tài)的發(fā)布內(nèi)容不為空,我們就采用百度的情感分析 API 去分析內(nèi)容的積極性。
from?aip?import?AipNlpdef?get_word_nlp(word):"""判斷內(nèi)容是否為消極的:param?word::return:""""""?你的?APPID?AK?SK?"""APP_ID?=?'xx'API_KEY?=?'xxx'SECRET_KEY?=?'xxxx'client?=?AipNlp(APP_ID,?API_KEY,?SECRET_KEY)"""?調(diào)用情感傾向分析?"""result?=?client.sentimentClassify(word)#?該情感搭配的極性(0表示消極,1表示中性,2表示積極)sentiment?=?result.get("items")[0].get("sentiment")return?sentiment?==?0過濾掉消極內(nèi)容和已經(jīng)點(diǎn)過贊的動(dòng)態(tài),其他每一條動(dòng)態(tài)都執(zhí)行點(diǎn)贊操作。
#?如果文本存在,并且是消極的,就不處理 if?dynamic_contents[2]?and?get_word_nlp(dynamic_contents[2]):print('消極的內(nèi)容,不點(diǎn)贊!')continue#?點(diǎn)擊,彈出點(diǎn)贊按鈕element_perform_click(element,?self.tag_id['id_page_friend_circle_approve_button_pre'])#?不點(diǎn)贊的情況:已經(jīng)點(diǎn)過贊、有文字內(nèi)容并且為消極#?未點(diǎn)贊:贊;已贊:取消if?approve_text?==?'取消':#?關(guān)閉點(diǎn)贊彈框print('已經(jīng)點(diǎn)贊過,不點(diǎn)贊')element_perform_click(element,?self.tag_id['id_page_friend_circle_approve_button_pre'])continue#?注意,點(diǎn)贊按鈕沒法執(zhí)行點(diǎn)擊操作,需要往上找父類元素執(zhí)行點(diǎn)擊操作element_perform_click(self.driver,?self.tag_id['id_page_friend_circle_approve_button'])處理完一頁動(dòng)態(tài)之后,接著可以循環(huán)滑動(dòng)頁面去查找動(dòng)態(tài)列表,繼續(xù)上面的操作。
while?True:elements?=?self.driver.find_elements_by_id(id_item)#?....?循環(huán)操作#?滑動(dòng)一次swipe_up(self.driver,?500)time.sleep(2)第 6?步,可變數(shù)據(jù)參數(shù)化
為了保證后期的可維護(hù)性,對(duì)文中查詢的 id、文本等元素寫入到 yaml 配置文件中。
tag:id:id_page_main_discover:?'com.tencent.mm:id/cw2'???#?主頁:發(fā)現(xiàn)按鈕id_page_friend_circle_listview:?'com.tencent.mm:id/e2p'???#?朋友圈頁面:動(dòng)態(tài)列表id_page_friend_circle_item:?'com.tencent.mm:id/e6t'???#?朋友圈頁面:每一項(xiàng)動(dòng)態(tài)id_page_friend_circle_item_title:?'com.tencent.mm:id/e6x'??#?朋友圈頁面:動(dòng)態(tài)標(biāo)題文本id_page_friend_circle_item_friend_name:?'com.tencent.mm:id/azl'??#?朋友圈頁面:動(dòng)態(tài)的發(fā)布者id_page_friend_circle_item_publish_time:?'com.tencent.mm:id/e25'???#?朋友圈頁面:動(dòng)態(tài)發(fā)布時(shí)間id_page_friend_circle_approve_button_pre:?'com.tencent.mm:id/e2c'??#?朋友圈頁面:動(dòng)態(tài)點(diǎn)贊入口按鈕id_page_friend_circle_approve_status:?'com.tencent.mm:id/e1l'??#?朋友圈頁面:動(dòng)態(tài)點(diǎn)贊狀態(tài)文本(贊或者取消)id_page_friend_circle_approve_button:?'com.tencent.mm:id/e1k'??#?朋友圈頁面:每一個(gè)動(dòng)態(tài)的點(diǎn)贊按鈕text:discover:?'發(fā)現(xiàn)'friend_circle:?'朋友圈'后期一旦微信版本升級(jí)迭代,只需要更改此處代碼即可。
第 7?步,異常處理
上面的代碼如果不做異常處理,直接運(yùn)行很有可能會(huì)出現(xiàn)各類異常,下面逐一進(jìn)行說明。
首尾動(dòng)態(tài)處理:當(dāng)前界面第一條動(dòng)態(tài)和最后一條動(dòng)態(tài)中的部分元素不可見。
針對(duì)這個(gè)問題,需要考慮是在頂部還是尾部。如果在頂部,繼續(xù)處理下一條動(dòng)態(tài);如果在尾部,直接跳出本次循環(huán)。
for?index,?element?in?enumerate(elements):try:dynamic_contents?=?self.__get_dynamic_content(element)except?Exception?as?e:err_tag?=?"頭部元素"?if?index?==?0?else?"尾部元素"err?=?"**********%s產(chǎn)生一個(gè)異常**********"?%?err_tagprint(err)logging.error(err)logging.error(traceback.format_exc())#?判斷是頁面的第一個(gè)元素還是最后一個(gè)元素if?index?==?0:continueelse:break元素不可點(diǎn)擊:可以往上查找父級(jí)元素,直到找到一個(gè)可以點(diǎn)擊的元素,直接點(diǎn)擊操作。
元素不可見:有些元素在執(zhí)行點(diǎn)擊操作的時(shí)候,不可見。
這個(gè)問題只需要捕獲異常,滑動(dòng)小距離之后,再次執(zhí)行點(diǎn)擊操作即可。
def?fb_id(driver:?WebDriver,?parentElement,?element_id):"""通過id查找元素:param?driver::param?parentElement?父元素中查找:param?element_id::return:"""while?True:try:#?注意:查找單個(gè)元素經(jīng)常容許產(chǎn)生異常,這里進(jìn)行捕獲后,然后滑動(dòng)一次,繼續(xù)查找element?=?parentElement.find_element_by_id(element_id)return?elementexcept:print('查找元素:【%s】產(chǎn)生異常,滑動(dòng)一次,再進(jìn)行查找!'?%?element_id)swipe_up_small(driver,?500)3
結(jié) 果 結(jié) 論
通過上面的 7 步操作,就能完成了一個(gè)有感情的朋友圈點(diǎn)贊機(jī)器人。
我已經(jīng)將全部源碼上傳到后臺(tái),關(guān)注公眾號(hào)后回復(fù)「 點(diǎn)贊?」即可獲得全部源碼。
如果你覺得文章還不錯(cuò),請(qǐng)大家點(diǎn)贊分享下。你的肯定是我最大的鼓勵(lì)和支持。
來和小伙伴們一起向上生長呀!
掃描下方二維碼,添加小詹微信,可領(lǐng)取千元大禮包并申請(qǐng)加入 Python 學(xué)習(xí)交流群,群內(nèi)僅供學(xué)術(shù)交流,日?;?dòng),如果是想發(fā)推文、廣告、砍價(jià)小程序的敬請(qǐng)繞道!一定記得備注「交流學(xué)習(xí)」,我會(huì)盡快通過好友申請(qǐng)哦!
????長按識(shí)別,添加微信
(添加人數(shù)較多,請(qǐng)耐心等待)
????長按識(shí)別,關(guān)注小詹
(掃碼回復(fù) 1024 領(lǐng)取程序員大禮包)
總結(jié)
以上是生活随笔為你收集整理的为了追到小姐姐,我用 Python 制作了一个机器人的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小白级OpenCV入门
- 下一篇: 关于Python异常处理,你需要了解的知