当前位置 博文首页 > AirPython的博客:为了追到小姐姐,我用 Python 制作了一个机器
点击上方“AirPython”,选择“置顶公众号”
第一时间获取 Python 技术干货!
阅读文本大概需要 15 分钟。
1
目 标 场 景
最近发现有一个微信好友,我的每一条朋友圈动态,无论什么时候发布,发布的什么内容,点赞列表总有它的身影。
这不禁让我陷入一种沉思,是否我也能做一个机器人,第一个时间给暗恋的小姐姐朋友圈点赞,是不是也能拉动我们之间的距离。
作为技术人,肯定首先想的是如何实现的,实现这个功能的主流方案就下面 3 种,分别是:自动化、无障碍服务、Xposed 插件。
本篇文章带大家利用 Python 自动化实现这一骚操作。
2
编 写 代 码
在开始编写代码之前,需要做下面的准备工作
?Android 开发环境
本机安装?Node.js
npm 命令安装?Appium Server
安装 Python 依赖包
百度情感分析 API
开启 Appium 服务
#?1、安装?Node.js
#?2、安装?Appium
npm?install?-g?appium
#?3、打开appium服务,并开启服务便于调试
appium?-g?/appium.log
#?4、百度情感分析API依赖
pip3?install?baidu-aip
#?5、安装Python依赖
pip3?install?Appium-Python-Client
下面通过 7 步完成这个功能,分别是:打开微信、进入朋友圈入口、
首次滑动处理、获取每条动态的内容、操作点赞、可变数据参数化、异常处理。
第 1 步,打开微信
我们利用 adb 命令获取微信应用的包名及入口 Activity,通过数据线连接电脑,获取到设备 id,编写 Appium 配置文件。
#?配置文件
caps?=?{
????"platformName":?"Android",
????"deviceName":?"ca2b3455",?????#?设备id
????"appPackage":?'com.tencent.mm',???#?微信包名
????"appActivity":?'com.tencent.mm.ui.LauncherUI',??#?微信入口Activity
????"autoGrantPermissions":?True,????
????"noReset":?True???#?不重置应用
}
然后,WebDriver 就能通过上面的配置文件打开微信 App 了。
#?根据配置文件,驱动应用打开
self.driver?=?webdriver.Remote("http://127.0.0.1:4723/wd/hub",?caps)
#?隐式等待微信主页完全加载
self.driver.implicitly_wait(10)
第 2 步,进入朋友圈入口
只需要找到首页的「发现」Tab,执行点击操作,接着点击「朋友圈」文本控件,即能进入到朋友圈主界面。
由于从点击到朋友圈页面完全加载需要一个不确定的时间,这里使用一个显式等待,直到朋友圈「动态列表元素」加载可见。
def?__open_friend_circle(self):
?????"""
?????打开朋友圈
?????:return:
?????"""
?????#?点击发现Tab
?????find_element_by_id_and_text(self.driver,?self.tag_id["id_page_main_discover"],
????????????????????????????????????self.tag_text["discover"]).click()
?????#?进入朋友圈
?????find_element_by_text(self.driver,?self.tag_text["friend_circle"]).click()
def?__wait_for_appear(self,?id):
?????"""
?????等待某个元素出现
?????:param?id:
?????:return:
?????"""
?????#?显式等待?30s,直到元素出现
?????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 步,首次滑动处理
由于屏幕分辨率的差异,部分小屏手机可能第一条动态在界面上可能展示不全,直接处理会产生异常,为了保证处理的完整性,需要做一次滑动预处理。
比如:下图的第一条动态只有发布者和发布内容可见,发布时间不可见。
我们只需要拿到「第一条动态元素」的 y 轴坐标,向上对应的距离,这样第一条动态就完全展示出来了。
def?swipe_first(self,?id_listview):
????"""
????首次滑动
????:param?param:
????:return:
????"""
????element_listview?=?self.driver.find_element_by_id(id_listview)
????#?由于动态Item从ListView的第二子元素开始,获取到第一个子元素的高度
????element_content?=?element_listview.find_element_by_class_name("android.widget.LinearLayout")
????#?获取元素的属性
????size?=?element_content.size
????#?滑动一次
????#?由于滑动因为滑动速度存在误差,这里滑动距离需要做一下处理
????swipe_up_with_distance(self.driver,?size.get("height")?-?50,?1000)
????time.sleep(2)
需要注意的是,由于滑动过快时,滑动距离会存在误差,这里对滑动距离稍微做了一下处理。
第 4?步,获取每条动态的内容
动态的内容分为纯文本、其他(图片、视频、链接、音乐等)、文本+其他三种形式。
我们获取到:动态的发布者、发布时间、发布文本内容。
def?__get_dynamic_content(self,?element):
????"""
????获取动态的类型
????:param?element:
????:return:
????"""
????#?文字的id:
????#?注意:不确定是否存在的元素,要使用find_elements_**,否则会抛出异常
????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'])
????#?发布时间
????#?注意:可能没法找到,导致异常
????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?=?None
????if?len(element_titles)?>?0:
????????content?=?element_titles[0].get_attribute('text')
????#?返回发布者、发布时间、发布内容
????return?author_name,?publish_time,?content
第 5?步,操作点赞
根据上面获取的内容,去判断这条动态是否值得我们去点赞。
如果本条动态的发布内容不为空,我们就采用百度的情感分析 API 去分析内容的积极性。
from?aip?import?AipNlp
def?get_word_nlp(word):
????"""
????判断内容是否为消极的
????:param?word:
????:return:
????"""
????"""?你的?APPID?AK?SK?"""
????APP_ID?=?'xx'
????API_KEY?=?'xxx'
????SECRET_KEY?=?'xxxx'
????client?=?AipNlp(APP_ID,?API_KEY,?SECRET_KEY)
????"""?调用情感倾向分析?"""
????result?=?client.sentimentClassify(word)
????#?该情感搭配的极性(0表示消极,1表示中性,2表示积极)
????sentiment?=?result.get("items")[0].get("sentiment")
????return?sentiment?==?0
过滤掉消极内容和已经点过赞的动态,其他每一条动态都执行点赞操作。
#?如果文本存在,并且是消极的,就不处理
if?dynamic_contents[2]?and?get_word_nlp(dynamic_contents[2]):
????print('消极的内容,不点赞!')
????continue
????#?点击,弹出点赞按钮
????element_perform_click(element,?self.tag_id['id_page_friend_circle_approve_button_pre'])
????#?不点赞的情况:已经点过赞、有文字内容并且为消极
????#?未点赞:赞;已赞:取消
????if?approve_text?==?'取消':
?????????#?关闭点赞弹框
?????????print('已经点赞过,不点赞')
?????????element_perform_click(element,?self.tag_id['id_page_friend_circle_approve_button_pre'])
?????????continue
?????#?注意,点赞按钮没法执行点击操作,需要往上找父类元素执行点击操作
?????element_perform_click(self.driver,?self.tag_id['id_page_friend_circle_approve_button'])
处理完一页动态之后,接着可以循环滑动页面去查找动态列表,继续上面的操作。
while?True:
?????elements?=?self.driver.find_elements_by_id(id_item)
?????#?....?循环操作
?????#?滑动一次
?????swipe_up(self.driver,?500)
?????time.sleep(2)
第 6?步,可变数据参数化
为了保证后期的可维护性,对文中查询的 id、文本等元素写入到 yaml 配置文件中。
tag:
??id:
????id_page_main_discover:?'com.tencent.mm:id/cw2'???#?主页:发现按钮
????id_page_friend_circle_listview:?'com.tencent.mm:id/e2p'???#?朋友圈页面:动态列表
????id_page_friend_circle_item:?'com.tencent.mm:id/e6t'???#?朋友圈页面:每一项动态
????id_page_friend_circle_item_title:?'com.tencent.mm:id/e6x'??#?朋友圈页面:动态标题文本
????id_page_friend_circle_item_friend_name:?'com.tencent.mm:id/azl'??#?朋友圈页面:动态的发布者
????id_page_friend_circle_item_publish_time:?'com.tencent.mm:id/e25'???#?朋友圈页面:动态发布时间
????id_page_friend_circle_approve_button_pre:?'com.tencent.mm:id/e2c'??#?朋友圈页面:动态点赞入口按钮
????id_page_friend_circle_approve_status:?'com.tencent.mm:id/e1l'??#?朋友圈页面:动态点赞状态文本(赞或者取消)
????id_page_friend_circle_approve_button:?'com.tencent.mm:id/e1k'??#?朋友圈页面:每一个动态的点赞按钮
??text:
????discover:?'发现'
????friend_circle:?'朋友圈'
后期一旦微信版本升级迭代,只需要更改此处代码即可。
第 7?步,异常处理
上面的代码如果不做异常处理,直接运行很有可能会出现各类异常,下面逐一进行说明。
首尾动态处理:当前界面第一条动态和最后一条动态中的部分元素不可见。
针对这个问题,需要考虑是在顶部还是尾部。如果在顶部,继续处理下一条动态;如果在尾部,直接跳出本次循环。
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产生一个异常**********"?%?err_tag
??????????print(err)
??????????logging.error(err)
??????????logging.error(traceback.format_exc())
??????????#?判断是页面的第一个元素还是最后一个元素
??????????if?index?==?0:
????????????????continue
??????????else:
????????????????break
元素不可点击:可以往上查找父级元素,直到找到一个可以点击的元素,直接点击操作。
def?element_perform_click(parentElement,?id):
????"""
????某个元素执行点击操作
????:param parentElement:WebDriver或者WebElement
????:param?id:待查找的元素id
????:return:
????"""
????element?=?parentElement.find_element_by_id(id)
????#?判断是否可以点击
????element_clickable?=?element.get_attribute("clickable")
????if?element_clickable:
????????element.click()
????????return
????#?如果当前元素不可以点击,一直向上找可以点击的父类元素,执行点击操作
????while?True:
????????element?=?element.parent
????????if?element.get_attribute("clickable"):
????????????element.click()
????????????break
元素不可见:有些元素在执行点击操作的时候,不可见。
这个问题只需要捕获异常,滑动小距离之后,再次执行点击操作即可。
def?fb_id(driver:?WebDriver,?parentElement,?element_id):
????"""
????通过id查找元素
????:param?driver:
????:param?parentElement?父元素中查找
????:param?element_id:
????:return:
????"""
????while?True:
????????try:
????????????#?注意:查找单个元素经常容许产生异常,这里进行捕获后,然后滑动一次,继续查找
????????????element?=?parentElement.find_element_by_id(element_id)
????????????return?element
????????except:
????????????print('查找元素:【%s】产生异常,滑动一次,再进行查找!'?%?element_id)
????????????swipe_up_small(driver,?500)
3
结 果 结 论
通过上面的 7 步操作,就能完成了一个有感情的朋友圈点赞机器人。
我已经将全部源码上传到后台,关注公众号后回复「 点赞?」即可获得全部源码。
如果你觉得文章还不错,请大家点赞分享下。你的肯定是我最大的鼓励和支持。
推荐阅读
10万+的短视频被批量生产了,Python表示不服
视频剪辑什么鬼?Python 带你高效创作短视频
聊聊微信小程序自动化如何来做?
THANDKS
- End -
cs