Python 爬取 全民K歌 個人主頁全部歌曲和MV

//日記式小白向Python2爬蟲編寫記錄, 這是我第二次寫Python程序, 有不對或者疏漏的地方還請dalao輕噴 ?』ω』?

小姐姐唱歌辣么好聽, 好想都下載下來每天都好好欣賞, 可是全民K歌並沒有提供下載歌曲的途徑, 就連自己唱的歌也不給下載, 那我們要怎麼辦呢

以全民K歌主頁熱門作品推薦里推薦的用戶為例, 按下F12用Chrome瀏覽器的開發者工具(元素審查)打開用戶主頁任意一首歌曲, 在Network里選擇Media, 可以看到當前頁面載入的歌曲的鏈接地址

右鍵查看網頁源代碼, Ctrl + F 搜索這個地址

發現這個地址就安安靜靜地躺在源碼里(需要形如 kg.qq.com/node/play? 鏈接的網頁, kg.qq.com/share.html? xxxxxxxx 的分享網頁里歌曲地址是動態載入出來的源碼里找不到)

於是我們只需要用python的requests模塊這樣寫就可以下載這首歌曲了(當然直接輸入到瀏覽器地址欄訪問也可以通過瀏覽器下載)

html = requests(url = 歌曲地址)with open(文件名.m4a, wb) as fp: fp.write(html.content)

下載是能下載了, 可是我們總不能一個一個這麼手動去下載吧多撒比呀, 用re模塊通過正則表達式來提取這個地址就行啦ヾ(*′▽『*)?

html = requests.get(url = 歌曲播放地址, timeout = 60)temp = re.findall(r"playurl":"http://.*.m4a[w=&?]*", html.content)FileUrl = temp[0][11:-1]

聰明的小夥伴們會注意到"playurl"後面還有一個"playurl_video", 如果播放的是MV那我們要提取的地址就是"playurl_video"後面的了, 這部分完整的代碼如下

html = requests.get(url = Url, timeout = 60)temp = re.findall(r"playurl":"http://.*.m4a[w=&?]*", html.content)Type = .m4aif len(temp) == 0: temp = re.findall(r"playurl_video":"http://.*.mp4[w=&?]*", html.content) Type = .mp4 if len(temp) == 0: break else: FileUrl = temp[0][17:-1]else: FileUrl = temp[0][11:-1]

如果沒有找到.m4a的歌曲地址那就嘗試提取.mp4的MV地址, 如果也沒有那就打出GG

接下來回到用戶的個人主頁, 現在只要遍歷個人主頁的所有作品鏈接就能下載所有的作品了(//▽//)按下F12審查元素, 在Element里發現所有作品的播放鏈接都在html文件a標籤的href里

一開始我使用BeautifulSoup來解析html再獲取url, 然後很悲慘的發現哎怎麼只下載了8首明明還有很多的QAQ

我們把網頁往下拉, 發現還有

這個!

切換到Network點擊"查看更多", 發現它是通過xhr動態載入的, 點開這個xhr

看這url好像再請求一個json, 通過瀏覽器訪問這個鏈接發現

確實是這樣

細細品讀這個json, "has_more"好像是一個flag, 1 表示還有未載入的作品 0 表示載入完啦;"ugc_total_count"後面應該是作品總數;"avatar"這後面是個圖片, 明顯就是作品封面圖嘛;"shareid"後面跟著的一串字元串跟我們最開始在歌曲播放鏈接kg.qq.com/node/play? xxxxxxxx後面的xxxxxxxx好像啊, 難不成...;"title"後面就是作品名字嘛

再來看我們提交的url, 裡面有"&start=2&num=8"把這兩個參數改一改, 發現num表示一次獲取多少個作品(最大不超過15), start表示以num計的第多少頁, 比如&start=2&num=4就是獲取第5到第8個作品的信息

再說那個"shareid", 試著訪問一下kg.qq.com/node/play?"shareid", 發現果然不出我所料(??????) ?

(話說評論你腳本參數沒寫好嘛怎麼鬼畜了= =)

於是我們構造這個url就可以獲取到全部作品的播放鏈接, 然後使用threading模塊開多個線程同時下載所有作品, 不知道是Python本身的問題還是我電腦環境的問題有時候requsets.get()訪問網路的時候會卡死按Ctrl + C也沒發退出, 只好給get設置超時時間, 再捕獲到異常的話重試三遍╮(??? ????)╭

接著又發現有些小姐姐會把同一首歌唱不止一遍, 而且作品的shareid不是唯一的小姐姐唱了新的歌我們去爬的時候又會把已經下載的作品重新下載一遍, 於是用hashlib來計算文件md5值保存成json, 每次先載入json, 作品下載完先計算md5值判斷文件是否下載過了, 沒下載過就保存否則就棄置 (:3[▓▓]

需要使用第三方的requests模塊

全部代碼 GitHub

SpiderClass.py

#!/usr/bin/python# -*- coding: UTF-8 -*-import requestsimport reimport os.pathimport sysimport timeimport jsonimport threadingimport hashlibreload(sys)sys.setdefaultencoding(utf-8)class KGSpider: def __init__(self, uid): self.uid = uid self.Songs = [] self.Failed = [] self.Record = [] self.PreUrl = http://kg.qq.com/cgi/kg_ugc_get_homepage?from=1&g_tk=5381&g_tk_openkey=5381&format=json&type=get_ugc def DownloadMusic(self, Url, Name, ID, Path = ./): Times = 1 while True: try: html = requests.get(url = Url, timeout = 60) temp = re.findall(r"playurl":"http://.*.m4a[w=&?]*", html.content) Type = .m4a if len(temp) == 0: temp = re.findall(r"playurl_video":"http://.*.mp4[w=&?]*", html.content) Type = .mp4 if len(temp) == 0: break else: FileUrl = temp[0][17:-1] else: FileUrl = temp[0][11:-1] FileName = Name + [ + ID + ] + Type print u開始下載 + Name download = requests.get(url = FileUrl, timeout = 60).content md5 = hashlib.md5(download).hexdigest() if not md5 in self.Record: with open(Path + FileName.decode(utf-8).encode(gbk), wb) as fp: fp.write(download) self.Record += [md5] print Name + u 下載完畢 else: print Name + u 已存在 except: if Times <= 3: print Name + u 下載失敗 正在進行第 + str(Times) + u 次重試(共 3 次) Times += 1 time.sleep(3) continue else: print Name + u 下載失敗 重試次數用盡 self.Failed += [{name:Name, id:ID}] break break def Run(self): html = requests.get(http://kg.qq.com/node/personal?uid= + self.uid) UserName = re.findall(r<span class="my_show__name">.*?</span>, html.content) if len(UserName) == 0: UserName = else: UserName = UserName[0][28:-7] UserName = re.sub(r<img src=.*?>, , UserName) print u用戶名: + UserName html = requests.get(url = self.PreUrl + &start= + str(1) + &num=8&touin=&share_uid= + self.uid) Total = int(json.loads(html.content.decode(gbk).encode(utf-8))[data][ugc_total_count]) PageN = Total / 8 if Total % 8 != 0: PageN += 1 print u共 + str(Total) + u 個歌曲或視頻文件 for i in range(PageN): html = requests.get(url = self.PreUrl + &start= + str(i + 1) + &num=8&touin=&share_uid= + self.uid).content temp = json.loads(html.decode(gbk).encode(utf-8))[data][ugclist] for it in temp: self.Songs += [{url:http://kg.qq.com/node/play?s= + it[shareid], name:it[title], id:it[shareid]}] UserName = re.sub(r[/:*?"<>|], _, UserName.decode(utf-8).encode(gbk)) if len(UserName) > 180: UserName = UserName[:180] Path = ./ + UserName + [ + self.uid + ] + / if not os.path.exists(Path): os.makedirs(Path) if os.path.isfile(Path + Record.json): with open(Path + Record.json, r) as fp: self.Record = json.load(fp) Threads = [] for Song in self.Songs: ThisThread = threading.Thread(target = self.DownloadMusic, args = (Song[url], Song[name], Song[id], Path, )) Threads += [ThisThread] ThisThread.start() for ThisThread in Threads: ThisThread.join() if len(self.Failed) == 0: print u
全部下載完成 else: print u部分內容下載失敗 for it in self.Failed: print it[name] + [ + it[id] + ] with open(Path + Record.json, w) as fp: json.dump(self.Record, fp)

SpiderMain.py

#!/usr/bin/python# -*- coding: UTF-8 -*-import requestsimport reimport sysfrom SpiderClass import KGSpiderreload(sys)sys.setdefaultencoding(utf-8)PreUrl = http://kg.qq.com/node/personal?uid=while True: temp = raw_input(u請輸入要抓取的全民K歌個人主頁鏈接: .decode(utf-8).encode(gbk)) temp = re.findall(rw{10,}, temp) if len(temp) == 0: print u錯誤的個人主頁. continue uid = temp[0] html = requests.get(url = PreUrl + uid) if html.status_code == 404 or html.content.find(rinvalid shareuid) != -1: print u錯誤的個人主頁. continue else: Spider = KGSpider(uid = uid) Spider.Run() sys.exit(0)

推薦閱讀:

換個思路學Python-以爬蟲為例(一)
NodeJs爬蟲抓取古代典籍,共計16000個頁面心得體會總結
關於爬蟲,看這一篇就夠了!
爬取採集亞馬遜商品評論(Review)一鍵搞定!
爬蟲代碼改進(二)|多頁抓取與二級頁面

TAG:Python入門 | 網頁爬蟲 |