爬蟲實戰(二) 用Python爬取網易雲歌單


最近,博主喜歡上了聽歌,但是又苦於找不到好音樂,於是就打算到網易雲的歌單中逛逛

本着 “用技術改變生活” 的想法,於是便想着寫一個爬蟲爬取網易雲的歌單,並按播放量自動進行排序

這篇文章,我們就來講講怎樣爬取網易雲歌單,並將歌單按播放量進行排序,下面先上效果圖

1、用 requests 爬取網易雲歌單

打開 網易雲音樂 歌單首頁,不難發現這是一個靜態網頁,而且格式很有規律,爬取起來應該十分簡單

按照以前的套路,很快就可以寫完代碼,無非就是分為下面幾個部分:

(1)獲取網頁源代碼

這里我們使用 requests 發送和接收請求,核心代碼如下:

import requests
def get_page(url):
    # 構造請求頭部
    headers = {
        'USER-AGENT':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
    }
    # 發送請求,獲得響應
    response = requests.get(url=url,headers=headers)
    # 獲取響應內容
    html = response.text
    return html

(2)解析網頁源代碼

解析數據部分我們使用 xpath(對於 xpath 的語法不太熟悉的朋友,可以看看博主之前的文章)

文章傳送門:

核心代碼如下:

from lxml import etree
# 解析網頁源代碼,獲取數據
def parse4data(self,html):
    html_elem = etree.HTML(html)
    # 播放量
    play_num = html_elem.xpath('//ul[@id="m-pl-container"]/li/div/div/span[@class="nb"]/text()')
    # 歌單名稱
    song_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@title')
    # 歌單鏈接
    song_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@href')
    song_link = ['https://music.163.com/#'+item for item in song_href]
    # 用戶名稱
    user_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@title')
    # 用戶鏈接
    user_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@href')
    user_link = ['https://music.163.com/#'+item for item in user_href]
    # 將數據打包成列表,其中列表中的每一個元素是一個字典,每一個字典對應一份歌單信息
    data = list(map(lambda a,b,c,d,e:{'播放量':a,'歌單名稱':b,'歌單鏈接':c,'用戶名稱':d,'用戶鏈接':e},play_num,song_title,song_link,user_title,user_link))
    # 返回數據
    return data

# 解析網頁源代碼,獲取下一頁鏈接
def parse4link(self,html):
    html_elem = etree.HTML(html)
    # 下一頁鏈接
    href = html_elem.xpath('//div[@id="m-pl-pager"]/div[@class="u-page"]/a[@class="zbtn znxt"]/@href')
    # 如果為空,則返回 None;如果不為空,則返回鏈接地址
    if not href:
        return None
    else:
        return 'https://music.163.com/#' + href[0]

(3)完整代碼

import requests
from lxml import etree
import json
import time
import random

class Netease_spider:
    # 初始化數據
    def __init__(self):
        self.originURL = 'https://music.163.com/#/discover/playlist'
        self.data = list()

    # 獲取網頁源代碼
    def get_page(self,url):
        headers = {
            'USER-AGENT':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
        }
        response = requests.get(url=url,headers=headers)
        html = response.text
        return html

    # 解析網頁源代碼,獲取數據
    def parse4data(self,html):
        html_elem = etree.HTML(html)
        play_num = html_elem.xpath('//ul[@id="m-pl-container"]/li/div/div/span[@class="nb"]/text()')
        song_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@title')
        song_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@href')
        song_link = ['https://music.163.com/#'+item for item in song_href]
        user_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@title')
        user_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@href')
        user_link = ['https://music.163.com/#'+item for item in user_href]
        data = list(map(lambda a,b,c,d,e:{'播放量':a,'歌單名稱':b,'歌單鏈接':c,'用戶名稱':d,'用戶鏈接':e},play_num,song_title,song_link,user_title,user_link))
        return data

    # 解析網頁源代碼,獲取下一頁鏈接
    def parse4link(self,html):
        html_elem = etree.HTML(html)
        href = html_elem.xpath('//div[@id="m-pl-pager"]/div[@class="u-page"]/a[@class="zbtn znxt"]/@href')
        if not href:
            return None
        else:
            return 'https://music.163.com/#' + href[0]

    # 開始爬取網頁
    def crawl(self):
        # 爬取數據
        print('爬取數據')
        html = self.get_page(self.originURL)
        data = self.parse4data(html)
        self.data.extend(data)
        link = self.parse4link(html)
        while(link):
            html = self.get_page(link)
            data = self.parse4data(html)
            self.data.extend(data)
            link = self.parse4link(html)
            time.sleep(random.random())
        # 處理數據,按播放量進行排序
        print('處理數據')
        data_after_sort = sorted(self.data,key=lambda item:int(item['播放量'].replace('萬','0000')),reverse=True)
        # 寫入文件
        print('寫入文件')
        with open('netease.json','w',encoding='utf-8') as f:
            for item in data_after_sort:
                json.dump(item,f,ensure_ascii=False)

if __name__ == '__main__':
    spider = Netease_spider()
    spider.crawl()
    print('Finished')

2、用 selenium 爬取網易雲歌單

然而,事情真的有這么簡單嗎?

當我們運行上面的代碼的時候,會發現解析網頁源代碼部分,返回的竟然是空列表!

這是為什么呢?敲重點,敲重點,敲重點,這絕對是一個坑啊!

我們重新打開瀏覽器,認真觀察網頁的源代碼

原來,我們所提取的元素被包含在 <iframe> 標簽內部,這樣我們是無法直接進行定位的

因為 iframe 會在原有頁面中加載另外一個頁面,當我們需要獲取內嵌頁面的元素時,需要先切換到 iframe 中

明白了原理之后,重新修改一下上面的代碼

思路是利用 selenium 獲取原有網頁,然后使用 switch_to.frame() 方法切換到 iframe 中,最后返回內嵌網頁

對 selenium 的使用以及環境配置不清楚的朋友,可以參考博主之前的文章

需要修改的地方是獲取網頁源代碼的函數,另外也需要在初始化數據的函數中實例化 webdriver,完整代碼如下:

from selenium import webdriver
from lxml import etree
import json
import time
import random

class Netease_spider:
    # 初始化數據(需要修改)
    def __init__(self):
        # 無頭啟動 selenium
        opt = webdriver.chrome.options.Options()
        opt.set_headless()
        self.browser = webdriver.Chrome(chrome_options=opt)
        self.originURL = 'https://music.163.com/#/discover/playlist'
        self.data = list()

    # 獲取網頁源代碼(需要修改)
    def get_page(self,url):
        self.browser.get(url)
        self.browser.switch_to.frame('g_iframe')
        html = self.browser.page_source
        return html

    # 解析網頁源代碼,獲取數據
    def parse4data(self,html):
        html_elem = etree.HTML(html)
        play_num = html_elem.xpath('//ul[@id="m-pl-container"]/li/div/div/span[@class="nb"]/text()')
        song_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@title')
        song_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@href')
        song_link = ['https://music.163.com/#'+item for item in song_href]
        user_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@title')
        user_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@href')
        user_link = ['https://music.163.com/#'+item for item in user_href]
        data = list(map(lambda a,b,c,d,e:{'播放量':a,'歌單名稱':b,'歌單鏈接':c,'用戶名稱':d,'用戶鏈接':e},play_num,song_title,song_link,user_title,user_link))
        return data

    # 解析網頁源代碼,獲取下一頁鏈接
    def parse4link(self,html):
        html_elem = etree.HTML(html)
        href = html_elem.xpath('//div[@id="m-pl-pager"]/div[@class="u-page"]/a[@class="zbtn znxt"]/@href')
        if not href:
            return None
        else:
            return 'https://music.163.com/#' + href[0]

    # 開始爬取網頁
    def crawl(self):
        # 爬取數據
        print('爬取數據')
        html = self.get_page(self.originURL)
        data = self.parse4data(html)
        self.data.extend(data)
        link = self.parse4link(html)
        while(link):
            html = self.get_page(link)
            data = self.parse4data(html)
            self.data.extend(data)
            link = self.parse4link(html)
            time.sleep(random.random())
        # 處理數據,按播放量進行排序
        print('處理數據')
        data_after_sort = sorted(self.data,key=lambda item:int(item['播放量'].replace('萬','0000')),reverse=True)
        # 寫入文件
        print('寫入文件')
        with open('netease.json','w',encoding='utf-8') as f:
            for item in data_after_sort:
                json.dump(item,f,ensure_ascii=False)

if __name__ == '__main__':
    spider = Netease_spider()
    spider.crawl()
    print('Finished')

這樣,得到目前網易雲音樂中播放量排名前十的歌單如下(哈哈,又可以愉快的聽歌啦):

  1. 2018年度最熱新歌TOP100
  2. 聽說你也在找好聽的華語歌
  3. 精選 | 網絡熱歌分享
  4. 隔壁老樊的孤單
  5. 溫柔暴擊 | 沉溺於男友音的甜蜜鄉
  6. 誰說翻唱不好聽
  7. 若是心懷舊夢 就別再無疾而終
  8. KTV必點:有沒有一首歌,唱着唱着就淚奔
  9. 化妝拍照BGM.
  10. 會講故事的男聲 歌詞唱的太像自己

注意:本項目代碼僅作學習交流使用!!!


免責聲明!

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



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