Python爬蟲實戰一之爬取QQ音樂


一、前言


      前段時間嘗試爬取了網易雲音樂的歌曲,這次打算爬取QQ音樂的歌曲信息。網易雲音樂歌曲列表是通過iframe展示的,可以借助Selenium獲取到iframe的頁面元素,

  而QQ音樂采用的是異步加載的方式,套路不一樣,這是主流的頁面加載方式,爬取有點難度,不過也是對自己的一個挑戰。

 

二、Python爬取QQ音樂單曲


之前看的慕課網的一個視頻, 很好地講解了一般編寫爬蟲的步驟,我們也按這個來。

          爬蟲步驟

1.確定目標

首先我們要明確目標,本次爬取的是QQ音樂歌手劉德華的單曲。

(百度百科)->分析目標(策略:url格式(范圍)、數據格式、網頁編碼)->編寫代碼->執行爬蟲

2.分析目標

 歌曲鏈接:https://y.qq.com/n/yqq/singer/003aQYLo2x8izP.html#tab=song&

從左邊的截圖可以知道單曲采用分頁的方式排列歌曲信息,每頁顯示30條,總共30頁。點擊頁碼或者最右邊的">"會跳轉到下一頁,瀏覽器會向服務器發送ajax異步請求,從鏈接可以看到begin和num參數,分別代表起始歌曲下標(截圖是第2頁,起始下標是30)和一頁返回30條,服務器響應返回json格式的歌曲信息(MusicJsonCallbacksinger_track({"code":0,"data":{"list":[{"Flisten_count1":......]})),如果只是單獨想獲取歌曲信息,可以直接拼接鏈接請求和解析返回的json格式的數據。這里不采用直接解析數據格式的方法,我采用的是Python Selenium方式,每獲取和解析完一頁的單曲信息,點擊 ">" 跳轉到下一頁繼續解析,直至解析並記錄所有的單曲信息。最后請求每個單曲的鏈接,獲取詳細的單曲信息。

 

右邊的截圖是網頁的源碼,所有歌曲信息都在類名為mod_songlist的div浮層里面,類名為songlist_list的無序列表ul下,每個子元素li展示一個單曲,類名為songlist__album下的a標簽,包含單曲的鏈接,名稱和時長等。

 

3.編寫代碼

1)下載網頁內容,這里使用Python 的Urllib標准庫,自己封裝了一個download方法:

 1 def download(url, user_agent='wswp', num_retries=2):
 2     if url is None:
 3         return None
 4     print('Downloading:', url)
 5     headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}
 6     request = urllib.request.Request(url, headers=headers)  # 設置用戶代理wswp(Web Scraping with Python)
 7     try:
 8         html = urllib.request.urlopen(request).read().decode('utf-8')
 9     except urllib.error.URLError as e:
10         print('Downloading Error:', e.reason)
11         html = None
12         if num_retries > 0:
13             if hasattr(e, 'code') and 500 <= e.code < 600:
14                 # retry when return code is 5xx HTTP erros
15                 return download(url, num_retries-1)  # 請求失敗,默認重試2次,
16     return html

 


2)解析網頁內容,這里使用第三方插件BeautifulSoup,具體可以參考BeautifulSoup API 。

 1 def music_scrapter(html, page_num=0):
 2     try:
 3         soup = BeautifulSoup(html, 'html.parser')
 4         mod_songlist_div = soup.find_all('div', class_='mod_songlist')
 5         songlist_ul = mod_songlist_div[1].find('ul', class_='songlist__list')
 6         '''開始解析li歌曲信息'''
 7         lis = songlist_ul.find_all('li')
 8         for li in lis:
 9             a = li.find('div', class_='songlist__album').find('a')
10             music_url = a['href']  # 單曲鏈接
11             urls.add_new_url(music_url)  # 保存單曲鏈接
12             # print('music_url:{0} '.format(music_url))
13         print('total music link num:%s' % len(urls.new_urls))
14         next_page(page_num+1)
15     except TimeoutException as err:
16         print('解析網頁出錯:', err.args)
17         return next_page(page_num + 1)
18     return None
 1 def get_music():
 2      try:
 3         while urls.has_new_url():
 4             # print('urls count:%s' % len(urls.new_urls))
 5             '''跳轉到歌曲鏈接,獲取歌曲詳情'''
 6             new_music_url = urls.get_new_url()
 7             print('url leave count:%s' % str( len(urls.new_urls) - 1))
 8             html_data_info = download(new_music_url)
 9             # 下載網頁失敗,直接進入下一循環,避免程序中斷
10             if html_data_info is None:
11                 continue
12             soup_data_info = BeautifulSoup(html_data_info, 'html.parser')
13             if soup_data_info.find('div', class_='none_txt') is not None:
14                 print(new_music_url, '   對不起,由於版權原因,暫無法查看該專輯!')
15                 continue
16             mod_songlist_div = soup_data_info.find('div', class_='mod_songlist')
17             songlist_ul = mod_songlist_div.find('ul', class_='songlist__list')
18             lis = songlist_ul.find_all('li')
19             del lis[0]  # 刪除第一個li
20             # print('len(lis):$s' % len(lis))
21             for li in lis:
22                 a_songname_txt = li.find('div', class_='songlist__songname').find('span', class_='songlist__songname_txt').find('a')
23                 if 'https' not in a_songname_txt['href']:  #如果單曲鏈接不包含協議頭,加上
24                     song_url = 'https:' + a_songname_txt['href']
25                 song_name = a_songname_txt['title']
26                 singer_name = li.find('div', class_='songlist__artist').find('a').get_text()
27                 song_time =li.find('div', class_='songlist__time').get_text()
28                 music_info = {}
29                 music_info['song_name'] = song_name
30                 music_info['song_url'] = song_url
31                 music_info['singer_name'] = singer_name
32                 music_info['song_time'] = song_time
33                 collect_data(music_info)
34      except Exception as err:  # 如果解析異常,跳過
35          print('Downloading or parse music information error continue:', err.args)
 
        

4.執行爬蟲

爬蟲跑起來了,一頁一頁地去爬取專輯的鏈接,並保存到集合中,最后通過get_music()方法獲取單曲的名稱,鏈接,歌手名稱和時長並保存到Excel文件中。
 
        

三、Python爬取QQ音樂單曲總結


1.單曲采用的是分頁方式,切換下一頁是通過異步ajax請求從服務器獲取json格式的數據並渲染到頁面,瀏覽器地址欄鏈接是不變的,不能通過拼接鏈接來請求。一開始想過都通過Python Urllib庫來模擬ajax請求,后來想想還是用Selenium。Selenium能夠很好地模擬瀏覽器真實的操作,頁面元素定位也很方便,模擬單擊下一頁,不斷地切換單曲分頁,再通過BeautifulSoup解析網頁源碼,獲取單曲信息。

 

2.url鏈接管理器,采用集合數據結構來保存單曲鏈接,為什么要使用集合?因為多個單曲可能來自同一專輯(專輯網址一樣),這樣可以減少請求次數。

 1 class UrlManager(object):
 2     def __init__(self):
 3         self.new_urls = set()  # 使用集合數據結構,過濾重復元素
 4         self.old_urls = set()  # 使用集合數據結構,過濾重復元素
 5     def add_new_url(self, url):
 6         if url is None:
 7             return
 8         if url not in self.new_urls and url not in self.old_urls:
 9             self.new_urls.add(url)
10 
11     def add_new_urls(self, urls):
12         if urls is None or len(urls) == 0:
13             return
14         for url in urls:
15             self.add_new_url(url)
16 
17     def has_new_url(self):
18         return len(self.new_urls) != 0
19 
20     def get_new_url(self):
21         new_url = self.new_urls.pop()
22         self.old_urls.add(new_url)
23         return new_url
 

3.通過Python第三方插件openpyxl讀寫Excel十分方便,把單曲信息通過Excel文件可以很好地保存起來。

1 def write_to_excel(self, content):
2     try:
3         for row in content:
4             self.workSheet.append([row['song_name'], row['song_url'], row['singer_name'], row['song_time']])
5         self.workBook.save(self.excelName)  # 保存單曲信息到Excel文件
6     except Exception as arr:
7         print('write to excel error', arr.args)
 
        

四、后語


最后還是要慶祝下,畢竟成功把QQ音樂的單曲信息爬取下來了。本次能夠成功爬取單曲,Selenium功不可沒,這次只是用到了selenium一些簡單的功能,后續會更加深入學習Selenium,不僅在爬蟲方面還有UI自動化。

后續還需要優化的點:

1.下載的鏈接比較多,一個一個下載起來比較慢,后面打算用多線程並發下載。

2.下載速度過快,為了避免服務器禁用IP,后面還要對於同一域名訪問過於頻繁的問題,有個等待機制,每個請求之間有個等待間隔。

3. 解析網頁是一個重要的過程,可以采用正則表達式,BeautifulSoup和lxml,目前采用的是BeautifulSoup庫, 在效率方面,BeautifulSoup沒lxml效率高,后面會嘗試采用lxml。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM