python爬取网易云歌单
写在前面:
我的博客已迁移至自建服务器:博客传送门,****博客暂时停止,如有机器学习方面的兴趣,欢迎来看一看。
此外目前我在gitHub上准备一些李航的《统计学习方法》的实现算法,目标将书内算法全部手打实现,欢迎参观并打星。GitHib传送门
背景
这学期报了一门海量数据处理,在数据处理前需要爬取一些内容。所以做了一个小练习,爬取网易云的歌单。其中包括歌单名称,播放量和url地址。
网易云还是具有一些反爬措施的,这里主要说以下几个方面以及我的应对措施。
-
无法直接访问
我没有实践过直接访问网易云音乐会不会被报404,所以这一条本质上不太确定的,我的做法是直接在浏览器中通过F12查看请求头,在代码中将请求头加入其中以此来模拟我是浏览器访问的,但是后期因为使用了PhantomJS,也就不需要请求头的,后期的时候请求头就没有再用了。 -
动态页面加载
动态的页面加载导致正常的页面爬虫无法工作,在浏览器上看到的很多内容,通过python读取到html后在内部找不到对应内容。对应的方法是使用PhantomJS。PhantomJS按照我自己的理解,是模拟了一个浏览器,这样子可以最大程度上让服务器认为我是普通用户的浏览器,从而防止被反爬。此外上一条中说到使用这个以后不需要再加请求头也是这个原因,我的身份已经变成了一个浏览器,所以也就不需要再使用请求头来伪装身份。 -
PhantomJS效率过低
PhantomJS毕竟是一个浏览器,在速度上有很大劣势,所以在使用时需要对其进行配置。从直观上来看,就是关闭浏览器的图片加载功能,打开浏览器缓存功能等等,所有的配置都和浏览器正常设置差不多,其代码如下:
service_args=[]
service_args.append('--load-images=no') ##关闭图片加载
service_args.append('--disk-cache=yes') ##开启缓存
service_args.append('--ignore-ssl-errors=true') ##忽略https错误
-
部分页面需要滚轮下拉才能显示更多内容
我的读取方式是读取用户的个人主页,其中包括‘自建歌单’以及‘收藏歌单’。可以点击https://music.163.com/#/user/home?id=113975098看到。
爬虫对自建歌单以及收藏歌单都进行爬取,一个用户爬取完后,在收藏歌单中随机找一个歌单进去,进入后是歌单内容详情,此时点击上方的作者,再次链接到该作者的个人主页,继续爬取自建歌单以及收藏歌单。
但是部分用户会自建很多歌单,这样导致收藏歌单不会直接显示,在浏览器中需要滚轮下滑才能继续加载,这就导致程序不能够读取到收藏歌单,也就不能继续爬取了。
这里有两种方法,一种是通过F12分析滚轮下滑后发出的请求,在程序中发出该请求就可以读取到后续列表。
老实说,这是我第一次爬页面,之前对html从来没有过了解,此外马上就要交作业了,简单看了看页面以后,决定用一种偷懒的方法去解决它。
我每次对一个用户的所有收藏歌单都读取到以后,将所有歌单的url读取到并存在一个list内部。爬完一个用户后,在list内部随机找到一个url作为下一个爬取对象,同时将该url在list内删除。当爬取到某个页面无法找到收藏歌单时,从该list内部再随机拿取一个重新进行分析。
这里有一个bug,假如很多用户都没有收藏歌单,剩余用户有很少的收藏歌单时,某一次运行中可能会出现list为空的情况,也就没有url可以用来分析了。另外盲目地扩充list对于内存的占用也是一大笔开销。但是对于2000条歌单的课程要求来说足够了。
实际上我爬了一万条歌单,运行正常。
以下是结果:
代码
from selenium import webdriver
import csv
import random
import os
#读取文件 若文件不存在则新建文件 同时写入表头
if os.path.exists('playList.csv'):
csvFile = open('playList.csv', 'a+', newline='', encoding="utf-8")
writer = csv.writer(csvFile)
else:
csvFile = open('playList.csv', 'a+', newline='', encoding="utf-8")
writer = csv.writer(csvFile)
writer.writerow(['标题', '播放数', '链接'])
#配置PhantomJS,提纲爬取速度
service_args=[]
service_args.append('--load-images=no') ##关闭图片加载
service_args.append('--disk-cache=yes') ##开启缓存
service_args.append('--ignore-ssl-errors=true') ##忽略https错误
playUrl = 'https://music.163.com/#/user/home?id=1320157310'
runCnt = 0 #程序运行次数计数
cPlayerList = [] #url列表 当当前url不合适时,从内部随机取出一个继续爬取
while runCnt < 10000: #爬取两千条记录
driver = webdriver.PhantomJS("D:\Python\Python37\Scripts\phantomjs.exe", service_args=service_args)
print(playUrl) #打印当前爬取的url
driver.get(playUrl) #获取链接
try: #在网页中若出现错误及时捕获
#-----------------读取用户自建歌单-------------------
driver.switch_to.frame('contentFrame')
data = driver.find_element_by_id('cBox').find_elements_by_tag_name('li')
for i in range(len(data)):
nb = data[i].find_element_by_class_name('nb').text
msk = data[i].find_element_by_css_selector('a.msk')
writer.writerow([msk.get_attribute('title'),
nb, msk.get_attribute('href')])
runCnt += 1
print('runCnt:', runCnt)
#-----------------读取用户收藏歌单-------------------
data = driver.find_element_by_id('sBox').find_elements_by_tag_name('li')
for i in range(len(data)):
nb = data[i].find_element_by_class_name('nb').text
nb.replace(u'\xa0', u' ');
msk = data[i].find_element_by_css_selector('a.msk')
#UnicodeEncodeError: 'gbk' codec can't encode character '\xa0' in position 2: illegal multibyte sequence
#csvFile = open('playList.csv', 'w', newline='', encoding="utf-8")
writer.writerow([msk.get_attribute('title'),
nb, msk.get_attribute('href')])
runCnt += 1
print('runCnt:', runCnt)
cPlayerList.append(msk.get_attribute('href'))
#从url列表中随机读取一个作为下一爬取的url
randIndex = int(random.uniform(0, len(cPlayerList)))
playUrl = cPlayerList[randIndex]
del cPlayerList[randIndex] #列表中取走后需要在列表中将该url删除
#转到的页面是歌单的详细页面,以下代码负责读取该页面中的作者页面,跳转到作者页面以便继续爬取
driver.get(playUrl)
driver.switch_to.frame('contentFrame')
data = driver.find_element_by_id('m-playlist').find_element_by_class_name('cntc').find_element_by_class_name('name')
playUrl = data.find_element_by_css_selector('a.s-fc7').get_attribute('href')
except:
#若出现错误,从url列表中继续取出一个url
randIndex = int(random.uniform(0, len(cPlayerList)))
playUrl = cPlayerList[randIndex]
del cPlayerList[randIndex]
print('页面发生异常,取出一个备用url,当前url剩余:', len(cPlayerList))
driver.get(playUrl)
driver.switch_to.frame('contentFrame')
data = driver.find_element_by_id('m-playlist').find_element_by_class_name('cntc').find_element_by_class_name(
'name')
playUrl = data.find_element_by_css_selector('a.s-fc7').get_attribute('href')
插入链接与图片
以后希望自己能多写一些机器学习相关的博客,现在进入研一也有一个多月了,慢慢的更确立了自己的学习方向。对爬虫有些兴趣但不是主要的学习方向,以后可能除非项目需要,否则不太会用爬虫去爬一些数据。
立下flag,以后多写机器学习。