文章目錄
- (一)工欲善其事必先利其器-安裝工具
-
- Selenium
- 浏覽器
- (二)實戰
(一)工欲善其事必先利其器-安裝工具
Selenium
Selenium是一個強大的網絡資料采集工具,其最初是為網站自動化測試而開發的。近幾年,它還被廣泛用于擷取精确的網站快照,因為它們可以直接運作在浏覽器上。Selenium 庫是一個在WebDriver 上調用的API。WebDriver 有點兒像可以加載網站的浏覽器,但是它也可以像BeautifulSoup對象一樣用來查找頁面元素,與頁面上的元素進行互動(發送文本、點選等),以及執行其他動作來運作網絡爬蟲。安裝方式與其他Python第三方庫一樣。
pip install Selenium
浏覽器
Selenium 自己不帶浏覽器,它需要與第三方浏覽器結合在一起使用。例如,如果你在Firefox 上運作Selenium,可以直接看到一個Firefox 視窗被打開,進入網站,然後執行你在代碼中設定的動作。雖然這樣可以看得更清楚,但不适用于這個爬蟲程式,爬一頁就打開一頁效率太低,是以我用一個叫PhantomJS的工具代替真實的浏覽器。
PhantomJS:是一個“無頭”(headless)浏覽器。它會把網站加載到記憶體并執行頁面上的JavaScript,但是它不會向使用者展示網頁的圖形界面。把Selenium和PhantomJS 結合在一起,就可以運作一個非常強大的網絡爬蟲。
官方網站下載下傳:http://phantomjs.org/download.html
然後把可執行檔案拷貝到Python安裝目錄的Scripts檔案夾。
(二)實戰
1.爬取歌單的名字,播放量,連結放入一個csv檔案;
2.将歌單封面和歌單中的歌曲下載下傳到檔案中。
網易雲站外播放器連結:http://music.163.com/song/media/outer/url?id=
加上歌曲id号即可以擷取歌曲,如:http://music.163.com/song/media/outer/url?id=476592630.mp3
網易雲歌單連結(第一頁):https://music.163.com/#/discover/playlist/?order=hot&cat=全部&limit=35&offset=0
“F12”分析一下:

播放數nb (number broadcast):59340
封面 msk (mask):有标題(title)和url
同理,也可以找到“下一頁”的url,最後一頁的url是“javascript:void(0)”
這就可以做一個循環,來擷取資料:
url="https://music.163.com/#/discover/playlist/?order=hot&cat=%E5%85%A8%E9%83%A8&limit=35&offset=0"
while url!='javascript:void(0)':
網易雲有反爬蟲機制:
1.User Agent:
找到User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6801.400 QQBrowser/10.3.2960.400
如果是使用入門的requests,BeautifulSoup來爬取資料就需要添加headers,不然伺服器會識别爬蟲,擷取不到資料;
headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6788.400 QQBrowser/10.3.2714.400"}
html=requests.get(url,headers=headers)
2.frame架構:
網易雲用了frame架構,使用BeautifulSoup的find()方法擷取class和tag是擷取不到資料的,隻能使用xpath;
而這裡使用Selenium,就直接使用switch_to.frame()方法;
先完成第1步:
from selenium import webdriver
import csv
#準備好儲存資料的csv檔案
csv_file=open("playlist.csv","a",newline="",encoding='utf-8')
writer=csv.writer(csv_file)
writer.writerow(['歌單名','播放量','連結'])
#歌單第一頁
url="https://music.163.com/#/discover/playlist/?order=hot&cat=%E5%85%A8%E9%83%A8&limit=35&offset=0"
#用PhantomJS接口建立一個Selenium的WebDriver
driver=webdriver.PhantomJS()
#解析每一頁,到最後一頁
while url!='javascript:void(0)':
print("通路位址:",url)
#用WebDriver加載界面
driver.get(url)
#切換到内容的iframe
driver.switch_to.frame("g_iframe")
#擷取歌單标簽
data=driver.find_element_by_id("m-pl-container").find_elements_by_tag_name("li")
for i in range(len(data)):
src=data[i].find_element_by_class_name("j-flag").get_attribute('src')
a=data[i].find_element_by_class_name("msk")
name=a.get_attribute('title') #歌單名字
print("歌單名:",name)
nb=data[i].find_element_by_class_name("nb").text #播放量
print("播放量:",nb)
hf=a.get_attribute('href') #連結
print("連結:",hf)
#将資料寫入csv檔案
writer.writerow([name,nb,hf])
url=data[i].find_element_by_xpath('//*[@id="m-pl-pager"]/div/a[11]').get_attribute('href')
csv_file.closed()
爬取資料結果:
完成第2步(網易雲維護更新,站外播放器的連結失效了,通路都是404,是以将歌曲連結存為.txt檔案):
from selenium import webdriver
import csv
import os
import requests
import re
import urllib
#建立以歌單名為檔案名的檔案
def creatFile(url,name):
if not os.path.isdir(os.getcwd()+"/"+name):
os.mkdir(os.getcwd()+"/"+name)
img=requests.get(url)
with open(os.getcwd()+"/"+name+"/"+"封面.jpg",'wb') as f:
f.write(img.content)
#獲得歌單内歌曲界面
def getMusicPage(name,url):
driver=webdriver.PhantomJS()
driver.get(url)
driver.switch_to.frame("g_iframe")
info=driver.find_element_by_xpath('//table[@class="m-table "]/tbody').find_elements_by_tag_name("tr")
for i in range(len(info)):
musicName=info[i].find_element_by_tag_name("b").get_attribute('title').replace('\xa0','')
print(musicName)
title=validateTitle(musicName)
hf=info[i].find_element_by_tag_name("a").get_attribute('href')
#song_id=hf.strip('https://music.163.com/song?id=85575')
#song_url="http://music.163.com/song/media/outer/url?id="+song_id
#print(song_url)
downloadMusic(name,title,hf)
#正規表達式處理建立檔案夾檔案名時的非法字元
def validateTitle(title):
rstr=r"[\/\\\:\*\?\"\<\>\|]" # '/ \ : * ? " < > |'
new_title=re.sub(rstr,"_",title) #将非法字元替換為下劃線
return new_title
#将歌曲連結存入檔案
def downloadMusic(name,musicName,url):
if not os.path.isdir(os.getcwd()+"/"+name+"/"+musicName):
os.mkdir(os.getcwd()+"/"+name+"/"+musicName)
with open(os.getcwd()+"/"+name+"/"+musicName+"/"+"連結.txt","w") as f:
f.write(url)
'''
#下載下傳歌曲(已失效)
path=os.getcwd()+"/"+name+"/{}.mp3".format(musicName)
with open(path,'wb') as f:
try:
urllib.request.urlretrieve(url,path)
except BaseException:
print("下載下傳失敗:"+musicName)
pass
'''
if __name__=='__main__':
csv_file=open("playlist.csv","a",newline="",encoding='utf-8')
writer=csv.writer(csv_file)
writer.writerow(['歌單名','播放量','連結'])
#歌單第一頁
url="https://music.163.com/#/discover/playlist/?order=hot&cat=%E5%85%A8%E9%83%A8&limit=35&offset=0"
#用PhantomJS接口建立一個Selenium的WebDriver
driver=webdriver.PhantomJS()
#解析每一頁,到最後一頁
while url!='javascript:void(0)':
print("通路位址:",url)
#用WebDriver加載界面
driver.get(url)
#切換到内容的iframe
driver.switch_to.frame("g_iframe")
#擷取歌單标簽
data=driver.find_element_by_id("m-pl-container").find_elements_by_tag_name("li")
for i in range(len(data)):
src=data[i].find_element_by_class_name("j-flag").get_attribute('src')
a=data[i].find_element_by_class_name("msk")
name=a.get_attribute('title') #歌單名字
fileName=validateTitle(name)
#建立檔案存儲歌單
creatFile(src,fileName)
hf=a.get_attribute('href') #連結
#獲得歌單連結,獲得歌曲資訊
getMusicPage(fileName,hf)
url=data[i].find_element_by_xpath('//*[@id="m-pl-pager"]/div/a[11]').get_attribute('href')
建立檔案夾時使用相對路徑建立檔案夾,爬取結果:
以歌單名建立的檔案夾:
以歌曲名建立的檔案夾:
檔案夾中為歌曲播放連結: