Python爬蟲之網易雲音樂歌曲歌詞爬取並保存到本地(詳解分析+代碼實現)

一、項目介紹

    整個項目目的是爬取網易雲音樂的歌曲的歌詞,可以做成歌詞本,或其他用途,做到了人性化選擇,可以選擇網易雲音樂所有的音樂種類與音樂人,按照不同的需求拼接url獲取內容。但是整個項目我個人認爲有些複雜,通過分析網易雲音樂網站的源代碼發現,裏面有很多坑,具體我會在下面展開,提醒大家,我也會用到一些新的方法,並且需要分析的內容也比較多,多提一嘴,我寫的這些教程面向有編程基礎的讀者,因爲有些內容我沒有詳細講解,篇幅也不夠,如果有新入門的朋友,可以私信我,我會提供技術支持,好技術一起分享

二、所需技術

  1. import urllib.request
  2. import urllib.parse (1和2都是python2.7的爬蟲庫,因爲這個項目需要解析和拼接響應內容,所以用這個庫)
  3. import lxml import etree (xpath用於獲取內容)
  4. from selenium import webdriver (這是瀏覽器的自動化庫,可以控制瀏覽器,就是因爲網易雲音樂的這個坑所以才用的這個)
  5. import re (正則表達式,用於提取內容)
  6. import requests (用於正常的解析內容)
  7. import os (保存數據時會用到)
  8. import json (用於txt和json格式的轉換)
  9. chromedriver.exe (這是一個工具,配合selenium使用,注意要下載對應瀏覽器的對應版本)

三、網頁分析

1、分析首頁(一級)響應內容

    還是老規矩我們來看網站首頁能反饋給我們什麼內容,我們可以看到,我們應該從首頁進入歌手界面,所以需要獲取到他的href,這樣我們才能拼接url,跳轉到歌手界面,但是這裏是第一個坑,可見下圖,紅色是我們需要獲取的內容,但是,這些內容都在藍色的iframe標籤中,因爲當時(半年前)我還沒學前端,所以我在這裏踩了第一個坑,根本獲取不到內容,解析的html什麼都沒有,要知道,iframe就是爲了無法解析瀏覽器,所以我們這裏就用到的selenium自動化控制瀏覽器,使用控制軟件,打開瀏覽器,這樣就能自動獲取到我們想要的內容,也就是href,雖然這樣需要打開瀏覽器,但是我們可以設置關閉,所以並無大礙。
在這裏插入圖片描述

2、分析二級頁面響應內容

    我們在上面已經獲取到了二級頁面的相關href,我們就已經來到了二級頁面,二級頁面我們需要獲取到每個歌手的分類,我們就能進入到具體的歌手界面,所以我們還需要獲取到每個分類的id,獲取到之後就能拼接每個歌手具體的url,同樣還是包在iframe標籤中,所以還需要selenium自動化測試,會在下面的代碼中展示。看下圖,我們找到了對應分類的url,這裏需要說明,我們後續爲了讓用戶更好的體驗能夠選擇,所以我們需要用到正則獲取到大分類的id,這樣我們就能來到分類頁面,這樣我們就來到了三級頁面。
在這裏插入圖片描述

3、分析三級頁面響應內容

    來到三級頁面之後,我們可以看到這個分類的所有歌手都展示出來了,這就到了小分類,根據這個大分類中的首字母進行小分類,我們還是需要獲取到href,可見包裹在li標籤中,這樣我們就可以使用xpath提取,同樣我們還是會使用字典進行保存,通過用戶輸入內容進行拼接url,在這裏我們就能進入下級頁面。
在這裏插入圖片描述

3、分析四級頁面響應內容

    來到四級頁面後,紅框就是我們最終根據用戶輸入拼接的url,而藍色部分爲我們需要爬取的內容,我們可以看到,都保存在tr中,方便我們使用xpath提取,當然,還需要selenium自動化測試,具體方法我都在下面的代碼中標明。
在這裏插入圖片描述

四、分析小結

    通過上述分析,我們可以發現,整體技術並不太過複雜,主要是比較麻煩,推薦各位使用selenium,如果有更好的方法,歡迎私信我,這是我半年前寫的代碼,還是比較青澀,其實就是發送請求,獲取響應數據,解析內容,保存數據,短短几個字雖然能概括,但是其中需要大家付出很多努力。

五、代碼實現

import urllib.request
import urllib.parse
from lxml import etree
from selenium import webdriver
import re
import requests
import os
import json

class WangYiYunYinYue:
    # 初始化方法
    def __init__(self):
        # 歌手頁url,待拼接
        self.info_url = "https://music.163.com/#"
        # 具體歌手內部頁url,待拼接
        self.start_url = "https://music.163.com/#/discover/artist/cat?"
        # 具體歌曲url,待拼接
        self.prot_url = "https://music.163.com/#/artist?"
        # 請求頭
        self.headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36"}

    def dafenlei(self,dafenlei_name):
        # 使用字典鍵值對的方式,根據用戶輸入內容進行拼接url
        item = {
            '華語男歌手' : 1001, '華語女歌手' : 1002, '華語組合/樂隊' : 1003, '歐美男歌手' : 2001, '歐美女歌手' : 2002, '歐美組合/樂隊' : 2003, '日本男歌手' : 6001, '日本女歌手' : 6002,
            '日本組合/樂隊' : 6003, '韓國男歌手' : 7001, '韓國女歌手' : 7002, '韓國組合/樂隊' : 7003, '其他男歌手' : 4001, '其他女歌手' : 4002, '其他組合/樂隊' : 4003,}
        return item[dafenlei_name]

    def geshoufenlei(self,geshou):
        # 使用字典鍵值對的方式,根據用戶輸入內容進行拼接url
        item = {
            '熱門' : -1, 'A' : 65, 'B' : 66, 'C' : 67, 'D' : 68, 'E' : 69, 'F' : 70, 'G' : 71, 'H' : 72, 'I' : 73, 'J' : 74, 'K' : 75, 'L' : 76, 'M' : 77, 'N' : 78,
            'O': 79,'P' : 80, 'Q' : 81, 'R' : 82, 'S' : 83, 'T' : 84, 'U' : 85, 'V' : 86, 'W' : 87, 'X' : 88, 'Y' : 89, 'Z' : 90, '其他' : 0, }
        return item[geshou]

    # 根據上面的用戶輸入內容,拼接url
    def get_url(self,fenlei_id,geshou_initial):
        data = {
            "id" : fenlei_id,
            "initial" : geshou_initial
        }
        # 解碼
        data_final = urllib.parse.urlencode(data)
        return self.start_url + data_final

    def get_gesou_info(self,html):
        # 使用xpath獲取內容
        geshou_html = etree.HTML(html)
        geshou = geshou_html.xpath("//ul[@class='m-cvrlst m-cvrlst-5 f-cb']/li")
        for list in geshou:
            item= {}
            item["歌手名稱:"] = list.xpath("./a/text()") + list.xpath("./div/a/text()") + list.xpath("./p/a/text()")
            href = list.xpath("./a[1]/@href") + list.xpath("./div/a/@href")
            # 獲取二級頁面url
            self.get_erji_info(href)

    # 整個項目的請求方法,可以獲得響應內容
    def parse_url(self,url):
        response = requests.get(url=url,headers=self.headers)
        return response.content.decode()

    # 根據get_gesou_info中獲取到的二級頁面url,使用正則表達式提取待拼接的內容,繼續發送請求
    # 這裏使用selenium控制瀏覽器
    def get_erji_info(self,href):
        src = href[0]
        id = re.findall(r"\d*\d",src,re.S)[0]
        data = {
            "id" : id,
        }
        finaldata = urllib.parse.urlencode(data)
        url = self.prot_url + finaldata
        driver = webdriver.Chrome()
        driver.get(url)
        driver.switch_to.frame(driver.find_element_by_name("contentFrame"))
        html = driver.page_source
        info_html = etree.HTML(html)
        next_href_list = info_html.xpath("//table[@class='m-table m-table-1 m-table-4']/tbody")
        # 獲取到三級頁面的url
        for next_href in next_href_list:
            next_url = next_href.xpath("./tr/td[2]/div/div/div/span/a/@href")
            self.make_sanji_url(next_url)

    # 解析三級頁面的響應
    def make_sanji_url(self,next_url):
        for src in next_url:
            url = self.info_url + src
            self.get_sanji_info(url)

    # 繼續獲取內容,這纔是核心,提取到歌詞
    def get_sanji_info(self,url):
        driver_html = webdriver.Chrome()
        driver_html.get(url)
        driver_html.switch_to.frame(driver_html.find_element_by_name("contentFrame"))
        html = driver_html.page_source
        info_html = etree.HTML(html)
        all = {}
        all["歌曲名稱:"] = info_html.xpath("//div[@class='cnt']/div[@class='hd']/div//text()")
        all["歌手:"] = info_html.xpath("//div[@class='cnt']/p/span/a/text()")
        all["所屬專輯:"] = info_html.xpath("//div[@class='cnt']/p/a/text()")
        all["歌詞:"] = info_html.xpath("//div[@class='cnt']/div[@id='lyric-content']//text()")
        self.save(all)

    # 保存方法
    def save(self,all):
        name = all["歌手:"][0]
        if not os.path.exists(name):
            os.mkdir(name)
        filename = name + ".txt"
        filepath = name + "/" + filename
        with open(filepath,"a",encoding="utf-8") as tf:
            tf.write(json.dumps(all,ensure_ascii=False,indent=2))
            tf.write("\n")

    # 主方法
    def run(self):
        dafenlei_name = input("請輸入歌手分類:")
        fenlei_id = self.dafenlei(dafenlei_name)
        geshou = input("請輸入歌手首字母/熱門/其他:")
        geshou_initial = self.geshoufenlei(geshou)
        url = self.get_url(fenlei_id,geshou_initial)
        driver = webdriver.Chrome()
        driver.get(url)
        driver.switch_to.frame(driver.find_element_by_name("contentFrame"))
        html = driver.page_source
        self.get_gesou_info(html)
        driver.quit()

if __name__ == '__main__':
    wangyiyunyinyue = WangYiYunYinYue()
    # 調用方法
    wangyiyunyinyue.run()

六、爬取結果(簡單四個示例,您想爬取多少都可以)

在這裏插入圖片描述

六、總結

    總體來說,爬取一些大公司難度還是有的,可見上圖,代碼完美運行,能獲取到所有的歌詞信息,方便我們使用。這類的我不在解析,後面會帶來分佈式爬取,驗證碼破解等進階內容。

七、我的一個Python-DjangoWeb項目

    github地址:https://github.com/IronmanJay/DailyFresh/tree/master
    有需要的可以下載使用,需要什麼可以私信我,喜歡的點個star,謝謝各位!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章