Scrapy爬取網易雲音樂和評論---轉

開發環境:WIN7+Anaconda+py2.7+scrapy
數據庫:MongoDB
文章的順序:
一、先分析思路;
二、再分析scrapy框架每一個模塊的做用;
三、最後寫代碼和分析API,以及評論javascript

Scrapy爬取網易雲音樂和評論(1、思路分析)

 

1、咱們先分析歌手,有兩種方法:

方法一:遍歷html

優勢:有個別歌手有主頁,可是沒有申請音樂人,因此不存在歌單列表頁,用第二種方法也獲取不到。
缺點:很差測試它到底有多少,大概十一二萬的樣子,大多id是相隔不遠的。有些id之間相隔了幾位數,原本挺穩定的,想着往4位數遍歷就行,卻發現還有7位數的,這樣遍歷的跨度有些大,(id從1872開始),要作些處理,還有判斷這個頁面存在與否。java

方法二:從歌手分類爬取全部歌手的idpython

歌手分類頁http://music.163.com/#/discover/artist
這裏要說一下,網易雲的全部網址,要去掉中間那個#號纔是真正的url,帶#的查看源代碼是獲取不到真正的信息的。
因此實際上是:http://music.163.com/discover/artistgit

優勢:方便,不須要考慮遍歷的數量,不須要對頁面是否存在作處理
缺點:可能會漏掉一些有主頁但未註冊的歌手。github

咱們主要以方法二入手,分析以下:mongodb

咱們看這個頁面左側欄:數據庫


 
圖一

二、由於當時我寫的時候,參考到前面提到的那篇GitHub上的代碼,
這個group_ids裏的就是左側每一個項對應全部的頁面了(不包括最上方的推薦歌手和入駐歌手,由於包含在其餘裏面了)json

 
圖二 左欄裏的id

三、咱們按F12或右鍵檢查,如圖,每一個對應的url是:http://music.163.com/discover/artist/cat?id=xxx
這裏的id就是上面group_ids裏的數字了。api

 
圖三

四、而後咱們再點進去,如圖四,url的id就是上面這個group_ids裏的元素了,然後面的initial是首字母的意思,你看下面咱們選中的是A,而後它是65,是否是想到ASCII碼?在ASCII碼中A就是從65開始的,Z是90,後面以此類推,最後有個其餘,代替的是0:

 
圖四

咱們將這兩個分別存儲爲一個列表或元組:

# 左側欄全部:男女、國家分類id group_ids = (1001, 1002, 1003, 2001, 2002, 2003, 6001, 6002, 6003, 7001, 7002, 7003, 4001, 4002, 4003) # 歌手姓名首字母id initials = [i for i in range(65,91)] + [0] 

2、歌手頁

一、點進來以後咱們來到歌手頁,http://music.163.com/#/artist?id=6452,一樣,查看源代碼的時候去掉url裏的#

二、咱們獲取的這個歌手頁的url對應的是熱門50首,在對應網頁裏咱們會發現下面有好幾個塊:熱門50首專輯MV歌手介紹

 
圖五

三、由於受框架的限制,以上四個信息的內容不在一個傳遞鏈裏,
如下兩種順序的特色都是後者傳入的參數都是由前者返回的,而這四個之間屬於相同的id,他們並不須要由前者返回,不構成一個傳遞鏈:

1)、歌手 ——>專輯列表——>歌曲列表——>歌曲信息——>第5
2)、歌手 ——>熱門50首的歌曲列表——>歌曲信息——>第4

四、若是若是你只須要熱門歌曲你能夠獲取它全部連接,這個代碼被我分爲兩塊:
1)、第一塊是包含熱門50首的url,也只有url,在id名爲'song-list-pre-cache'的div標籤裏,div->ul->li->a->href
2)、而第二塊textarea裏是json,是這50首歌的比較完整的信息,只不過,這些信息經過lxml.etree或者BeautifulSoup用text的方式獲取下來會是字符串,咱們須要用json將它格式化。
若是你只須要歌曲的話,選擇第一條就行了,直接跳到第四篇講API的),用歌曲的API便可。

 
圖六 熱門50首歌曲的全部連接或者json

五、咱們要獲取全部歌手的歌曲,就得從歌手的專輯下手,獲取專輯裏全部的歌手才行。咱們在專輯頁會發現,有些是有不少頁的,我最開始用的是scrapy的xpath解析頁面,後來搜的時候發現了API,因此接下來的東西,咱們就不經過頁面的方式了,API我是經過這個網站發現的:http://moonlib.com/606.html(最近發現網站掛了,請看個人第四篇講API的,有其餘相似API的文章連接)。

咱們用到的是2到6(不包括5,沒用到歌單),第7條接口是MV的,不過不幸沒有發現像專輯同樣的列表頁信息,它只有單曲的MV的API。不過這裏咱們用不上。後面第四篇會專門分析API。

 
圖七

六、接下來就是每一個專輯的全部歌曲還有專輯、歌手的一些信息,另外專輯下也有評論,且評論數的獲取方式有些不一樣,所以評論有兩種處理。

 
圖八 專輯信息、歌曲列表

七、最後從圖八里的歌曲連接點進去的就是歌曲頁了,如圖九:

 
圖九

2、Scrapy框架每一個模塊的做用

關於如何創建一個scrapy程序,能夠參考這兩篇文章:

一、http://cuiqingcai.com/3472.html(建立的時候推薦)
二、http://www.cnblogs.com/wuxl360/p/5567631.html

關於使用mongodb,能夠參考:

一、http://www.jianshu.com/p/30408d8ad1c0

1、創建的命令:

scrapy startproject + 你的項目名

第一篇文章有提到兩個比較特別且有用的地方:

 

一、
 
entrypoint.py

解釋一下:execute裏面的三個字符串連起來它其實就是最後執行scrapy程序的命令。這個文件的好處是,假若你在使用編輯器,好比sublime,是能夠在配置後直接執行的,而不用打開DOS窗口而後執行,若是你在sublime裏直接執行scrapy自己的任何一個文件,它都不會執行成功,而只能執行這個entrypoint.py,名字應該隨意吧,無所謂。

另外一點請參考如下的第三部分

2、框架結構

如今整個框架的結構是這樣的:


 
項目結構

固然,這個spiders文件夾下的WangYiYun.py並非自動生成的,這個須要咱們本身創建,這個文件就是主爬蟲程序。

另外,這個腳本的名字建議不要取和項目名同名,不然後面可能會踩坑。如下簡稱WYY.py,免得出錯,我由於已經生成了,改了別的地方又會出錯,解決辦法是在代碼的最前面,編碼註釋的後面加上這麼一句(參考連接找不到了,可是參考GitHub的連接代碼裏也有):

from __future__ import absolute_import 
  • entrypoint.py:執行程序,就想象是run/python xx.py
  • items.py:若是你學過orm的話,會很好的理解它,它至關於數據庫的字段。
  • middlewares.py:這個是個中間件,我也不知道它是作什麼的,由於並無用到。且不少例子上顯示的結構沒有它,基本上咱們也不會改到它。
  • pipelines.py:這個就是定義存儲的文件,好比鏈接,使用哪一個數據庫存儲。
  • settings.py:一看就知道,是配置文件。

3、關於setting配置:

一、關於調試

上面的緣由和配置解釋的很清楚,


 
來自http://cuiqingcai.com/3472.html

二、關於spidername和robots.txt

 
spidername and robots.txt

 

BOT_NAME很重要,在WYY.py文件裏寫腳本的時候,繼承自scrapy.Spider的這個類,它須要有一個name,而這二者必須同名。

 
spidername

最下面那行的ROBOTSTXT_OBEY,你們知道爬蟲繞不開robots.txt這個文件,每一個網站都會有這個網站,是必須遵照的一個守則吧,就是有些不讓你爬,有些又容許你爬。默認是True,若是失敗了,能夠嘗試將其註釋,而後複製一行,改成False。

settings.py文件裏大多都是寫好的,你只要將它複製,取消註釋,而後修改便可,最好不要不復制直接在原文上改,萬一改到了什麼出了錯,還能有個參照物。

三、關於headers

 
Headers

 

重要的通常就是Referer、User-Agent(這個必需要有)、Accept(可選,可是涉及到xhr,即json文件,就要修改了)。
這裏將它註釋,改爲本身的,你也能夠寫在主爬蟲WYY.py文件裏另寫,比較自由,寫在這裏算是一個基本配置吧。

四、關於ITEM_PIPELINES

 
ITEM_PIPELINES

 

這個是啓用一個Item Pipeline組件,數字表明優先級,越小越優先,沒有註釋的那行是個人,而下面還有一行,是我以前在網上看過的一種寫法,可是並不能成功,它應當是一個字典,列表不行

五、關於mongodb配置
隨便寫在哪,咱們就寫在剛剛ITEM_PIPELINES的後面

 
mongodb

 

這裏順便建議,常量都用大寫。
HOST是本地,PORT是端口,DBNAME是數據庫,WYY。
接下來四個是集合了,至關於table,這個順序是倒序。

一、MONGODB_COL_ARTIST - > ArtistInfo -> 全部的歌手列表
二、MONGODB_COL_ALBUMLIST - >AlbumListInfo - > 每一個歌手的全部專輯列表
三、MONGODB_COL_ALBUM - >AlbumInfo - > 每張專輯內的全部歌曲列表
四、MONGODB_COL_SONG - > SongInfo -> 每首歌曲的信息

4、關於items.py

它就至關於SQL/MySQL裏的字段,它沒有什麼特別的字段類型,反正全部都是scrapy.Field()就能夠了,另外三個集合一樣,每一個單獨寫個類,依照大家本身的需求定字段便可。

 
items.py

5、關於pinelines.py

 
pinelines.py

切記,要記得導入items裏的那幾個你定義的字段的類,我以前忘了導入,而後一切程序正常,就死活存不進去,也不報錯,差點掉坑裏走不出來

而後這個WangyiyunPipeline基本就兩塊,一個初始化init(),一個process_item(),前者是用來鏈接的,後者是用來存儲的。

能夠看到我init裏有一些註釋,這裏說明一下,由於涉及到多個集合存儲,一開始真不知道怎麼弄,一開始我覺得把每一個都扔init就成了,而後經過self調用,後來發現不行,在init定義一個集合就能夠了。process_item()仍是參考剛剛那個GitHub那個項目,才知道經過isinstance判斷。

 
pinelines.py -> process_item()

isinstance你們知道什麼意思吧,而後每一個item對應的什麼在註釋我也寫了。另外,我下面還有一些被註釋掉的代碼部分,這裏就是我在最開頭說的,想要跳過一些重複的地方,可是跳過以後不知道作什麼處理。

在不用框架的時候,咱們存Mongodb。是先定義一個空字典,而後賦值,最後insert_many/insert_one,這裏也是同樣的,只不過,咱們是將傳入的item給dict化。

然後面,在不使用默認的集合時,從新賦一個取代以前的artist便可。

接下來咱們開始正式寫代碼了。

3、爬取歌手

前面有提到,spiders目錄下的文件最好不要取和項目相同的名字,若是取了也不要緊,有辦法,在導入模塊的最前面加上這句:

from __future__ import absolute_import 

由於參考的文章太多了,我也找不到出處的連接了抱歉。

1、導入:

 
import

仍然提醒,要記得導入items的那幾個模塊、

2、最基本的代碼結構是這樣的:

class WangYiYunCrawl(scrapy.Spider): name = 'WangYiYun' allowed_domains = ['music.163.com'] # start_urls = 'http://music.163.com/discover/artist/cat?id={gid}&initial={initial}' group_ids = (1001, 1002, 1003, 2001, 2002, 2003, 6001, 6002, 6003, 7001, 7002, 7003, 4001, 4002, 4003) initials = [i for i in range(65,91)] + [0] headers = { "Referer":"http://music.163.com", "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3067.6 Safari/537.36", } def start_requests(self): pass def parse(self,response): pass 

最前面的那一大段前面都有說過,就再也不提,這裏的headers是本身寫的,因此後面會調用到self.headers,只在settings.py文件裏配置的這裏能夠省略,後面也不用用。

還剩allowed_domains。


 
http://cuiqingcai.com/3472.html

首先講一下我以前一直困惑的地方:start_urls 和start_requests()能夠同時存在,也能夠只要一個便可。
若是你寫的是start_urls,那start_requests()這個函數能夠省掉,直接在parse裏對它進行處理,parse這個函數,就是爬蟲的主程序,日常怎麼寫就怎麼寫。

而後這個response,咱們先來看代碼:


 
start_requests()和parse()

start_requests()這個函數在返回的時候,(對了,這個scrapy裏返回用的都不是return,而是yield,迭代的意思),使用Request,能夠看到它大可能是和requests這個庫很像,它的做用也是同樣,返回是一個response,它特別的在於它最後一個參數,callback的值接的是回調函數,即你要把返回的response做爲參數傳遞給哪一個函數,這個函數後面不須要括號,因此一開始我也沒搞懂它是個什麼。

另外,這裏調用headers是由於我將headers定義在了這個class裏,若是是定義在settings.py裏,這裏可省略。
以後的函數都是這樣,若是你要將什麼參數穿到下一個函數,均可以用這個,而在回調函數裏必須傳入這個response參數。

關於parse函數:

 
來自http://cuiqingcai.com/3472.html

parse這個函數的名稱無所謂,可是最好帶上parse(許多scrapy類型的文章都這麼用,一眼看上去就知道是什麼),而且保證傳遞的回調函數參數和這個函數名稱一致便可。

3、parse函數

一、默認狀況,scrapy推薦使用Xpath,由於response這個對象能夠直接使用Xpath來解析數據,好比我代碼中的,response對象下直接就能夠用selector.xpath。

response.selector.xpath('//ul[@id="m-artist-box"]/li') 

固然,除此以外,還有一種使用xpath的方法:

from scrapy.selector import Selector selector = Selector(response.body) 

關於Selector的用法,能夠參考:
http://blog.csdn.net/liuweiyuxiang/article/details/71065004
可是這種方法並非特別方便,因此直接使用response.selector.xpath的方法就好。

二、關於xpath的格式,參考中文官方文檔吧,http://scrapy-chs.readthedocs.io/zh_CN/1.0/intro/tutorial.html。它跟lxml大同小異,可是仍是有些區別,如圖,這是四種基本的方法:

 
Selector()的方法

它返回的其實都是數組,xpath不用說,而後最經常使用的就是extract了,這個返回的列表裏都是文本,而不是Selector對象

 
extract()

它獲取的就是全部href的集合。
等價於BeautifulSoup這麼用,只不過這個是獲取單個的:

from bs4 import BeautifulSoup soup = BeautifulSoup(response.content,'lxml') href = soup.find('a')['href'] 

而後簡單提兩個xpath簡單而經常使用用法:
@href:這種@後面加什麼的,都是某個標籤的某個屬性,其餘好比src也是這樣用。
text():這個就是獲取文本了。

三、item它就是那個對應某個爬蟲所對應的數據庫的字段,由於mongodb存儲的格式相似json,在python裏它就是個dict,當它是個dict就能夠了。

item = WYYArtistItem()

四、使用scrapy.Request它能夠傳遞的不僅是url,它也能夠傳遞整個item,使用meta,例如

yield scrapy.Request(url=url,meta={'item': item}, headers=self.headers, method='GET', callback=self.parse) 

而後在parse()函數調用的時候,

def parse(self,response): item = response.meta['item'] 

可是並不建議這麼用,由於很浪費資源。

另外,傳遞url的時候,除了用url,若是得到的url這段直接存進了item裏,也能夠直接用item['url']:

yield scrapy.Request(url=item['album_url'], headers=self.headers, method='GET', callback=self.parse_album_list) 

最最最最重要的一點是,若是要存到數據庫裏,好比最後一個不用再Request了,那麼必定要加上

yield item 

這樣才能存進數據庫裏,以前一直存不進去,一個就是前面忘了導入items,一個就是這裏。

4、其餘部分

後面基本都照這個模式來,由於個人順序是:歌手--專輯頁--專輯全部歌曲--歌曲,恰好每個爬下來的url均可以直接傳遞給下一個函數,經過callback的方式。

這裏最大的好處就是,好比歌手頁,不用爬下來存一個列表,而後到了下一個函數,再遍歷一遍這個列表,它每抓一個url,直接就能到下一個函數運行。

我運行的時候最大的一個問題就是‘yield item’那裏,四個部分,我最後一個步驟才放‘yield item’,因而它只存最後一個,即歌曲部分,搞得我一臉懵逼,後來想一想大概要執行完這個,而後再把前面的改爲yield item,才能都存進去。這個是一個很嚴重的問題。

因此最好就是在parse就是第一個地方就存,yield item,存完再改爲yield Request再執行下一個函數。我才知道以前參照的那個項目裏爲何會有註釋掉的'yield item'。

由於這份代碼被我棄用了,因此還有一些有瑕疵的地方沒改過來,代碼就不發出來了。
我以前貼的那個GitHub的項目就還能夠,我就是參照那個改的,基本上我講清楚了,弄懂了就能夠看得懂,就能夠上手了。


4、關於API

前面有提到,API的參考連接,另外再放上幾個,

一、http://moonlib.com/606.html(我用的這個)
二、http://blog.csdn.net/qujunjie/article/details/34422379
三、https://binaryify.github.io/NeteaseCloudMusicApi/#/?id=neteasecloudmusicapi(這個比較官方,我也不知道是否是官方,可是很全很全很全)

咱們爬取的順序是:

一、歌手專輯
二、專輯信息(不包括評論)
三、歌曲信息(不包括評論)
四、歌詞
五、專輯和歌曲評論(這個另起一章寫)

咱們拿一個來說解,其餘的相似:
好比,歌手專輯:

http://music.163.com/api/artist/albums/166009?id=166009&offset=0&total=true&limit=12 
  • offset:偏移量,它其實算是好比歌手的專輯頁每頁有12首,第一頁的offset就是0(通常是0開頭),而後第二頁offset就是12,第一頁0~11恰好12位數,因此offset從12開頭,以此類推。能夠算做是second_offset = limit*(first_offset+1)
  • limit:一頁有多少,這個能夠改,可是不一樣的網站有不一樣的規律,好比豆瓣,它這個limit的上限跟當頁顯示給你看的不一樣,好比一頁默認給你顯示20條數據,可是你給它改爲50也會給你返回50條。可是網易雲不同,它顯示是12條,它的limit能夠往下,但不能往上,往上它返回也是12條,它這是固定的,那超過怎麼辦呢?假設36條,那麼3頁對吧,根據前面的offset,不停的改offset,這數字一看就有規律,很容易就能想到用遍歷,如圖,這是我寫的一個歌手專輯信息的函數:
 
image.png

個人那個get_req()函數就是對requests.get作了些處理,中途確定會遇到各類各樣的狀態碼對吧,這個大家本身去思考。

這裏我沒有用response,由於不涉及到一個完整的傳遞鏈,它只是要存進數據庫的某一個字段,如圖,這個纔是我要進行存儲的的函數,其中調用了get_artist_album_info()這個函數,它只是做爲一個字段存進了item。

 
其中調用了get_artist_album_info()這個函數,它只是做爲一個字段存進了item。

而後回到get_artist_album_info()函數,這裏的建議就是,將固定的不變,會變的用params這個參數,requests.get它後面能夠傳各類參數,包括params,以及前面的headers。

這個params裏有四個參數:

  • id:歌手的id,事實上,有它,albums_url裏那個%s佔位的地方能夠不要,可是由於最開始參照的那個網站有,也是測試成功就沒管了。(上面我提供的連接1和2的區別在這裏,用哪一個都行)
  • offset:這個首先看到個人page_count,就是指頁數,好比3頁,但其實網易雲這個抓不到有多少頁,其實應該是從第一頁的json返回的專輯信息獲得的總專輯數量有多少,而後進行處理的。可是由於寫這個的時候我直接爬的頁數,可是當時沒測試,因此建議改爲這樣就能夠了:
# album_count是一個歌手全部專輯的總數 # 得到的方法能夠先爬第一頁的json數據,或者別的大家本身找 for offset in range(0,album_count,12): params = { 'id':singer_id, 'offset':offset, 'total':'true', 'limit':12 } 
  • total:這個參數的意義除了在評論有用,這裏不肯定到底有什麼用,最開始我覺得,改爲TRUE能不須要offset,會返回全部數據,後來發現沒什麼區別,最大上限就是一頁12,放着也不要緊。
  • limit:這裏就是12

API

以此類推,其餘到底都是這樣了,這裏把http://moonlib.com/606.html的API集中寫一下,method都是GET:
一、歌手專輯:

# 歌手專輯: # 三種寫法,隨意,推薦第三種,後面都是 一、http://music.163.com/api/artist/albums/[artist_id]/ 二、http://music.163.com/api/artist/albums/166009/id=166009&offset=0&total=true&limit=5 三、url='http://music.163.com/api/artist/albums/166009' params = {....} 

二、專輯裏的歌曲列表

# 專輯裏的歌曲列表 http://music.163.com/api/album/2457012?ext=true&id=2457012&offset=0&total=true&limit=10 

三、歌曲信息

# 歌曲信息 # 這裏說明一下,%5B和%5D就是一對中括號[],最好改爲[],像歌手專輯裏第一種寫法那個同樣,由於%5B那種寫法還要處理,且麻煩。 http://music.163.com/api/song/detail/?id=28377211&ids=%5B28377211%5D 

四、歌詞信息

# 這個跟其餘都不同,後面的lv、kv、tv是固定的,只要改id便可。 http://music.163.com/api/song/lyric?os=pc&id=93920&lv=-1&kv=-1&tv=-1

5、評論

評論的API的參考連接:

一、https://github.com/darknessomi/musicbox/wiki/%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E6%96%B0%E7%89%88WebAPI%E5%88%86%E6%9E%90%E3%80%82(這個是從歌單下手的,裏面的評論能夠參考)
二、http://www.imooc.com/article/17459?block_id=tuijian_wz
三、http://blog.csdn.net/u012104691/article/details/53766045
後面這幾篇都講的比較詳細,當時查資料的時候,還查到另一種寫法,就是裏面有一堆命名是first_param什麼的,看得頭暈眼花,而後當時測試彷佛也沒有成功,建議用如今的這種就行了。

基本模式就是這樣:


 
來自https://github.com/darknessomi/musicbox/wiki/%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E6%96%B0%E7%89%88WebAPI%E5%88%86%E6%9E%90%E3%80%82

由於專輯和歌曲都有評論,因此我專門將它寫成了個類,後面直接調用就能夠了。

# -*-coding:utf-8-*- import os import re import sys import json import base64 import binascii import hashlib import requests from Crypto.Cipher import AES class CommentCrawl(object): '''評論的API封裝成一個類,直接傳入評論的API,再調用函數get_song_comment()和get_album_comment()便可分別獲取歌曲和專輯的評論信息 ''' def __init__(self,comment_url): self.comment_url = comment_url self.headers = { "Referer":"http://music.163.com", "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3067.6 Safari/537.36", } def createSecretKey(self,size): '''生成長度爲16的隨機字符串做爲密鑰secKey ''' return (''.join(map(lambda xx: (hex(ord(xx))[2:]), os.urandom(size))))[0:16] def AES_encrypt(self,text, secKey): '''進行AES加密 ''' pad = 16 - len(text) % 16 text = text + pad * chr(pad) encryptor = AES.new(secKey, 2, '0102030405060708') encrypt_text = encryptor.encrypt(text.encode()) encrypt_text = base64.b64encode(encrypt_text) return encrypt_text def rsaEncrypt(self, text, pubKey, modulus): '''進行RSA加密 ''' text = text[::-1] rs = int(text.encode('hex'), 16) ** int(pubKey, 16) % int(modulus, 16) return format(rs, 'x').zfill(256) def encrypted_request(self, text): '''將明文text進行兩次AES加密得到密文encText, 由於secKey是在客戶端上生成的,因此還須要對其進行RSA加密再傳給服務端。 ''' pubKey = '010001' modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7' nonce = '0CoJUm6Qyw8W8jud' text = json.dumps(text) secKey = self.createSecretKey(16) encText = self.AES_encrypt(self.AES_encrypt(text, nonce), secKey) encSecKey = self.rsaEncrypt(secKey, pubKey, modulus) data = { 'params': encText, 'encSecKey': encSecKey } return data def get_post_req(self, url, data): try: req = requests.post(url, headers=self.headers, data=data) except Exception,e: # dosomething print url,e # return None return req.json() def get_offset(self, offset=0): '''偏移量 ''' if offset == 0: text = {'rid':'', 'offset':'0', 'total':'true', 'limit':'20', 'csrf_token':''} else: text = {'rid':'', 'offset':'%s' % offset, 'total':'false', 'limit':'20', 'csrf_token':''} return text def get_json_data(self,url,offset): '''json 格式的評論 ''' text = self.get_offset(offset) data = self.encrypted_request(text) json_text = self.get_post_req(url, data) return json_text def get_song_comment(self): '''某首歌下所有評論 ''' comment_info = [] data = self.get_json_data(self.comment_url,offset=0) comment_count = data['total'] if comment_count: comment_info.append(data) if comment_count > 20: for offset in range(20,int(comment_count),20): comment = self.get_json_data(self.comment_url,offset=offset) comment_info.append(comment) return comment_info def get_album_comment(self,comment_count): '''某專輯下所有評論 ''' album_comment_info = [] if comment_count: for offset in range(0,int(comment_count),20): comment = self.get_json_data(self.comment_url,offset=offset) album_comment_info.append(comment) return album_comment_info 

重複的地方我就不贅述了,最後兩個地方我之因此分開寫,是由於專輯的評論數能夠從專輯信息裏獲取,但歌曲評論數從專輯列表信息裏獲取不到,只能先爬取它第一頁的json數據,它裏面的total就是評論總數,而後再作後面的處理。

評論的API:

# 一、專輯評論API: comment_url = 'http://music.163.com/weapi/v1/resource/comments/R_AL_3_%s?csrf_token=' % album_id # 二、歌曲評論API: comment_url = 'http://music.163.com/weapi/v1/resource/comments/R_SO_4_%s?csrf_token=' % song_id 

而後將comment_url 做爲參數傳入上面封裝的那個類裏便可,不一樣的是專輯還需先獲取專輯評論的數量。

全部的分析都結束了,接下來的代碼本身寫吧。

轉載連接:https://www.jianshu.com/p/65f29ed8b718
相關文章
相關標籤/搜索