Python爬取网易云音乐榜单歌曲高赞评论

廖雪峰官网学习完Python之后,花了两天时间在网易云课堂找了个课程进行了爬虫入门学习。所以接下来,想找一个网页爬一爬,实战一下。可能代码有些地方比较臃肿,但总的来说功能实现了。

首先说需求:爬取网易云音乐首页的三个榜单中,每个榜单中排名前10歌曲的赞数超1000的评论。包括歌名,评论者昵称,获赞数,评论内容。

首先从获取每一首歌的评论开始:

1.对于静态页面,学习完网易云课堂这个课之后应该就可以处理了。但爬虫抓取数据时有些数据是动态数据,例如用js动态加载的,使用普通的requests 是抓不到相关数据的,明明在浏览器里有相应的信息,但是在requests抓取的时候却发现获得的html缺失了太多数据,这是因为网页用js异步加载数据,再动态显示出来。一种处理方式是找出相应的js接口,但是有时这是非常难得,因为还得分析js的调用参数,而有些参数是有加密的,还得进行解密操作;另一种出来方式是用selenium。下面先用第一种方法:

import requests
import json
import os


def getcommets(id_song, name_song, listtype):
    url = 'https://music.163.com/weapi/v1/resource/comments/R_SO_4_' + id_song + '?csrf_token='
    header = {  # 请求头部
        'User-Agent': 'Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
    }
    data = {  # 表单信息,服务器根据表单信息的正确与否,决定是否发送数据。
        'params': 'N+zv4smEmpA+qRJcbrvXQiaVifgDskCoylBFjGqvxojjHciXzycxTE3xa50Ec//+6OdpTuxmgWXjty/gMoudXLFnQVnzKDuWGuHqEme1861T7R5x8L/hwaKgsMS4zQ0t7scgEJs46HbQFSSzcGA1teqRumGen4C3Xhv8jIyvJsmJTaknNx53tjhDxeV8xo0p',
        'encSecKey': 'd0a60f2145711ddf491d4c946d1003662399c1f10f2c4392f49e5cbdc9ee276b06e7af2e5eb354f613a25902ef36ba4f04ded9efc64a523ba6f7a634dbfe7c06f661577e84523174ab62ddd768992e4cc6101683aabce48e8ad82aac05573b1ac3723e678476e9400fe6ec972096b087795bb17a066e61efd74be2412afbd6fb'}
    r = requests.post(url, data=data, headers=header)
    comments = json.loads(r.text)
    our_comment = list(filter(lambda s: s.get('likedCount') >= 1000, comments.get('hotComments')))
    print(our_comment)

    filedir1 = os.path.join(os.getcwd(), '网易云三榜歌曲热评')
    if not os.path.exists(filedir1):
        os.mkdir(filedir1)
    filedir2 = os.path.join(filedir1, listtype)  # 路径拼接最好用.join(),因为拼接内容会因操作系统而变化
    if not os.path.exists(filedir2):
        os.mkdir(filedir2)
    with open(os.path.join(filedir2, '%s.txt' % name_song), mode='w', encoding='utf-8') as f:
        f.write('《' + name_song + '》' + '\n')
        for x in our_comment:
            f.write(x.get('user').get('nickname') + ':' + '      获赞数,' + str(x.get('likedCount')) + '\n' + x.get(
                'content') + '\n' + '------------------------------------------------------' + '\n')

这里首先用到了https://blog.csdn.net/fengxinlinux/article/details/77950209提供的方法。这里重点就是找到了获取评论信息的POST请求的url,但如果想通过此url获取评论,需要提交相应的表单(<form>标签)信息,这里的表单信息是加密的,这里原作者经过细致的观察,采用了一个偷懒 的方法来实现。

2.接下来是id_song, name_song, listtype(每首歌的id值,歌曲名称,所属榜单)信息的获取

这里用到了Selenium,首先要进行安装:

没有安装anaconda的:这个方法不推荐,因为如果电脑中同时安装了python2.x和python3.x,那这样安装的Selenium默认安装到python2.x。而我是在python3.x下编程的,所以要费时处理这个问题。

$ sudo pip install selenium

安装了anaconda的:这样安装省心

$ conda install selenium

但是出现了下列问题:

CondaIOError: Missing write permissions in: /usr/local/anaconda3
#
# You don't appear to have the necessary permissions to install packages
# into the install area '/usr/local/anaconda3'.
# However you can clone this environment into your home directory and
# then make changes to it.
# This may be done using the command:
#
# $ conda create -n my_root --clone="/usr/local/anaconda3"
 

问题在于 anaconda所在文件夹只有root用户才有权限,而 root下并没有conda命令,解决方案是修改文件夹权限给当前用户,其中usr是你的用户名,foldername是anaconda安装路径/usr/local/anaconda3:

$ sudo chown -R usr foldername

至此,Selenium的安装就结束了。然后为了使Selenium能够驱动一个浏览器(这里我们选择了chrome),需要安装相应的驱动chromedriver,要注意与自己的chrome版本相匹配。这里的最新版本是:2.41,可别搞错了。

安装chromedriver步骤:

切换到下载的目录,解压下载的.zip文件

$ unzip chromedriver_linux64.zip 

转移解压后的文件:

$ sudo mv chromedriver /usr/bin/

切换到root用户,进入/usr/bin/,给刚刚的文件添加权限:

# chmod +x chromedriver

至此,Selenium的安装就结束了。在web应用中,前台网页的设计一般会用到iframe/frame表单嵌套页面的应用,这种异步加载方式。简单的说就是一个页面签嵌套多个HEML/JSP文件。selenium webdriver  只能在同一页面识别定位元素,可以狭隘的理解成只能识别当前所在位置的页面上的元素。对于不同的iframe/frame表单中的元素是无法直接定位的,requests模块也是束手无策,对此类页面解析,基本上什么数据都得不到。此时,需要结合driver.switch_to.frame('g_iframe')方法切换到指定的frame/iframe中。driver.switch_to.frame('g_iframe')函数参数默认的是取表单的ID或name属性。如果没有id和name ,可通过Xpath路径定位。

from selenium import webdriver
from bs4 import BeautifulSoup
from getcomment import getcommets


def getmusics(id_toplist):
    url = 'https://music.163.com/#' + id_toplist
    driver = webdriver.Chrome()  # 用webdriver打开chrome
    driver.get(url)  # 打开对应网址
    driver.switch_to.frame('contentFrame')  # 将当前driver转入到相应的iframe中去
    # 这里面的内容,用request模块是解析不到的
    html = driver.page_source  # 获得当前iframe所包含的源码
    soup = BeautifulSoup(html, 'lxml')  # 利用美丽汤进行一些列  定位、获取信息  处理
    listType = soup.find('title').text.split(' ')[0]  # 是为了截取比较好看的榜单名字
    songs = soup.body.findAll(lambda tag: tag.name == 'tr' and tag.has_attr('id'))[:10]  # 取榜单中前10名歌曲
    keyinfo = [x.find('span', class_='txt') for x in songs]
    songid = [x.find('a').attrs['href'].lstrip('/song?id=') for x in keyinfo]
    songname = [str(x.find('b').attrs['title']) for x in keyinfo]
    i = 0
    while i < len(songs):
        getcommets(songid[i], songname[i], listType)
        i=i+1

可以看到,这里程序在运行过程中,要频繁的打开浏览器新窗口去加载相应页面,所以为了不必要的麻烦,可以使用Selenium+Phantomjs的模式,来进行动态页面的爬虫工作。

d3.接下来是从https://music.163.com分别进入三个榜单的操作:

from selenium import webdriver
from bs4 import BeautifulSoup
from getmusic import getmusics


def getlists(url):
    drivier = webdriver.Chrome()
    drivier.get(url)
    drivier.switch_to.frame('g_iframe')
    html = drivier.page_source
    soup = BeautifulSoup(html, 'lxml')
    link = soup.findAll('div', class_='cver u-cover u-cover-4')
    links = [x.find('a').attrs['href'] for x in link]
    for x in links:
        getmusics(x)


getlists('https://music.163.com')

最后执行程序,得到正确的结果:

《不染 - (电视剧《香蜜沉沉烬如霜》主题曲)》
武装心碎:      获赞数,72315
海雷老师所作歌词有古风韵味,简老师作曲丝丝入扣,毛不易的醇厚嗓音,带着不符合他年纪的沧桑,诠释着词曲里的爱别离,放不下。独特的搭配,竟如此刚好,如斯惊艳。不禁在想,不染,是不沾染,还是一尘不染……希望初心不染杂质,期盼爱情不染功利。若有幸携手,于这孤独世间行走,愿你青春,黑发不染
------------------------------------------------------
有味是清欢_:      获赞数,46207
昨天听到了《不染》的试听版,惊艳毛不易的高音。可能许多人都停留在他在《消愁》《像我这样的人》一系列歌中的低吟浅唱,其实毛不易在《imagine》《请记住我》的高音也非常棒!感谢作词作曲老师们让我们听到这么好听的歌,配合电视剧一定超棒!感谢毛不易献唱,期待他越来越好。少年未来可期[爱心]
------------------------------------------------------

猜你喜欢

转载自blog.csdn.net/Saltwind/article/details/82670189