python實現某網站的音樂下載
寫在前面:首先,理論上講,如果歌曲可以在網頁上播放,那麼一定有網址(source src)儲存著歌曲的原始檔。那麼利用火狐(或者谷歌)瀏覽器的F12功能,就可以快速提取出該source src,進而完成歌曲下載了。基於上述操作,我就想到了用python把如前所述封裝起來,輸入歌曲名稱進行選擇進而完成下載。
1. 前期準備
開發環境:win10 + py3.5(即windows + py3.x)
需要安裝的庫:requests 和 selenium(具體安裝方法網上有很多,這裡不再贅述。其中關於selenium的教程建議參考
蟲師
的《selenium2 python自動化測試》)- ps
2. 具體步驟
2.1 網址定位
開啟QQ音樂,使用“搜尋”按鈕進行搜尋後(以搜尋-崇拜-為例),得到的結果為:
從圖中複製可以得到歌曲搜尋網址如下:
https://y.qq.com/portal/search.html#page=1&searchid=1&remoteplace=txt.yqq.top&t=song&w=%E5%B4%87%E6%8B%9C
注意:上述網址中的%E5%B4%87%E6%8B%9C
是“崇拜”的HTML可識別編碼形式。
接下來我們使用requests.get(url)
就可以得到頁面內容了,讓我們看一下結果:
# coding:utf-8
# 測試返回結果
import requests
import urllib.request
headers = {
'Host': 'y.qq.com',
'User-Agent': '', # 這個大家用自己的就好
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3' ,
'Accept-Encoding': 'gzip, deflate, br',
'Referer': 'http://y.qq.com/',
'Connection': 'keep-alive',
'Cache-Control': 'max-age=0',
}
MusicName = "崇拜" # 測試用例
urlMusicName = urllib.parse.quote(MusicName) # 轉換成url可以讀取的字串
url = "https://y.qq.com/portal/search.html#page=1&searchid=1&remoteplace=txt.yqq.top&t=song&w=" + urlMusicName
response = requests.get(url, headers = headers)
print(response.text)
執行上述程式碼,列印結果如下所示:
由於結果太長,這裡我們擷取一部分觀察。發現結果中出現很多亂碼,原來是缺失對response
編碼的結果。在上述程式碼中進行修改:
response = requests.get(url, headers = headers)
response.encoding = 'utf-8' # 對結果進行utf-8編碼
此時再重新列印,就可以顯示中文了!
但是我們會發現,在返回的結果中,並沒有顯示出歌曲列表,這是為什麼呢?
對比一下原網頁的F12結果和返回結果我們發現,在原網頁中,歌曲列表是存在在這個div
裡的:
<div class="js_search_tab_cont" id="song_box" style="display: block;">
但是在返回結果中,該div
標籤的顯示為:
<div class="js_search_tab_cont" id="song_box"></div>
內容為空!這也就解釋了為什麼我們無法得到歌曲列表了,因為根本沒有加載出來。
為了進一步解釋這個問題,我們需要做以下幾件事:
開啟火狐瀏覽器(因為我selenium用的火狐,所以這裡都是使用火狐瀏覽器,當然使用谷歌也完全沒有問題~~),在位址列中輸入
about:config
,進入瀏覽器設定頁面。在頁面中輸入
javascript.enabled
,找到後 滑鼠右鍵-切換。這樣我們就將瀏覽器的
javascript
關掉了。關掉之後再重新載入原搜尋網頁,可以發現歌曲列表不見了。
通過上述操作,我們已經弄明白原因,那就是該歌曲列表是通過JavaScript
進行載入,而非靜態網頁,也因此,我們使用靜態網頁方法進行獲取時,是得不到載入後的歌曲列表的。
為了解決上述問題,也查看了不少方法,覺得說的比較全面在這裡:傳送門-知乎
最後,我選擇了一種最不智慧的方法,就是—暗中觀察。
繼續F12,網路–JS–“clint_search”找到了js跳轉的真正網址,具體如圖:
這樣就完成了請求網址的定位,萬里長征走完第一步~
2.2 歌曲列表列印
可能有同學要問了,為什麼我們要去定位上述網址呢,上述網址開啟之後是什麼樣子呢?
亂亂的,像json
。為了方便閱讀,這裡直接使用火狐自帶的閱讀模式,就是上圖中紅色框中的button,點選,繼續觀察:
注意圖中紅色框出的部分,“media_mid”,“name”,“title”。後面兩個很好解釋,那麼第一個是做什麼用的呢?
在歌曲列表網頁中隨便點選一首歌曲,可以發現其跳轉後的URL為:
https://y.qq.com/n/yqq/song/003JlYgD1SvCYe.html
後面這一長串就是media_mid,原來它是用來定位歌曲的。
具體如何從json中查找出全部有用資訊,這就涉及到了python 正則匹配的問題,建議大家自行學習,這裡匹配不難,注意細節即可!
將全部有用資訊提取出來後,存入字典,並列印在螢幕上,這樣就可以自己選擇想下載的歌曲了。
其中,字串的對齊需要注意一下。因為包含中文和標點符號,因此直接使用`”%-10s”等python自帶的對齊方式是不行的,需要重新寫對齊函式,我參考了這裡中文字元對齊-傳送門,並進行了部分修改,得到字元對齊函式如下:
# ================中文檢測函式====================
# 參考網址:http://lib.csdn.net/article/python/66507?knId=160
def findChinese(source):
# text = source.decode('utf8') # python3 預設為unicode
r = re.findall('[\u4e00-\u9fa5]', source)
# 去除空格影響
condition = lambda t: t != ' '
results = list(filter(condition, r))
return results
# ===============填充字元函式======================
# 參考網址:http://lib.csdn.net/article/python/66507?knId=160
# 輸入變數說明:
# un_align_str: 輸入字串
# lenh: 半形字元個數;自己設定;預設為0
# lenf: 全形字元個數;自己設定;預設為0
# addh: 半形字元空格
# addf: 全形字元空格
def myAlign(un_align_str, lenh = 0, lenf = 0, addh = ' ', addf = u' '):
assert isinstance(lenh, int) # 斷言半形長度為整形變數
assert isinstance(lenf, int) # 斷言全形長度為整形變數
slen = len(un_align_str)
# 中文在預設的utf-8編碼下顯示佔用約2個字元
chn = findChinese(un_align_str)
numchn = len(chn)
numspn = slen - numchn
str = addh * (lenh - numspn) + addf * (lenf - 2 * numchn)
return str
OK,完成歌曲列表列印!
2.3 連結跳轉與歌曲下載
我們發現,當我們點選歌曲的-播放-按鈕時,實際上完成的是一次網頁跳轉(jump~jump~)
對於這種類似於人的操作,我們可以使用selenium來進行模擬。
具體過程也可以描述成如下步驟:
定位“播放”按鈕
模擬滑鼠左鍵單擊操作
讀取跳轉後的網頁,查詢
source src
由於頁面載入需要時間,這裡我直接使用的是time.sleep()
函式來進行等待,另外針對多視窗之間的跳轉,selenium也給出了API,那就是driver.window_handles
。
在這裡,需要利用time.sleep()
等待控制代碼載入完成,否則會出現無法讀取的情況,因此我用瞭如下判斷語句進行處理
count = 0
allhandles = driver.window_handles
for handle in allhandles:
count += 1
if count == 2:
driver.switch_to_window(driver.window_handles[1])
else:
time.sleep(5)
driver.switch_to_window(driver.window_handles[1])
有了當前driver
,我們就可以讀取driver.page_source
,正則匹配出source src
,然後利用requests.get(url).content
完成歌曲下載啦!
3. 相關原始碼
原始碼已經上傳,大家可以自行下載:QQ音樂下載器原始碼。