文章目录
- (一)工欲善其事必先利其器-安装工具
-
- 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')
创建文件夹时使用相对路径创建文件夹,爬取结果:
以歌单名创建的文件夹:
以歌曲名创建的文件夹:
文件夹中为歌曲播放链接: