文章目錄
一、項目介紹
整個項目目的是爬取網易雲音樂的歌曲的歌詞,可以做成歌詞本,或其他用途,做到了人性化選擇,可以選擇網易雲音樂所有的音樂種類與音樂人,按照不同的需求拼接url獲取內容。但是整個項目我個人認爲有些複雜,通過分析網易雲音樂網站的源代碼發現,裏面有很多坑,具體我會在下面展開,提醒大家,我也會用到一些新的方法,並且需要分析的內容也比較多,多提一嘴,我寫的這些教程面向有編程基礎的讀者,因爲有些內容我沒有詳細講解,篇幅也不夠,如果有新入門的朋友,可以私信我,我會提供技術支持,好技術一起分享。
二、所需技術
- import urllib.request
- import urllib.parse (1和2都是python2.7的爬蟲庫,因爲這個項目需要解析和拼接響應內容,所以用這個庫)
- import lxml import etree (xpath用於獲取內容)
- from selenium import webdriver (這是瀏覽器的自動化庫,可以控制瀏覽器,就是因爲網易雲音樂的這個坑所以才用的這個)
- import re (正則表達式,用於提取內容)
- import requests (用於正常的解析內容)
- import os (保存數據時會用到)
- import json (用於txt和json格式的轉換)
- 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,謝謝各位!