Welcome

目前MSRA intern,现阶段从事文摘生成方向研究。对NLP感兴趣或对博客内容有疑惑及意见建议的,欢迎评论或添加我微信。此外如果有需要内推的同学,也欢迎来骚扰我。联系方式详见contact页面。

python爬取网易云歌单

python爬取网易云歌单

阅读数:1,765

背景

这学期报了一门海量数据处理,在数据处理前需要爬取一些内容。所以做了一个小练习,爬取网易云的歌单。其中包括歌单名称,播放量和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,以后多写机器学习。

 

 

Dodo

5条评论

匿名 发布于11:01 上午 - 7月 9, 2019

在Except里面, 不建议再使用http get请求, 这样出现异常的时候会直接退出程序.

匿名 发布于3:13 下午 - 2月 13, 2019

你好,你这个网站的源码能否提供给我一下,我觉得很不错

    Dodo 发布于2:28 下午 - 2月 16, 2019

    我使用的是wordpress,可以直接在服务器上部署,非常方便。

匿名 发布于10:29 下午 - 12月 15, 2018

add oil

    Dodo 发布于3:34 下午 - 12月 17, 2018

    Thank you