一、分析熱評的請求URL
- 首先我們先對請求抓包,發現所有的評論都包含https://music.163.com/weapi/v1/resource/comments/R_SO_4_32785700?csrf_token=" 裏面,然後再去分析這個請求,發現這是一個POST請求,請求參數由兩個params以及encSecKey。好了到此我們需要的東西都有了,接下來我們分析如何去得到這兩個參數。
-
找到請求
二、分析js加密 - 找到全局js文件,找到兩個參數所在的位置
- 發現這兩個參數是由window.asrsea獲得的,接着去定位到這個函數找到對應的原函數d
- 對js進行調試,發現d的四個參數,有三個是定值,這個函數還用到了a、b、c三個函數
- 其中a是產生一個16位的隨機數(這裏我直接讓它等於FwtEYduOXlNEHbLP)爲什麼要等與這個呢 hhh 因爲我發現這個隨機數,他在生成encText的時候用了一次,生成encSecKey的時候,又用了一次,而且encSecKey就只跟這個隨機數相關,所以讓這個隨機數爲定值的話,就可以直接得到encSecKey的值,不用再去搞一個rsa加密,爲了讓你們看清楚,我還是把貼出來把
- b函數就是我們主要要解決的AES加密,經過調試,我們可以知道它的兩個參數a、b分別是加密字符轉、密鑰。以及AES的偏移量爲0102030405060708、加密模式爲CBC
- 接下來看c函數,c函數其實是RSA加密,獲取encSecKey的值的他的三個參數,只有a是變量,是我們隨機生成的16爲隨機數,這裏我們就默認爲定值,b、c應該是和rsa加密有關的參數,應爲本身並沒有學過加密,這裏我就不多說了,但是經過調試,我們可以知道b、c是定值 b =010001 c是一大串字符串。見下圖。
- 最後我們具體分析一下d函數,經過N次調試,我發現這其實和我的想法差不多,h是一個字典,包含了我們需要的兩個參數。encText是由兩次AES加密產生的及兩次b,加密字符串是一樣的,然後密鑰第一次是個定值0CoJUm6Qyw8W8jud,第二次是16位隨機數,也相當於定值。所以encText就出來了,params是由一次RSA加密產生的,並且只與16位的隨機數有關,這裏就清楚爲什麼我讓隨機數直接等於FwtEYduOXlNEHbLP,哈哈。因爲我調試的時候,剛好出現了這麼個隨機數,於是我就直接拿過來用了,這個隨機數對應的encSecKey = 81e7a41af9830200d5606be1a632e57eb0006b3cdae579127115c6323d4c4802f3af9efcee21d9f4126dde266773cbd795f19ae44028f9f8d038cd62d2816952fa99bb61ecb5fba87d5b178ff4b982ee34c7491808f7cb774554a0235a210caf2e5e867a0e2ebdf6f994be1b198ab43b14ce1f7cfa6f80b9070dea5fc5d6c712
三、用python重寫js加密
- 經過js加密碼的分析,我用python實現了一下AES加密,具體代碼如下,包含兩個參數,一個是需要加密的字符串,一個是密鑰具體如下
def AES_encrypt(text, key):
pad = 16 - len(text) % 16
text = text + pad * chr(pad)
encryptor = AES.new(key, AES.MODE_CBC, "0102030405060708")
encrypt_text = encryptor.encrypt(text)
encrypt_text = base64.b64encode(encrypt_text)
return encrypt_text
- 兩次調用這個函數。得到結果與調試的結果對比,一模一樣。哈哈,上代碼、上圖
f_key = "0CoJUm6Qyw8W8jud"
text = "{\"rid\":\"R_SO_4_32785700\",\"offset\":\"20\",\"total\":\"true\",\"limit\":\"20\",\"csrf_token\":\"\"}"
rs = AES_encrypt(text, f_key)
params = AES_encrypt(str(rs)[2:-1], "FwtEYduOXlNEHbLP")
這裏解釋一下,text是我進過N次調試得出的,因爲在請求評論之前,text有好幾個值來驗證其他的東西,這裏我大概理解了一下text的含義,這裏我們只要知道offset是偏移量,limit是每次請求多少條,比如你請求前二十條則offset=0,limit = 20,我上面的是請求20-40條。
- 然後直接獲取的encSecKey直接賦值就好啦,結合這兩個參數,我們的請求參數就構造好了,直接POST吧,就能得到評論啦,哈哈,上代碼,上圖
data = {
'params': params,
'encSecKey': encSecKey
}
headers = {
'Accept-Language':"zh-CN,zh;q=0.9,en;q=0.8",
'User-Agent':'Mozilla/5.0 (X11; Linux x86_64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36',
'Cookie': 'appver=1.5.0.75771',
'Referer': 'http://music.163.com/'
}
url = "https://music.163.com/weapi/v1/resource/comments/R_SO_4_32785700?csrf_token="
raw = requests.post(url,headers=headers, data=data)print(raw.json())
四、解析json,獲取評論
- 上面我們已經獲取了原始數據,接下來我們從json數據中獲取我們需要的評論
def getComment(raw, comments):
comment = comments
contents = raw["comments"]
for content in contents:
part = []
content.pop('beReplied')
part.append(content["user"]['nickname'])
part.append(content["content"])
part.append(content["time"])
comment.append(part)
return comment
def getAll(id,total,limit):
comment = []
for i in range(0, int(total), 100):
raw = getRawData.getRawJson(id, i.__str__(), limit)
comment = getComment(raw, comment)
return comment
- 獲取了全部的評論之後,我們將其寫入csv文件
def getCSV(id,total, csv_name):
comment = getAll(id, total, '100')
df = pd.DataFrame(data=comment,
columns=['user', 'content', 'time'])
df['content'].to_csv(csv_name+'.csv', encoding='utf-8')
-
獲取的評論如下
-
最後根據評論生成詞雲
def gen_pic(csv_name):
with open(csv_name+'.csv', 'r', encoding='utf-8') as f:
content = f.read()
font = r'C://Windows//Fonts/simkai.ttf'
bg = np.array(Image.open("background/bg.png"))
pic = WordCloud(collocations=False, font_path=font, background_color="white", max_words=800, mask=bg, max_font_size=300, random_state=42).generate(content)
plt.imshow(bg, cmap=plt.cm.gray)
image_colors = ImageColorGenerator(bg)
plt.imshow(pic.recolor(color_func=image_colors))
plt.axis("off")
plt.show()
- 最終效果如下