天天看點

python爬網易雲音樂評論最多的歌_使用Python爬一爬網易雲音樂上那些評論火爆的歌曲...

網易雲音樂這款音樂APP本人比較喜歡,使用者量也比較大,而網易雲音樂之是以使用者衆多和它的歌曲評論功能密不可分,很多歌曲的評論非常有意思,其中也不乏很多感人的評論。但是,網易雲音樂并沒有提供熱評排行榜和按評論排序的功能,沒關系,本文就使用爬蟲給大家爬一爬網易雲音樂上那些熱評的歌曲。

結果

對過程沒有興趣的童鞋直接看這裡啦。

python爬網易雲音樂評論最多的歌_使用Python爬一爬網易雲音樂上那些評論火爆的歌曲...

評論數大于五萬的歌曲排行榜

首先恭喜一下我最喜歡的歌手(之一)周傑倫的《晴天》成為網易雲音樂第一首評論數過百萬的歌曲!

通過結果發現目前評論數過十萬的歌曲正好十首,通過這前十首發現:

薛之謙現在真的很火啦~

幾乎都是男歌手啊,男歌手貌似更受歡迎?(别打我),男歌手中周傑倫、薛之謙、許嵩(這三位我都比較喜歡)幾乎占了榜單半壁江山...

《Fade》電音強勢來襲,很帶感哈(搭配炫邁寫代碼完全停不下來..)

根據結果做了網易雲音樂歌單 :

提示: 評論數過五萬的歌曲 歌單中個别歌曲由于版權問題暫時下架,暫由其他優秀版本代替。

高能預警:TOP 29 《Lost Rivers》請慎重播放,如果你堅持播放請先看評論...

過程

爬取歌曲的ID

通過觀察歌曲詳情頁的URL,我們發現隻要爬取到對應歌曲的ID就可以得到它的詳情頁URL,而歌曲的資訊都在詳情頁。由此可知隻要收集到所有歌曲的ID那麼就可以得到所有歌曲的資訊啦。而這些ID要從哪裡爬呢?從歌單裡爬,而歌單在哪爬呢?通過觀察歌單頁的URL我們發現歌單也有ID,而歌單ID可以從歌單分類頁中爬,好了就這樣爬最終就能收集到所有歌曲的ID了。

通過爬取評論數篩選出符合條件的歌曲

python爬網易雲音樂評論最多的歌_使用Python爬一爬網易雲音樂上那些評論火爆的歌曲...

很遺憾的是評論數雖然也在詳情頁内,但是網易雲音樂做了防爬處理,采用AJAX調用評論數API的方式填充評論相關資料,由于異步的特性導緻我們爬到的頁面中評論數是空,那麼我們就找一找這個API吧,通關觀察XHR請求發現是下面這個家夥..

python爬網易雲音樂評論最多的歌_使用Python爬一爬網易雲音樂上那些評論火爆的歌曲...
python爬網易雲音樂評論最多的歌_使用Python爬一爬網易雲音樂上那些評論火爆的歌曲...

響應結果很豐富呢,所有評論相關的資料都有,不過經過觀察發現這個API是經過加密處理的,不過沒關系...

爬取符合條件的歌曲的詳細資訊(名字,歌手等)

這一步就很簡單了,觀察下歌曲詳情頁的HTML很容易就能爬到我們要的名字和歌手資訊。

源碼

# encoding=utf8

import requests

from bs4 import BeautifulSoup

import os, json

import base64

from Crypto.Cipher import AES

from prettytable import PrettyTable

import warnings

warnings.filterwarnings("ignore")

BASE_URL = 'http://music.163.com/'

_session = requests.session()

# 要比對大于多少評論數的歌曲

COMMENT_COUNT_LET = 100000

class Song(object):

def __lt__(self, other):

return self.commentCount > other.commentCount

# 由于網易雲音樂歌曲評論采取AJAX填充的方式是以在HTML上爬不到,需要調用評論API,而API進行了加密處理,下面是相關解決的方法

def aesEncrypt(text, secKey):

pad = 16 - len(text) % 16

text = text + pad * chr(pad)

encryptor = AES.new(secKey, 2, '0102030405060708')

ciphertext = encryptor.encrypt(text)

ciphertext = base64.b64encode(ciphertext)

return ciphertext

def rsaEncrypt(text, pubKey, modulus):

text = text[::-1]

rs = int(text.encode('hex'), 16) ** int(pubKey, 16) % int(modulus, 16)

return format(rs, 'x').zfill(256)

def createSecretKey(size):

return (''.join(map(lambda xx: (hex(ord(xx))[2:]), os.urandom(size))))[0:16]

# 通過第三方管道擷取網雲音樂的所有歌曲ID

# 這裡偷了個懶直接從http://grri94kmi4.app.tianmaying.com/songs爬了,這哥們已經把官網的歌曲都爬過來了,省事不少

# 也可以使用getSongIdList()從官方網站爬,相對比較耗時,但更準确

def getSongIdListBy3Party():

pageMax = 1 # 要爬的頁數,可以根據需求選擇性設定頁數

songIdList = []

for page in range(pageMax):

url = 'http://grri94kmi4.app.tianmaying.com/songs?page=' + str(page)

# print url

url.decode('utf-8')

soup = BeautifulSoup(_session.get(url).content)

# print soup

aList = soup.findAll('a', attrs={'target': '_blank'})

for a in aList:

songId = a['href'].split('=')[1]

songIdList.append(songId)

return songIdList

# 從官網的 發現-> 歌單 頁面爬取網雲音樂的所有歌曲ID

def getSongIdList():

pageMax = 1 # 要爬的頁數,目前一共42頁,爬完42頁需要很久很久,可以根據需求選擇性設定頁數

songIdList = []

for i in range(1, pageMax + 1):

url = 'http://music.163.com/discover/playlist/?order=hot&cat=全部&limit=35&offset=' + str(i * 35)

url.decode('utf-8')

soup = BeautifulSoup(_session.get(url).content)

aList = soup.findAll('a', attrs={'class': 'tit f-thide s-fc0'})

for a in aList:

uri = a['href']

playListUrl = BASE_URL + uri[1:]

soup = BeautifulSoup(_session.get(playListUrl).content)

ul = soup.find('ul', attrs={'class': 'f-hide'})

for li in ul.findAll('li'):

songId = (li.find('a'))['href'].split('=')[1]

print '爬取歌曲ID成功 -> ' + songId

songIdList.append(songId)

# 歌單裡難免有重複的歌曲,去一下重複的歌曲ID

songIdList = list(set(songIdList))

return songIdList

# 比對歌曲的評論數是否符合要求

# let 評論數大于值

def matchSong(songId, let):

url = BASE_URL + 'weapi/v1/resource/comments/R_SO_4_' + str(songId) + '/?csrf_token='

headers = {'Cookie': 'appver=1.5.0.75771;', 'Referer': 'http://music.163.com/'}

text = {'username': '', 'password': '', 'rememberLogin': 'true'}

modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'

nonce = '0CoJUm6Qyw8W8jud'

pubKey = '010001'

text = json.dumps(text)

secKey = createSecretKey(16)

encText = aesEncrypt(aesEncrypt(text, nonce), secKey)

encSecKey = rsaEncrypt(secKey, pubKey, modulus)

data = {'params': encText, 'encSecKey': encSecKey}

req = requests.post(url, headers=headers, data=data)

total = req.json()['total']

if int(total) > let:

song = Song()

song.id = songId

song.commentCount = total

return song

# 設定歌曲的資訊

def setSongInfo(song):

url = BASE_URL + 'song?id=' + str(song.id)

url.decode('utf-8')

soup = BeautifulSoup(_session.get(url).content)

strArr = soup.title.string.split(' - ')

song.singer = strArr[1]

name = strArr[0].encode('utf-8')

# 去除歌曲名稱後面()内的字,如果不想去除可以注掉下面三行代碼

index = name.find('(')

if index > 0:

name = name[0:index]

song.name = name

# 擷取符合條件的歌曲清單

def getSongList():

print ' ##正在爬取歌曲編号... ##'

# songIdList = getSongIdList()

songIdList = getSongIdListBy3Party()

print ' ##爬取歌曲編号完成,共計爬取到' + str(len(songIdList)) + '首##'

songList = []

print ' ##正在爬取符合評論數大于' + str(COMMENT_COUNT_LET) + '的歌曲... ##'

for id in songIdList:

song = matchSong(id, COMMENT_COUNT_LET)

if None != song:

setSongInfo(song)

songList.append(song)

print '成功比對一首{名稱:', song.name, '-', song.singer, ',評論數:', song.commentCount, '}'

print ' ##爬取完成,符合條件的的共計' + str(len(songList)) + '首##'

return songList

def main():

songList = getSongList()

# 按評論數從高往低排序

songList.sort()

# 列印結果

table = PrettyTable([u'排名', u'評論數', u'歌曲名稱', u'歌手'])

for index, song in enumerate(songList):

table.add_row([index + 1, song.commentCount, song.name, song.singer])

print table

print 'End'

if __name__ == '__main__':

main()

友情提示:随着網易雲音樂網站結構、接口、加密方式的更換本代碼可能并不能很好的工作,不過過程和原理都是一樣的,這裡也隻是給大家分享一下這一過程啦。這裡有個Java語言的實作教程不過源碼不全,有興趣的可以看看,本文代碼中的getSongIdListBy3Party()就是從這哥們的結果中爬的。