当前位置 博文首页 > AirPython的博客:为了追到小姐姐,我用 Python 制作了一个机器

    AirPython的博客:为了追到小姐姐,我用 Python 制作了一个机器

    作者:[db:作者] 时间:2021-08-15 22:09

    点击上方“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
    下一篇:没有了