【音乐爬虫】Python爬虫-selenium+browsermob-proxy 解决动态网页 js渲染问题
1.一般的python爬虫很简单,直接请求对应网址,解析返回的数据即可,但是有很多网站的数据的js动态渲染的,你直接请求是得不到对应的数据的
这时就需要其它手段来处理了。
2.以一个例子来说明,整个过程,爬取一个音乐网站的对应歌手的歌曲。
- 目标网址http://tool.liumingye.cn/music/?page=searchPage,在搜索框输入歌手名字即可得到歌曲。
- 如果我们直接请求这个网址:http://tool.liumingye.cn/music/?page=audioPage&type=migu&name=周杰伦,返回的数据不是我们想要的
- 在inspect里分析js或xhr数据找到真正的网址,可以看到在NetWork下的XHR中找到我们想要的网址,还有POSt请求时发送的数据
我们仿照他发起一个post请求,返回的数据确实是我们想要的,里面包含歌曲名字,还有下载地址,歌词等信息,直接请求歌曲下载地址即可。
import requests
headers = {
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36'}
data={'data':'3c7frUZZlQooe5YYDmZVHi9O4qOxgj8oXuivPGr2sfTT65IGXo85FOqjwtoJPmO-t6ew_q_59dY1SSl7','v':'2'}
r=requests.post('https://app.onenine.cc/m/api/search',headers=headers,data=data)
data=r.json()#转为json
print(data['data']['list'])- 大家不要觉得这样就可以了喔,其实问题还有很多。第一,你要构造POST请求必须要得到data参数的数据,就是那一长串字符串,如果没有data的话,会返回状态码403,禁止访问
- 而且每一个歌手或歌曲的data参数都是动态生成的,没有规律的,这样的话,你每构造一个POST请求就要在游览器那里看一下data参数是多少,根本不符合爬虫的要求。
- 第二,返回的数据只有二十首歌曲,想要得到更多,就必须点击‘下一页'按钮,网站才会去请求下一个二十首,然后渲染到网页上,所以就算有data参数,也无法爬到更多数据。
3.这时就到了Seleninum和Browsermob-proxy出场了。Seleninum是一个用于测试网站的自动化测试工具,支持各种浏览器包括Chrome、Firefox、Safari等主流界面浏览器,同时也支持phantomJS无界面浏览器。它主要得功能就是可以模拟游览器行为,包括执行js代码,寻找各种元素,发送点击,滑动事件等等。这样我们就可以实现点击功能了。selenium的教程推荐这个,他写得不错,比我写得好
https://blog.csdn.net/qq_32502511/article/details/101536325
Browsermob-proxy是一个开源的Java编写的基于LittleProxy的代理服务。Browsermob-Proxy的具体流程有点类似与Flidder或Charles。即开启一个端口并作为一个标准代理存在,当HTTP客户端(浏览器等)设置了这个代理,则可以抓取所有的请求细节并获取返回内容.简单来说就是它可以将网站的请求和返回的数据获取到,就如游览器的'inspect'功能,这样我们就可以获取到‘seacher’的数据了
Browsermob-proxy的教程推荐这个,不难的,看一下就可以上手了。https://blog.csdn.net/qq_32502511/article/details/101536325
4.我们分析一下获取到的数据,包含名字和歌词,封面,下载地址。歌曲的下载地址有四种,url_m4a是流畅,128是标准,320是高品,flac则是无损音乐。url那一栏是在线播放
4.有上面这些准备后就可以写代码爬取数据了,代码如下(有详细注释)
from selenium import webdriver
import time
import os
import threading
import simplejson as json
from browsermobproxy import Server
import requests
from selenium.webdriver.chrome.options import Options #Browsermob-proxy的路径
server=Server(r"D:\Python3.7\browser-proxy.bat\browsermob-proxy-2.1.4\bin\browsermob-proxy.bat")
server.start()#启动browsermob-proxy
proxy=server.create_proxy()
chrome_options=Options() #获取游览器对象,设置无界面模式
chrome_options.add_argument('--proxy-server={0}'.format(proxy.proxy))
chrome_options.add_argument('--headless')#无头模式,不开启游览器界面
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument('--incognito')
driver=webdriver.Chrome(chrome_options=chrome_options)#启用谷歌游览器 #获取音乐的信息
def getDownloadUrls(name): musicUrl='http://tool.liumingye.cn/music/?page=audioPage&type=migu&name='+str(name)
proxy.new_har('music',options={'captureHeaders':True,'captureContent':True})
driver.get(musicUrl)#请求网站
time.sleep(3)#等待页面加载完成,必须要延时,不然获取不到数据
lists=[]
#获取3页数据一共60首歌
for i in range(3):
index=0
result=proxy.har#获取所有请求网址
for entry in result['log']['entries']:
url=entry['request']['url']
#找对应的网址,我们要的是,search网址的
if "app.onenine.cc/m/api/search" in url:
index+=1
'''
因为每点击下一页都会产生一个新的‘search',
而proxy每次都会把所有的请求获取到,所以上一个的’search‘也会获取到
所以要判断排除掉,只获取最新的’search'
'''
if index>i:
content = entry['response']['content']
text=content['text']
data=json.loads(text)['data']
lists.extend(data['list']) #这里就是获取到的json数据.每一次添加20首歌曲的信息进去 #滑到底部
js="window.scrollTo(0,10000)"#滑动滚动条的js代码,如果元素不在页面上显示,是找不到的,所以滑动,让下一页的按钮显示出来
driver.execute_script(js)#执行·js代码
#点击下一页,获取更多歌曲
nextBtn=driver.find_element_by_css_selector('#player>div.aplayer-more')#找到下一页的按钮
nextBtn.click()#发送点击事件
time.sleep(1)#延时1秒等待数据加载
return lists #用于下载音乐代码
def download(path,musicName,folder,ext): r=requests.get(path)
f=open(folder+'/'+musicName+ext,'wb')
f.write(r.content)
f.close() #在lists例表中提取音乐的下载地址和歌曲名字
def downloadMusic(name,type,lists):
'''
name:歌手名字,为每一个歌手都新建一个文件夹
type:表示要下载何种音质
lists:获取的音乐数据列表
'''
isExists=os.path.exists(name)
if not isExists:
os.makedirs(name)
musicName=''
path=''
ext=''
for item in lists:
for key,value in item.items():
ext='.mp3'
key=str(key)
if key=='name':
musicName=value
elif key=='artist':
musicName=musicName+'-'+value
elif key=='url_m4a' and type==0:#m4a表示流畅
path=value
elif key=='url_128' and type==1:#128是标准
path=value
elif key=='url_320' and type==2:#320是高品质
path=value
elif key=='url_flac'and type==3:#flac表示无损
path=value
ext='.flac' if len(path)!=0:
#每一首歌开启一个线程去下载
t = threading.Thread(target=download,args=(path,musicName,name,ext))
t.start()
t.join()
path=''
musicName='' if __name__=='__main__': signer=input("输入歌手名字:")
lists=getDownloadUrls(signer)
print('获取完成,开始下载音乐')
downloadMusic(signer,0,lists)#0表示下载流畅音质的
print('下载结束')
等待一段时间就爬取成功了。
5.这个网站上还有歌单和各种音乐榜单,我们一样可以爬取下来,原理都一样,我就不多解释了。你只要掌握上面的东西,爬取这些东西,还是很简单的,只要给点耐心去学一下。
附上完整代码,包括爬取歌单和榜单
from selenium import webdriver
import time
import os
import threading
import simplejson as json
from browsermobproxy import Server
import requests
from selenium.webdriver.chrome.options import Options #Browsermob-proxy的路径
server=Server(r"D:\Python3.7\browser-proxy.bat\browsermob-proxy-2.1.4\bin\browsermob-proxy.bat")
server.start()#启动browsermob-proxy
proxy=server.create_proxy()
chrome_options=Options() #获取游览器对象,设置无界面模式
chrome_options.add_argument('--proxy-server={0}'.format(proxy.proxy))
chrome_options.add_argument('--headless')#无头模式,不开启游览器界面
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument('--incognito')
driver=webdriver.Chrome(chrome_options=chrome_options)#启用谷歌游览器 #获取音乐的信息
def getDownloadUrls(name): musicUrl='http://tool.liumingye.cn/music/?page=audioPage&type=migu&name='+str(name)
proxy.new_har('music',options={'captureHeaders':True,'captureContent':True})
driver.get(musicUrl)#请求网站
time.sleep(3)#等待页面加载完成,必须要延时,不然获取不到数据
lists=[]
#获取3页数据一共60首歌
for i in range(3):
index=0
result=proxy.har#获取所有请求网址
for entry in result['log']['entries']:
url=entry['request']['url']
#找对应的网址,我们要的是,search网址的
if "app.onenine.cc/m/api/search" in url:
index+=1
'''
因为每点击下一页都会产生一个新的‘search',
而proxy每次都会把所有的请求获取到,所以上一个的’search‘也会获取到
所以要判断排除掉,只获取最新的’search'
'''
if index>i:
content = entry['response']['content']
text=content['text']
data=json.loads(text)['data']
lists.extend(data['list']) #这里就是获取到的json数据.每一次添加20首歌曲的信息进去 #滑到底部
js="window.scrollTo(0,10000)"#滑动滚动条的js代码,如果元素不在页面上显示,是找不到的,所以滑动,让下一页的按钮显示出来
driver.execute_script(js)#执行·js代码
#点击下一页,获取更多歌曲
nextBtn=driver.find_element_by_css_selector('#player>div.aplayer-more')#找到下一页的按钮
nextBtn.click()#发送点击事件
time.sleep(1)#延时1秒等待数据加载
return lists #用于下载音乐代码
def download(path,musicName,folder,ext): r=requests.get(path)
f=open(folder+'/'+musicName+ext,'wb')
f.write(r.content)
f.close() #在lists例表中提取音乐的下载地址和歌曲名字
def downloadMusic(name,type,lists):
'''
name:歌手名字,为每一个歌手都新建一个文件夹
type:表示要下载何种音质
lists:获取的音乐数据列表
'''
isExists=os.path.exists(name)
if not isExists:
os.makedirs(name)
musicName=''
path=''
ext=''
for item in lists:
for key,value in item.items():
ext='.mp3'
key=str(key)
if key=='name':
musicName=value
elif key=='artist':
musicName=musicName+'-'+value
elif key=='url_m4a' and type==0:#m4a表示流畅
path=value
elif key=='url_128' and type==1:#128是标准
path=value
elif key=='url_320' and type==2:#320是高品质
path=value
elif key=='url_flac'and type==3:#flac表示无损
path=value
ext='.flac' if len(path)!=0:
#每一首歌开启一个线程去下载
t = threading.Thread(target=download,args=(path,musicName,name,ext))
t.start()
t.join()
path=''
musicName='' #获取歌单的id和歌单名字
def getSongsSheetId(type): id=[]
sheetNames=[]
#这里的 limit表示获取多少个歌单,2表示两个
url='https://musicapi.leanapp.cn/top/playlist?limit=2&offset=0&order=hot&cat='+str(type)
r=requests.get(url);
data=r.json()
playlist=data['playlists']
for item in playlist:
id.append(item['id'])
sheetNames.append(item['name'])
print(id) return id,sheetNames
#下载歌单的歌曲
def getSongsSheetSongs(type): id,sheetNames=getSongsSheetId('日语')#这里可以做成变量,比如华语,欧美,流行....
url='http://tool.liumingye.cn/music/?page=audioPage&type=YQD&name=https%3A%2F%2Fmusic.163.com%2F%23%2Fplaylist%3Fid%3D'
lists=[]
for i in range(len(id)):
reqUrl=url+str(id[i])
proxy.new_har('lists',options={'captureHeaders':True,'captureContent':True})
driver.get(reqUrl)
time.sleep(3)
result=proxy.har
for entry in result['log']['entries']:
repUrl=entry['request']['url']
if "app.onenine.cc/m/api/search" in repUrl:
content = entry['response']['content']
text=content['text']
data=json.loads(text)['data']
lists=data['list'][:]
downloadMusic(sheetNames[i],type,lists) #榜单,榜单比较少所以可以,直接复制data数据然后直接请求
def getMusicListSongs(listType,quality):
'''
listType:何种榜单
quality:何种音质
'''
musicLists={'飙升榜':'9a96BCVWtsdySz5JAlEM4ETawT0kPc0PKnwYwhGTCdL_ABeeIChrZMDWVoEkAy8xVORmVw_mS9xE1maSBbzV1ISsUCDTq2_sWMDY2T-KnB7cqYjoRo9EYTROqsURCA',
'新歌榜':'0eb8XS226D93TYlB6f0YRLwpd88EElQZglIn4CnZo9CUa8yqKtPDCG-pL3GKi_9UoBxJClL04dhhdgjAh-EAho2EkPBct2PF2tJl3F8p2atv3IwIxgPYE9iDGFKD',
'原创':'9cecTgCD-VsNzA2od9fsyX6c2KpOCdI1VfGRMO26vy4XgwyApkwNtsii9u2La2FS1xoBsJL-nPFpORJ1t9Vd3T0aSzg-0R2LWbS8Yj8wJeYsTVlnnbzFdMeO88OV',
'热歌榜':'bfaFw92RlNQ6e3pJQ9tIkNtitPFqTUbXZHDzE8yuiJ-nNo7gpsDldd5R6AQ83eqQCuE9Id2GiS6ycC9Q8wqeB-HNfxC-Q-Mbv-Amdir7AHal8MKsHnZYeiCWKGG',
'ACG音乐榜':'b91b2EKQBotaf_vdDg3ttFZwGBGJ_uFpbSf8oSjga2p7NKAsNCCHFdj_dywOpfalhPUQz78HAwDNqp127vWDFCP5Ie4pKJEbigRXjr0FwVgPcrhyR5A_RvbJibnGlw',
'欧美热歌榜':'10e2UIqDLgcQvg74T9Y6BU0mlxUHvstcHkV5UKHPkSDzyi3TRBORkKAKLtAspirLXvbbTwN4B8Jm_IYzis6kbq1VkFnbcpM28AV54vdGk9Trj1ycEP1FbclVuvxGAXuX',
'抖音热歌榜':'b0758Dllkk2pO4ripW44AWtSVQMHsmPgxNcVsJZmQKtNgoIi4dCd0CVRsKOIt69f6TFRffmQhJKCp6dBO9l1UGyiwUks3T5xlxHndE5oOz0NJogMrE49W6e2S8xc-sZb',
'古典音乐榜':'c6540n5sOF11X1eYySdN9elyUeEtSPKAzU0b-Pm41g5Kpt7WGD7E6pEVyNNMEsMb5OL5f5NC-bAt6Prqvesz9xIwYWDrHCbfAsWldzvzMDJCNybErloRvh98iev2oQ'
}
lists=[]
url='https://app.onenine.cc/m/api/search'
headers={'content-type':'application/x-www-form-urlencoded; charset=UTF-8','user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36'}
data={'data':'b91b2EKQBotaf_vdDg3ttFZwGBGJ_uFpbSf8oSjga2p7NKAsNCCHFdj_dywOpfalhPUQz78HAwDNqp127vWDFCP5Ie4pKJEbigRXjr0FwVgPcrhyR5A_RvbJibnGlw','v':'2'}
r=requests.post(url,headers=headers,data=data)#构造post请求
data=r.json()
print(len(data))
lists=data['data']['list'] #data=data['list'] #print(data)
downloadMusic(listType,quality,lists) if __name__=='__main__': signer=input("输入歌手名字:")
lists=getDownloadUrls(signer)
print('获取完成,开始下载音乐')
downloadMusic(signer,0,lists)#0表示下载流畅音质的
print('下载结束')
'''
#这个是下载歌单的
getSongsSheetSongs(0)
'''
'''
#这个是下载榜单的
getMusicListSongs('ACG音乐榜',1)
'''
# getSongsSheetSongs(1)
# getMusicListSongs('ACG音乐榜',1)
# print('end')
server.stop()
driver.quit()
效果
6.在Windows下安装Browsermob-proxy,可能比较麻烦一点,如果不会,可以找。
有其他问题也可以随时留言。
【音乐爬虫】Python爬虫-selenium+browsermob-proxy 解决动态网页 js渲染问题的更多相关文章
- Python:利用 selenium 库抓取动态网页示例
前言 在抓取常规的静态网页时,我们直接请求对应的 url 就可以获取到完整的 HTML 页面,但是对于动态页面,网页显示的内容往往是通过 ajax 动态去生成的,所以如果是用 urllib.reque ...
- scrapy和selenium结合抓取动态网页
1.安装python (我用的是2.7版本的) 2.安装scrapy: 详情请参考 http://blog.csdn.net/wukaibo1986/article/details/8167590 ...
- [爬虫]Python爬虫基础
一.什么是爬虫,爬虫能做什么 爬虫,即网络爬虫,大家可以理解为在网络上爬行的一直蜘蛛,互联网就比作一张大网,而爬虫便是在这张网上爬来爬去的蜘蛛咯,如果它遇到资源,那么它就会抓取下来.比如它在抓取一个网 ...
- 爬虫-Python爬虫常用库
一.常用库 1.requests 做请求的时候用到. requests.get("url") 2.selenium 自动化会用到. 3.lxml 4.beautifulsoup 5 ...
- 【python】Selenium隐藏控制台解决办法
一.起因: 使用selenium 驱动浏览器的时候,如果使用headless模式,会有dos窗口弹出,输出监听信息,有时不想看到,很是麻烦. 二.解决办法: 修改源码:Lib \ site-packa ...
- [爬虫]Python爬虫进阶
请跳转到以下页面查看: 爬虫进阶
- python爬虫之selenium、phantomJs
图片懒加载技术 什么是图片懒加载技术 图片懒加载是一种网页优化技术.图片作为一种网络资源,在被请求时也与普通静态资源一样,将占用网络资源,而一次性将整个页面的所有图片加载完,将大大增加页面的首屏加载时 ...
- Python爬虫教程
Python爬虫(1):基本原理 Python爬虫(2):Requests的基本用法 Python爬虫(3):Requests的高级用法 Python爬虫(4):Beautiful Soup的常用方法 ...
- Python爬虫的简单入门(一)
Python爬虫的简单入门(一) 简介 这一系列教学是基于Python的爬虫教学在此之前请确保你的电脑已经成功安装了Python(本教程使用的是Python3).爬虫想要学的精通是有点难度的,尤其是遇 ...
随机推荐
- Labview学习之路(六)条件结构
条件结构位于 函数---结构 子选版中.相当于c语言的switch语句, 结构: 分支选择器 分支器标签 分支子框图 可接数据类型 布尔型 布尔常量 真和假 错误信息簇 真和假 整型数值 单值 可 ...
- 【深入理解Java虚拟机】Java虚拟机运行时数据区
Java虚拟机运行时数据区 线程私有 程序计数器 1.当前线程所执行的字节码的行号指示器. 2.唯一不会发生OutOfMemoryError的区域 3.如果执行的是java方法,计数器值为虚拟机字节码 ...
- Unity3D中可重载虚函数的总结
重载虚函数:Unity3D中所有控制脚本的基类MonoBehaviour有一些虚函数用于绘制中事件的回调,也可以直接理解为事件函数,例如大家都很清楚的Start,Update等函数,以下做个总结. A ...
- Pandas | Dataframe的merge操作,像数据库一样尽情join
今天是pandas数据处理第8篇文章,我们一起来聊聊dataframe的合并. 常见的数据合并操作主要有两种,第一种是我们新生成了新的特征,想要把它和旧的特征合并在一起.第二种是我们新获取了一份数据集 ...
- 十分钟快速入门 Python
本文以 Eric Matthes 的<Python编程:从入门到实践>为基础,以有一定其他语言经验的程序员视角,对书中内容提炼总结,化繁为简,将这本书的精髓融合成一篇10分钟能读完的文章. ...
- Java程序取MySQL的datetime字段,如何消除时间尾后讨厌的".0"
Problem: 直接取datetime字段的内容会有".0"后缀,如2020-05-03 18:21:39.0,虽无伤大雅但总觉得有些别扭. Solution:使用转化函数Dat ...
- 【Java】一句话获得你的IP地址
System.out.println(java.net.InetAddress.getLocalHost().getHostAddress());
- 8成以上的java线程状态图都画错了,看看这个-图解java并发第二篇
本文作为图解java并发编程的第二篇,前一篇访问地址如下所示: 图解进程线程.互斥锁与信号量-看完还不懂你来打我 图形说明 在开始想写这篇文章之前,我去网上搜索了很多关于线程状态转换的图,我惊讶的发现 ...
- 车联网容器应用探索:5G下边缘云计算的车路协同实践
导语 | 5G网络下,多接入边缘计算(MEC)应运而生.结合TKEStack强大的集群管理能力和异构计算资源管理能力,腾讯打造了一个功能完备的边缘计算PaaS平台TMEC,提供了高精确度定位.视频处理 ...
- jenkins打包前端项目报 error: index-pack died of signal 15 问题解决
jenkins打包前端项目报 error: index-pack died of signal 15 问题解决 前几天用jenkins打包一个前端项目的时候出现了 error: index-pack ...