相信大家每天都基本离不开听歌,老的歌单听久了就累了,推送的新歌单看的眼花缭乱,今天就来试试用selenium和wordcloud来分析歌单音乐可听性程度,涉及到歌单评论内容爬取、数据库存储、统计分析及可视化。

看看都有哪些干货,本节内容包含:

使用selenium 爬取网易云音乐歌单的评论内容

使用pymysql 将评论内容存入MySQL保存

使用jieba 对中文语句分词提取高频词

使用wordcloud 做词云及可视化

一、自动化测试工具selenium

首先,我们要了解什么是selenium?

答:这是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等。这个工具的主要功能包括:测试与浏览器的兼容性——测试你的应用程序看是否能够很好得工作在不同浏览器和操作系统之上。测试系统功能——创建回归测试检验软件功能和用户需求。支持自动录制动作和自动生成 .Net、Java、Perl等不同语言的测试脚本。

为什么要用selenium?也就是selenium的优势。

(1)Selenium是开源的      (2)简单,易于安装,易于工作

(3)selenium ide是selenium的唯一可以在浏览器窗口上记录用户行为的组件

(4)除了火狐上的事件外不会记录你电脑上的任何其他事件

(5)Selenium支持多种浏览器,能够运行与多种操作系统,因此更容易帮助测试人员发现应用程序在不同浏览器上的兼容性问题。通过在不同浏览器中运行测试,更容易发现浏览器的不兼容性;

(6)通过编写模仿用户操作的 Selenium 测试脚本,可以从终端用户的角度来测试应用程序;可以操作 Web 页面上的各种元素,诸如:点击按钮、输入文本框,以及断言 Web 页面上存在某些文本与 Web 元素等.

(7)提供Selenium IDE ,一个FireFox plugin,能自动记录用户的操作,生成测试脚本。生成的测试脚本可以用Selenium Core手工执行,也能基于Selenium RC放入Java,C#,Ruby的单元测试用例中自动运行;

(8)测试用例调用实际的浏览器(如IE、FireFox)来执行测试。和有些开源方案自行实现 Web解释引擎相比,实际的浏览器能模拟更多用户交互和JS语法。

(9)SELENIUM录制的脚本比较灵活,因为它生成的是PERL的

脚本程序。作为几乎最为强大和最广泛使用语言之一,PERL这种程序给予我最大的灵活性和控制度。

3、安装和使用seleium?如果你是Python3.x直接使用pip install selenium即可,安装后可以用pip show selenium来检验是否安装成功,当然之前说了seleium是模拟用户操作的一种测试脚本,当然需要浏览器驱动来控制它了,本文使用Chrome浏览器,相应的需要安装chromedriver,注意下载完driver,要放在对用python目录下的Scripts里,同时把script路径加入环境变量,然后在python代码中导入即可:

from selenium import webdriver

简单介绍完selenium之后,我们来看看如何把它用起来,直接边看边讲,先补充第三方库:

import randomfrom math import ceilimport pymysqlimport time

二、实践爬取歌单评论内容

接下来就是前期代码,如何利用这个工具去爬网页的内容,大致流程是启动Chrome浏览器-->访问指定URL-->通过html标签定位你需要的数据-->爬取内容数据的总页数(因为所需的数据往往不仅只在单个网页中)-->定位跳转页面位置及Click操作-->循环以上步骤直至爬取完毕:

def start_spider(url):    # 启动 Chrome 浏览器并访问指定URL    brower = webdriver.Chrome()      brower.get(url)    # 等待 5 秒, 让评论数据加载完成    time.sleep(5)    # 页面嵌套一层 iframe, 必须切换到 iframe, 才能定位的到 iframe 里面的元素    iframe = brower.find_element_by_class_name('g-iframe')      brower.switch_to.frame(iframe)    # 获取评论总数    new_comments = brower.find_elements_by_xpath("//h3[@class='u-hd4']")[1]    max_page = get_max_page(new_comments.text)    current = 1    is_first = True    while current <= max_page:        print('正在爬取第', current, '页的数据')        if current == 1:            is_first = True        else:            is_first = False        data_list = get_comments(is_first, brower)        time.sleep(2)        go_nextpage(brower)        time.sleep(random.randint(8, 12))        current += 1    print(data_list)

注意,在爬取过程中建议使用time.sleep()操作,目的当然是模拟用户操作,因为用户实际操作中并不会像电脑那么快,这样能伪装身份,另外使用时间设置random.randint(x,y)使得每次等待时间不同,可以让伪装工作更升一级。

补充上面的get_max_page()函数,其目的是计算总页数,将评论总数除以每页数量(每页20个评论),其中ceil(x) 函数返回一个大于或等于 x 的的最小整数,显然就是向上取整的方法咯,举个栗子21个评论,计算得1.05页,不是1页,而应该是2页。

def get_max_page(new_comments):    # 计算出总子页数    print('=== ' + new_comments + ' ===')    max_page = new_comments.split('(')[1].split(')')[0]    # 每页显示 20 条评论    offset = 20    max_page = ceil(int(max_page) / offset)    print('一共有', max_page, '个子页')    return max_page

当然发现在页面跳转左边有一个总页数的标识,也能实现获取总页数的功能,空闲时间可以试试。

def go_nextpage(brower):    # 点击下一页 操作    next_button = brower.find_elements_by_xpath("//div[@class='m-cmmt']/div[3]/div[1]/a")[-1]    js = "var q=document.documentElement.scrollTop=10000"    brower.execute_script(js)    if next_button.text == '下一页':        next_button.click()

接下来就是抓取内容的核心代码:

def get_comments(is_first, brower):    # 获取评论内容    items = brower.find_elements_by_xpath("//div[@class='cmmts j-flag']/div[@class='itm']")    # 首页的数据中包含 15 条精彩评论, 20 条最新评论, 只保留最新评论    if is_first:        items = items[15: len(items)]    data_list = list()    data = dict()    for each in items:        # 用户 Id        userId = each.find_elements_by_xpath("./div[@class='head']/a")[0]        userId = userId.get_attribute('href').split('=')[1]        # 用户昵称      nickname = each.find_elements_by_xpath("./div[@class='cntwrap']/div[1]/div[1]/a")[0]        nickname = nickname.text        # 评论内容      content = each.find_elements_by_xpath("./div[@class='cntwrap']/div[1]/div[1]")[0]        content = content.text.split(':')[1]  # 中文冒号分割        # 点赞数      like = each.find_elements_by_xpath("./div[@class='cntwrap']/div[@class='rp']/a[1]")[0]        like = like.text        if like:           like = like.strip().split('(')[1].split(')')[0]        else:           like = '0'        # 头像地址        avatar = each.find_elements_by_xpath("./div[@class='head']/a/img")[0]        avatar = avatar.get_attribute('src')        data['userId'] = userId        data['nickname'] = nickname        data['content'] = content        data['like'] = like        data['avatar'] = avatar        cursor.executemany("INSERT INTO wy_single_mu (userId,nickname,content,likenum,avatar)VALUES(%s,%s,%s,%s,%s)",[(userId,nickname,content,like,avatar)])        conn.commit()        data_list.append(data)        data = dict()    return data_list

代码分别抓取歌单评论在那个的用户ID、用户昵称、评论内容、点赞数量、头像URL,注意抓取下来的数据可能包含一些其他字符,例评论中包含“:”,那就需要我们使用split分割取后面的内容了,边读取边存入mysql数据库,是不是看起来很Easy?

再附上主函数,注意连接数据库前要保证数据库服务开启,用户名及密码正确,数据库名及表名对应(注意user和passwd要根据自己的电脑设置):

if __name__ == '__main__':    url = '#/playlist?id=50'    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='********', db='music', charset='utf8mb4')    cursor = conn.cursor()    start_spider(url)    cursor.close()    conn.close()

有坑@有坑@有坑!存储数据库的过程有一个坑,细心的小伙伴可能发现我的connect参数中charset是utf8mb4,而不是常用utf-8,那是因为我之前使用utf-8存储过程中报错,错误来源于content的存储中,为了演示错误,我把代码改回utf-8给大家截图看看:

原因来自于用户评论有表情包,而表情包只能存储为\\xF0的格式,mysql不能识别4个字节的utf8编码的字符,解决办法:将对应字符类型换成将对应的数据类型改为utf8mb4类型,同时连接类型也要改成utf8mb4_general_ci,注意mysql中对应表和字段字符设置utf8mb4,且在python代码连接中charset也要设置。

看看数据库表:

三、jieba及词云制作

jieba 基于Python的中文分词工具,简单地描述一下特点:

支持三种分词模式:

精确模式,试图将句子最精确地切开,适合文本分析;

全模式,把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义;

搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。

支持繁体分词

支持自定义词典

附上官网:

接下来就是将评论内容装入文本文件,丢给词云处理分析,代码如下

import jiebafrom wordcloud import WordCloud, ImageColorGeneratorimport numpyimport PIL.Image as Imagestopwords = {'噪声':0,'噪声':0,'噪声':0,}def chinese_jieba(text):    #只有词语    wordlist_jieba = jieba.cut(text)    #因为wordlist_jieba是list    text_jieba = ''.join(wordlist_jieba)    return text_jiebawith open ('contents_music.txt') as fp:    text = fp.read()    text = chinese_jieba(text)    mask_pic = numpy.array(Image.open('ali.png'))    wordcloud = WordCloud(font_path='KAI_GB2312.TTF',background_color='white',max_font_size=130,mask=mask_pic,stopwords=stopwords,max_words=30).generate(text)    #用图片色    image_colors = ImageColorGenerator(mask_pic)    wordcloud.recolor(color_func=image_colors)    #生成图    image = wordcloud.to_image()      image.show()

我们还可以对噪声词进行设置,虽然我没有设置(其实就是懒),大致看看效果,看这个歌单总体效果还可以,有空再去试听,另外我们可以选择使用图片色或者不使用、原图:

本文的基本内容已经大致写完了,小伙伴可以自己找喜欢的图(不要选择主体浅色的图片,比如北极熊,那么高频词会覆盖周围),找自己喜欢的内容去抓高频词,之前我曾用BeautifulSoup多线程爬取拉勾网的java工程师的任职要求,发现了Radius,Spring,数据库等等高频词,看来还有很多知识要学,继续加油!

推文发布5日内同步代码至:

本文作者目前就读研究生,学术尚浅和缺乏实战经验,若码字有误等,还望读者、老师们批评指正!