本篇涉及知識:
1、request第三方庫的基本使用
2、json解析
本篇目標:
爬取指定一個歌單的所有歌曲的熱門評論
(注:本篇爬取不直接解析html文本,而是直接分析獲取評論的api,從而獲得相應的json返回。然后,解析json獲取所需信息。)
踩坑記錄:
1、換行符號“\”導致一些未知問題,慎用換行符號“\”
分析api
- 我們首先用瀏覽器打開網易云音樂的網頁版,隨便進入一個歌單,點擊進入一首歌曲的頁面,可以看到下面有評論。接著F12進入開發者控制臺(審查元素)。
我們在搜索框里輸入comments即可找到對應的獲取評論的api的url,點擊它在右邊選擇Response就可以看到返回的json了。
那我們的思路就很清晰了,只需要分析這個api并模擬發送請求,獲取json進行解析就好了。右鍵復制這個url下來:
http://music.163.com/weapi/v1/resource/comments/R_SO_4_465920636?csrf_token=4377e7fd9c7eede75d669c60a951fbed
從瀏覽器的上的地址可以發現以上url里R_SO_4_后的數字就是歌曲的id,如下圖:
經測試,后面的csrf_tocken也可以用于其他歌曲。在開發者控制臺里,點擊headers就可以看到請求方式為post,請求頭里的表單數據有兩個加過密的參數(params和encSecKey)。但是不要緊,同樣經測試,這兩個參數也可以用于其他歌曲,直接copy就好。但只限于第一頁,其他頁碼就不一樣了,不過對于我們爬取熱門評論,第一頁就夠了。
發送請求獲取json
根據上個小節的分析,我們可以寫出以下代碼獲取到json:
import requests
import json
url='http://music.163.com/weapi/v1/resource/comments/R_SO_4_465920636?csrf_token=c0f6bfdcd0526ec0ba6c207051a08960'
param={'params':'wxLqdGgw16OHb6UwY/sW16VtLqAhGaDMeI2F4DaESDplHA+CPsscI4mgiKoVCPuWW8lcd9eY0YWR/iai0sJqs0NmtLubVCkGdpN\
TN3mLhevZpdZy/XM1+z7L18InFz5HbbRkq230i0aOco/3jVsMWcD3/tzzOCLkGuu5xdbo99aUjDxHwDSVfu4pz4spV2KonJ47Rt6vJhOorV7LfpIVmP/qe\
ZghfaXXuKO2chlqU54=',\
'encSecKey':'12d3a1e221cd845231abdc0c29040e9c74a47ee32eb332a1850b6e19ff1f30218eb9e2d6d9a72bd797f75\
fa115b769ad580fc51128cc9993e51276043ccbd9ca4e1f589a2ec479ab0323c973e7f7b1fe1a7cd0a02ababe2adecadd4ac93d09744be0deafd1eef\
0cfbc79903216b1b71a82f9698eea0f0dc594f1269b419393c0'}#這里每行末尾的‘\’是代碼過長用來換行的,慎用,換行多了易出現bug。
r=requests.post(url, param)
data=r.text#data得到的就是json
print data
運行查看輸出就可以知道是否成功獲取了json。這里requests的用法,可以參考requests快速上手。
解析json進行輸出
我們可以從瀏覽器的開發者控制臺里把json復制到一個 在線json校驗格式化工具,這樣可以比較清晰地看到json的結構,利于我們解析。如圖:
這個json里有豐富的信息,包括評論總數、用戶名、熱評、點贊數等。清楚了json 的結構,很容易就可以解析得到想要的信息了。json解析需要引入json包,了解json解析可以參考 使用python解析json詳解。里面把json類型和python類型之間的對應關系講得很清楚了,只需要會用dict和list。解析代碼如下:
import requests
import json
url='http://music.163.com/weapi/v1/resource/comments/R_SO_4_465920636?csrf_token=c0f6bfdcd0526ec0ba6c207051a08960'
param={'params':'wxLqdGgw16OHb6UwY/sW16VtLqAhGaDMeI2F4DaESDplHA+CPsscI4mgiKoVCPuWW8lcd9eY0YWR/iai0sJqs0NmtLubVCkGdpN\
TN3mLhevZpdZy/XM1+z7L18InFz5HbbRkq230i0aOco/3jVsMWcD3/tzzOCLkGuu5xdbo99aUjDxHwDSVfu4pz4spV2KonJ47Rt6vJhOorV7LfpIVmP/qe\
ZghfaXXuKO2chlqU54=',\
'encSecKey':'12d3a1e221cd845231abdc0c29040e9c74a47ee32eb332a1850b6e19ff1f30218eb9e2d6d9a72bd797f75\
fa115b769ad580fc51128cc9993e51276043ccbd9ca4e1f589a2ec479ab0323c973e7f7b1fe1a7cd0a02ababe2adecadd4ac93d09744be0deafd1eef\
0cfbc79903216b1b71a82f9698eea0f0dc594f1269b419393c0'}#這里每行末尾的‘\’是代碼過長用來換行的
r=requests.post(url, param)
data=r.text
jsob=json.loads(data)#加載獲取的json數據,獲得json對象
hotComments=jsob['hotComments']
for i in range(len(hotComments)):
user_nickname=hotComments[i]['user']['nickname']
likedCount=hotComments[i]['likedCount']
content=hotComments[i]['content']
print u'評論'+str(i+1)+u' 用戶名:'+user_nickname+u" 喜歡:"+str(likedCount)
print '------------------------------------------------------------------'
print content
print '------------------------------------------------------------------'
print '\n'
輸出結果:
爬取一個歌單所有歌的熱門評論
以上已經展示了怎么爬取一首歌的熱門評論,接下來我們就可以進一步把一個歌單里所有歌的熱門評論都爬取出來。
思路就是,將這個歌單所有歌曲的id爬取出來,替換到之前的url中,然后進行同樣的輸出。
同樣的方法,我們發送歌單的url訪問請求,讀取response的返回內容看看情況。
url='http://music.163.com/playlist?id=700734626'
r=requests.get(url)
print r.text
輸出:
可以看到第一行的標簽textarea里有一個json,我們可以用xpath定位到這個標簽,獲取到這個json并進行解析。在此之前,可以向之前一樣先復制到在線校驗工具看看結構。
確實有我們需要的數據,接下來就可以進行解析了。
我們這里寫成面向對象的風格,完整代碼如下:
import requests
import json
from lxml import html
class CrawlMusic163:
#獲取json解析出所有歌曲對象,返回歌曲的list
def getSongs(self,id):#這里的id是歌單的id
url='http://music.163.com/playlist?id='+str(id)
r=requests.get(url)
tree=html.fromstring(r.text)
data_json=tree.xpath('//textarea[@style="display:none;"]')[0].text
songs=json.loads(data_json)
return songs
#獲取每首歌的熱門評論的list
def getHotComments(self,id):#這里的id是歌曲的id
url='http://music.163.com/weapi/v1/resource/comments/R_SO_4_'+str(id)+'?csrf_token=c0f6bfdcd0526ec0ba6c207051a08960'
param={'params':'wxLqdGgw16OHb6UwY/sW16VtLqAhGaDMeI2F4DaESDplHA+CPsscI4mgiKoVCPuWW8lcd9eY0YWR/iai0sJqs0NmtLubVCkG\
dpNTN3mLhevZpdZy/XM1+z7L18InFz5HbbRkq230i0aOco/3jVsMWcD3/tzzOCLkGuu5xdbo99aUjDxHwDSVfu4pz4spV2KonJ47Rt6vJhOorV7LfpIVmP/qeZghfaXXuKO2chlqU54=',\
'encSecKey':'12d3a1e221cd845231abdc0c29040e9c74a47ee32eb332a1850b6e19ff1f30218eb9e2d6d9a72bd797f75fa115b769ad580fc51128cc9993e51276043ccbd9ca4e1f589a2ec479ab0323c973e7f7b1fe1a7cd0a02ababe2adecadd4ac93d09744be0deafd1eef0cfbc79903216b1b71a82f9698eea0f0dc594f1269b419393c0'}
r =requests.post(url,param)
data=r.text
jsob=json.loads(data)#加載獲取的json數據,獲得json對象
hotComments=jsob['hotComments']
return hotComments
crawl_music_163=CrawlMusic163()
songs=crawl_music_163.getSongs(700734626)#700734626是某個歌單的id,在網頁版進入歌單后可以在url末尾獲取
for song in songs:
hotComments=crawl_music_163.getHotComments(song['id'])#按id獲取該歌曲的熱門評論list
#輸出每首歌的歌手名字-歌名-熱門評論數
print song['artists'][0]['name']+"-"+song['name']+u"-熱門評論:"+str(len(hotComments))
print '########################################################################'
#每首歌循環輸出所有熱門評論
for i in range(len(hotComments)):
user_nickname=hotComments[i]['user']['nickname']
likedCount=hotComments[i]['likedCount']
content=hotComments[i]['content']
print u'評論'+str(i+1)+u' 用戶名:'+user_nickname+u" 喜歡:"+str(likedCount)
print '------------------------------------------------------------------'
print content
print '------------------------------------------------------------------'
print '\n'
print '########################################################################'
輸出結果展示:
(注:這里遇到一個神坑,由于param太長,我用了幾個""符號在末尾進行換行,如上代碼那樣換了兩行沒有問題,可是只要我在encSecKey參數這一行進行換行,就會產生bug,死活獲取不到json文本。經過多次測試,確實就是""符號導致param沒能正確地傳入post導致獲取不到json文本。目前不清楚什么原因,所以,這個換行還是慎用。)