Appium 爬取安卓版网易云音乐单曲评论(热门评论,近期热评,所有评论)

编程入门 行业动态 更新时间:2024-10-04 17:26:36

Appium 爬取安卓版网易云音乐<a href=https://www.elefans.com/category/jswz/34/1763917.html style=单曲评论(热门评论,近期热评,所有评论)"/>

Appium 爬取安卓版网易云音乐单曲评论(热门评论,近期热评,所有评论)

python Appium 爬取安卓版网易云音乐单曲评论(热门评论,近期热评,所有评论)

  • 程序概述
    • 网易云音乐的评论可以通过网页版进行爬取。但是目前网页版的评论内容有以下问题:
    • 使用 appium 爬取评论能解决以上两个问题,但是目前依旧存在的问题:
  • 环境搭建
  • 代码实现
    • 配置文件
    • 实现全局界面刷新的等待
    • 查找评论分类的方法
    • 爬取评论的方法
  • 程序实现功能
  • 爬取方法和数据样例:

程序概述

使用appium 爬取网易云音乐歌曲评论。

该博客内容只是对使用appium 爬取网易云音乐评论的一个概述。如果您对该程序感兴趣可以关注我,欢迎大家交流经验

网易云音乐的评论可以通过网页版进行爬取。但是目前网页版的评论内容有以下问题:

1、就是当翻页到500页左右的时候,服务端就不再返回正确的数据。返回的都是重复数据,知道最后500页才正确。
2、网页端的api目前比较陈旧,热门评论显示不全,近期热评不显示。

使用 appium 爬取评论能解决以上两个问题,但是目前依旧存在的问题:

1、由于app客户端返回的评论数据是加密的(需逆向工程分析解密),无法通过抓包直接分析返回的json数据。只能在界面上查找自己所需要的元素。这样整个程序处理的速度比较慢。
2、由于需要滑动界面,滑动的界面可能会切开一个完整的评论(完整的评论包括,昵称,点赞数,内容,时间,可能包括回复内容)。这样可能获取的数据存在不完整的情况。可能丢失回复的内容。
3、依托appium 、模拟器、或者真机,程序的稳定性比较差。

环境搭建

请自行使用搜索引擎python Appium环境搭建,这里不再赘述。需要以下环境:
1、Python3.6 以上
2、JDK1.8以上
3、安卓SDK
4、Appium(可用桌面版,比较方便)
5、安卓模拟器或安卓真机。模拟器:雷电3.44,分辨率 宽:1080 高:1920 DPI:160-240 (DPI 设置很重要,可以控制页面能够显示的评论数,最好是160左右)
6、mysql
7、网易云安卓客户端,V5.8.3

代码实现

这里讲述大概的实现流程。

配置文件

包含以下主要内容
1、数据库的连接配置
2、appium启动安卓程序的配置
3、appium需要点击和查找的元素的路径和节点名称
数据库连接代码

HOST = '127.0.0.1'
USER = 'root'
PASSWD = 'root'
PROT = 3306
DBNAME = 'music_comments'
CHARSET = 'utf8mb4'

启动安卓程序配置代码(根据模拟器或真机需要调整,以下为雷电模安卓拟器的实现)

APPIUM_ADDR = "http://127.0.0.1:4723/wd/hub"
DESIRED_CAPS = {"platformName": "Android",# "platformVersion": "6.0.1", #mumu模拟器"platformVersion": "5.1.1","appPackage": "comease.cloudmusic","appActivity": ".activity.MainActivity","deviceName": "emulator",# "deviceName": "127.0.0.1:7555", mumu模拟器的地址"noReset": True,"unicodeKeyboard": True,  # 输入中文。"resetKeyboard": True  # 输入中文。
}

查找节点元素的配置
通过xpath查找方法或id查找确定元素的内容

# # find_ele_str
SEARCH_BTN_XPATH_S = "//android.widget.TextView[@content-desc='搜索']"  # 搜索按钮路径
INPUT_BOX_ID_S = 'comease.cloudmusic:id/search_src_text'  # 输入框路径
SEARCH_RES_TAB_CLASSNAME_M = 'android.support.v7.app.ActionBar$Tab'  # 搜索结果的tab页
SEARCH_RES_SONG_MORE_INFO_ID_S = 'comease.cloudmusic:id/a'  # 搜索结果的歌曲的三个点
MORE_INFO_OF_COMMENT_XPATH_S = "//android.widget.TextView[contains(@text,'评论')]"  # 更多信息里的评论按钮RETURN_CUR_PAGE_TOP_ID_S = "comease.cloudmusic:id/m_"  # 返回评论界面的置顶
VIEW2VIEW_XPATH_M = "//android.view.View[2]/*"  # 评论总数
TITLE_SONG_NAME_ID_S = "comease.cloudmusic:id/a8p"  # 歌曲名字
TITLE_SINGER_NAME_ID_S = "comease.cloudmusic:id/a8q"  # 歌手名字
LISTVIEW_XPATH_M = "//android.widget.ListView/android.widget.RelativeLayout"  # 所有评论的LISTVIEW
COMMENT_TYPE_ID_S = "comease.cloudmusic:id/a8y"  # 获取歌曲类别的 。数据库中commentType 0 代表最新评论,1 代表精彩评论,2 代表近期热评
NICK_NAME_ID_S = "comease.cloudmusic:id/a80"  # 昵称
COMMENT_TIME_ID_S = "comease.cloudmusic:id/a8s"  # 评论时间
LIKE_CNT_ID_S = "comease.cloudmusic:id/a7z"  # 点赞数
CONTENT_TEXT_ID_S = "comease.cloudmusic:id/a81"  # 评论内容
REPLY_LINK_ID_S = "comease.cloudmusic:id/aip"  # 回复内容链接
BE_REPLY_CONTENT_ID_S = "comease.cloudmusic:id/a8_"  # 回复内容
MORE_HOT_COMMENT_ID_S = "comease.cloudmusic:id/b5m"  # 更多精彩评论
FROM_MORE_RETURN_MAIN_COMMENT = "//android.view.View[2]/android.widget.ImageButton"  # 返回箭头RELPLY_LINEARLAYOUT_XPATH_S = ".//android.widget.LinearLayout/android.widget.LinearLayout"  # 回复内容可点击的
RELPLY_LINEARLAYOUT_TEXT_XPATH_S = ".//android.widget.LinearLayout/android.widget.LinearLayout//android.widget.TextView" #回复内容文本数量该方法适用回复少于3条的内容

实现全局界面刷新的等待

因为需要实现界面的等待,但是appium的隐式等待显示等待不能满足全部需求,因为评论元素在一个view内是重复出现的。所以如果只是使用自带两个等待可能会出现数据丢失的问题。所以需要自定义一个等待方法。该方法表示当滑动屏幕完成后,当前界面如果在一定的条件下未发生变化,可认为界面整体加载完毕。一次性判断加载整体界面加载完毕的方式也可以节省大量的时间。以下是实现该方法的思路

    def wait_full_view(self, interval, comp_times, timeout):"""刷新整个view可以设置刷新间隔:param interval: 刷新间隔-秒单位:param comp_times: 如果连续刷新该次数的driver.page_source相同得话则认为界面加载完毕:param timeout: 最长得等待时间,如果超出该时间则认为界面加载完毕-秒单位:return:"""starttime = int(time.time() * 1000)comp_count = 0page_init = self.driver.page_sourcewhile True:page_fir = page_inittime.sleep(interval)page_sec = self.driver.page_sourceif page_fir == page_sec:comp_count += 1if page_fir != page_sec:comp_count = 0page_init = page_secendtime = int(time.time() * 1000)if comp_count >= comp_times or (endtime - starttime) >= timeout * 1000:break

查找评论分类的方法

如果我们要按照分类进行爬取,就必须知道评论的类型是属于热门,近期热评还是近期评论
所以需要知道评论类别的分界线,方便爬取

    def __aline_type_frame(self, app_control, type_str):  # 对齐评论类型与顶栏print("将尝试齐评论类型与顶栏")find_res = Falseself.__return_top(app_control)x = app_control.x * 0.5y = app_control.y * 0.5top_frame = app_control.find_one_element(By.ID, config.RETURN_CUR_PAGE_TOP_ID_S)swip_y_end = top_frame.location.get('y') + top_frame.size.get('height')while True:app_control.wait_full_view(0.4, 3, 5)# comments_type_name = app_control.find_one_element(By.ID, "comease.cloudmusic:id/a8u")try:maybe_hidde = app_control.driver.find_element(By.ID, config.COMMENT_TYPE_ID_S)except Exception:maybe_hidde = Noneif maybe_hidde:s = 3500  # 时间速度if maybe_hidde.location['y'] > y:app_control.swipe_up_down(650, 0.75, 0.65, 1)s = 6000  # 距离让子弹飞一会app_control.wait_full_view(0.4, 3, 5)comments_type_name = app_control.find_one_element(By.ID, config.COMMENT_TYPE_ID_S)if comments_type_name and comments_type_name.text[:4] == type_str:swip_y_begin = comments_type_name.location['y']app_control.driver.swipe(x, swip_y_begin, x, swip_y_end, s)find_res = Truebreakif comments_type_name and comments_type_name.text[:4] == '最新评论':breakapp_control.swipe_up_down(750, 0.75, 0.30, 1)return find_res

爬取评论的方法

    def __get_commmet(self, app_control, comment_type=None, entry_reply_link=False):"""因为滑屏的原因,将一个完整的评论,时间,昵称,内容等有可能进行了切分。虽然提供了简单的去重逻辑但是,不保证完全去除成功,并且有很小的概率删除一个人评论的内容相同的。该方法应该放在对齐方法的后面:param app_control: app_control类:param comment_type: 评论类型:param entry_reply_link: 是否进入回复评论连接:return:"""dbctl = OperateDB()swipe_count = 0distinct_pool = []  # 初始值定为50 为滑动的两页的数量driver_source_pool = []  # 存放整体页面。用来跳出循环type_flag_list = []  # 用来存放找到的评论分类while True:perhaps_flag = False  # 判断两个页面是不是衔接失败。如果driver_source_pool中不存在页面的第一个元素则认为衔接失败反向滑屏if len(driver_source_pool) >= 5:driver_source_pool.append(utils.get_str_md5(app_control.driver.page_source))driver_source_pool.pop(0)else:driver_source_pool.append(utils.get_str_md5(app_control.driver.page_source))if len(driver_source_pool) >= 5 and len(set(driver_source_pool)) == 1:print('滑动屏幕多次,界面未发生变化,不再继续滑动')breakrelativelayout_list = app_control.find_all_elements(By.XPATH, config.LISTVIEW_XPATH_M)destination_el = relativelayout_list[2]  # 用于scoll 这个为1 能保证数据的完整性。滑动到第二个元素的距离origin_el = relativelayout_list[-2]  # 从最后一个元素开始滑动relativelayout_index = 0for relativeLayout in relativelayout_list:# 找到第一个类别分界线。一般是指对其后的第一个one_type = app_control.get_element_text(relativeLayout, By.ID, config.COMMENT_TYPE_ID_S)if one_type:type_flag_list.append(one_type)if len(type_flag_list) >= 2:  # 如果找到第二个的话则中断循环breaknick_name = app_control.get_element_text(relativeLayout, By.ID, config.NICK_NAME_ID_S)comment_time = app_control.get_element_text(relativeLayout, By.ID, config.COMMENT_TIME_ID_S)like_cnt = app_control.get_element_text(relativeLayout, By.ID, config.LIKE_CNT_ID_S)content_text = app_control.get_element_text(relativeLayout, By.ID, config.CONTENT_TEXT_ID_S)be_reply = app_control.get_element_text(relativeLayout, By.ID, config.BE_REPLY_CONTENT_ID_S)be_reply_count = app_control.get_element_text(relativeLayout, By.ID, config.REPLY_LINK_ID_S)# print(be_reply_count)be_reply_count = utils.turn_reply_cnt(be_reply_count)if be_reply_count is None:reply_layout = app_control.elements_exist(relativeLayout, By.XPATH,config.RELPLY_LINEARLAYOUT_TEXT_XPATH_S)if reply_layout is not None:be_reply_count = len(reply_layout)like_cnt = utils.trun_likecount_str(like_cnt)if nick_name is None or comment_time is None or content_text is None:continueone_full_comment = utils.str_join_md5(nick_name, comment_time, like_cnt, content_text,be_reply)  # 将字符串结果组合然后进行MD5if len(distinct_pool) >= config.DISTINCT_POOL and relativelayout_index == 1:if one_full_comment not in distinct_pool:perhaps_flag = Truebreak# print(len(distinct_pool),relativelayout_index,perhaps_flag)if one_full_comment in distinct_pool:distinct_pool.append(one_full_comment)print('该条记录存在缓存池,不再放入到数据库中')else:distinct_pool.append(one_full_comment)print('昵称:%s; 时间:%s; 点赞数:%s; 内容:%s...' % (nick_name, comment_time, like_cnt, content_text[:20]))sql = "INSERT INTO `music_comments`.`comment_content`(`COMMENT_ID`, `TASK_ID`, `NICK_NAME`, " \"`COMMENT_TIME`, `LIKE_COUNT`, `COMMENT_TEXT`, `BE_REPLIED_TEXT`, `BE_REPLIED_COUNT`, " \"`COMMENT_TIME_PROC`, `COMMENT_TYPE`, `SWIPE_COUNT`, `CREATE_TIME`) " \"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s);"comment_id = int(time.time() * 1000)comment_info_list = (comment_id, self.task_id, nick_name, comment_time, like_cnt, content_text, be_reply,be_reply_count, utils.turn_time(comment_time), comment_type, swipe_count,datetime.datetime.now())dbctl.add_data(sql, comment_info_list)if entry_reply_link:  # 进入回复的评论self.__entry_reply_comment(dbctl, app_control, relativeLayout, comment_id)if len(distinct_pool) > config.DISTINCT_POOL:distinct_pool.pop(0)relativelayout_index += 1if len(type_flag_list) >= 2:breakelif not perhaps_flag:app_control.el_scroll(origin_el, destination_el, 4000)# app_control.swipe_up_down(450, 0.75, 0.30, 1)  # 这个不太好用,先弃用吧swipe_count += 1else:app_control.swipe_up_down(random.randint(650, 800), 0.30, 0.60, 1)print('可能衔接中断,开始上滑')app_control.wait_full_view(0.35, 3, 5)  # 大概每次网络请求会返回20条评论,在之后界面有一段需要加载的的时间,# 就是界面显示正在加载,这时无法使用显示等待或者隐式等待,通过判断整体界面在特定条件下是否有变化来判断界面是否加载完毕

程序实现功能

1 、对爬取所有评论时出现中断实现断点续爬
2、单独爬取不同类别的评论,爬取所有网易云音乐评论的所有热门评论,近期热评,近期评论,所有评论
3、爬取评论被回复的内容。可以爬取评论的被回复的所有内容
4、将爬取下来的数据存储到mysql 数据库

爬取方法和数据样例:

举例:爬取歌曲《后来》热门评论,不爬取评论回复内容,python run.py -n 城南花已开
参数说明:
-n:必须参数,歌曲名字,可以用“-”追加歌手名等内容但是不能有空格
-t:可选参数,任务类型,默认爬取热门评论。0 爬取最新评论,1爬取热门评论,2爬取近期热评,3近期热评+热门评论,4全部爬取
-i:可选参数,任务断点续爬,后面跟任务号。仅支持任务类型包括最新评论的重新爬取,该参数指定后再指定-t参数会无效
-e:可选参数,是否进入回复链接爬取回复内容,默认不爬取。1爬取,0不爬取
数据样例:
任务说明表信息

爬取的数据 城南花已开这首音乐,目前已经存在2000多条热门评论(如果仔细分析,这首歌和背后的故事的确鼓舞了很多人。),样例数据如下:

以上只是对使用appium 爬取网易云音乐评论的一个概述。如果您对该程序感兴趣可以关注我,欢迎大家交流经验

更多推荐

Appium 爬取安卓版网易云音乐单曲评论(热门评论,近期热评,所有评论)

本文发布于:2024-02-14 13:47:22,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1763484.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:单曲   网易   热评   近期   热门

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!