zoukankan      html  css  js  c++  java
  • 爬虫实战(二) 用Python爬取网易云歌单

    最近,博主喜欢上了听歌,但是又苦于找不到好音乐,于是就打算到网易云的歌单中逛逛

    本着 “用技术改变生活” 的想法,于是便想着写一个爬虫爬取网易云的歌单,并按播放量自动进行排序

    这篇文章,我们就来讲讲怎样爬取网易云歌单,并将歌单按播放量进行排序,下面先上效果图

    1、用 requests 爬取网易云歌单

    打开 网易云音乐 歌单首页,不难发现这是一个静态网页,而且格式很有规律,爬取起来应该十分简单

    按照以前的套路,很快就可以写完代码,无非就是分为下面几个部分:

    (1)获取网页源代码

    这里我们使用 requests 发送和接收请求,核心代码如下:

    import requests
    def get_page(url):
        # 构造请求头部
        headers = {
            'USER-AGENT':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
        }
        # 发送请求,获得响应
        response = requests.get(url=url,headers=headers)
        # 获取响应内容
        html = response.text
        return html
    

    (2)解析网页源代码

    解析数据部分我们使用 xpath(对于 xpath 的语法不太熟悉的朋友,可以看看博主之前的文章)

    文章传送门:

    核心代码如下:

    from lxml import etree
    # 解析网页源代码,获取数据
    def parse4data(self,html):
        html_elem = etree.HTML(html)
        # 播放量
        play_num = html_elem.xpath('//ul[@id="m-pl-container"]/li/div/div/span[@class="nb"]/text()')
        # 歌单名称
        song_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@title')
        # 歌单链接
        song_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@href')
        song_link = ['https://music.163.com/#'+item for item in song_href]
        # 用户名称
        user_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@title')
        # 用户链接
        user_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@href')
        user_link = ['https://music.163.com/#'+item for item in user_href]
        # 将数据打包成列表,其中列表中的每一个元素是一个字典,每一个字典对应一份歌单信息
        data = list(map(lambda a,b,c,d,e:{'播放量':a,'歌单名称':b,'歌单链接':c,'用户名称':d,'用户链接':e},play_num,song_title,song_link,user_title,user_link))
        # 返回数据
        return data
    
    # 解析网页源代码,获取下一页链接
    def parse4link(self,html):
        html_elem = etree.HTML(html)
        # 下一页链接
        href = html_elem.xpath('//div[@id="m-pl-pager"]/div[@class="u-page"]/a[@class="zbtn znxt"]/@href')
        # 如果为空,则返回 None;如果不为空,则返回链接地址
        if not href:
            return None
        else:
            return 'https://music.163.com/#' + href[0]
    

    (3)完整代码

    import requests
    from lxml import etree
    import json
    import time
    import random
    
    class Netease_spider:
        # 初始化数据
        def __init__(self):
            self.originURL = 'https://music.163.com/#/discover/playlist'
            self.data = list()
    
        # 获取网页源代码
        def get_page(self,url):
            headers = {
                'USER-AGENT':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
            }
            response = requests.get(url=url,headers=headers)
            html = response.text
            return html
    
        # 解析网页源代码,获取数据
        def parse4data(self,html):
            html_elem = etree.HTML(html)
            play_num = html_elem.xpath('//ul[@id="m-pl-container"]/li/div/div/span[@class="nb"]/text()')
            song_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@title')
            song_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@href')
            song_link = ['https://music.163.com/#'+item for item in song_href]
            user_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@title')
            user_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@href')
            user_link = ['https://music.163.com/#'+item for item in user_href]
            data = list(map(lambda a,b,c,d,e:{'播放量':a,'歌单名称':b,'歌单链接':c,'用户名称':d,'用户链接':e},play_num,song_title,song_link,user_title,user_link))
            return data
    
        # 解析网页源代码,获取下一页链接
        def parse4link(self,html):
            html_elem = etree.HTML(html)
            href = html_elem.xpath('//div[@id="m-pl-pager"]/div[@class="u-page"]/a[@class="zbtn znxt"]/@href')
            if not href:
                return None
            else:
                return 'https://music.163.com/#' + href[0]
    
        # 开始爬取网页
        def crawl(self):
            # 爬取数据
            print('爬取数据')
            html = self.get_page(self.originURL)
            data = self.parse4data(html)
            self.data.extend(data)
            link = self.parse4link(html)
            while(link):
                html = self.get_page(link)
                data = self.parse4data(html)
                self.data.extend(data)
                link = self.parse4link(html)
                time.sleep(random.random())
            # 处理数据,按播放量进行排序
            print('处理数据')
            data_after_sort = sorted(self.data,key=lambda item:int(item['播放量'].replace('万','0000')),reverse=True)
            # 写入文件
            print('写入文件')
            with open('netease.json','w',encoding='utf-8') as f:
                for item in data_after_sort:
                    json.dump(item,f,ensure_ascii=False)
    
    if __name__ == '__main__':
        spider = Netease_spider()
        spider.crawl()
        print('Finished')
    

    2、用 selenium 爬取网易云歌单

    然而,事情真的有这么简单吗?

    当我们运行上面的代码的时候,会发现解析网页源代码部分,返回的竟然是空列表!

    这是为什么呢?敲重点,敲重点,敲重点,这绝对是一个坑啊!

    我们重新打开浏览器,认真观察网页的源代码

    原来,我们所提取的元素被包含在 <iframe> 标签内部,这样我们是无法直接进行定位的

    因为 iframe 会在原有页面中加载另外一个页面,当我们需要获取内嵌页面的元素时,需要先切换到 iframe 中

    明白了原理之后,重新修改一下上面的代码

    思路是利用 selenium 获取原有网页,然后使用 switch_to.frame() 方法切换到 iframe 中,最后返回内嵌网页

    对 selenium 的使用以及环境配置不清楚的朋友,可以参考博主之前的文章

    需要修改的地方是获取网页源代码的函数,另外也需要在初始化数据的函数中实例化 webdriver,完整代码如下:

    from selenium import webdriver
    from lxml import etree
    import json
    import time
    import random
    
    class Netease_spider:
        # 初始化数据(需要修改)
        def __init__(self):
            # 无头启动 selenium
            opt = webdriver.chrome.options.Options()
            opt.set_headless()
            self.browser = webdriver.Chrome(chrome_options=opt)
            self.originURL = 'https://music.163.com/#/discover/playlist'
            self.data = list()
    
        # 获取网页源代码(需要修改)
        def get_page(self,url):
            self.browser.get(url)
            self.browser.switch_to.frame('g_iframe')
            html = self.browser.page_source
            return html
    
        # 解析网页源代码,获取数据
        def parse4data(self,html):
            html_elem = etree.HTML(html)
            play_num = html_elem.xpath('//ul[@id="m-pl-container"]/li/div/div/span[@class="nb"]/text()')
            song_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@title')
            song_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[1]/a/@href')
            song_link = ['https://music.163.com/#'+item for item in song_href]
            user_title = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@title')
            user_href = html_elem.xpath('//ul[@id="m-pl-container"]/li/p[2]/a/@href')
            user_link = ['https://music.163.com/#'+item for item in user_href]
            data = list(map(lambda a,b,c,d,e:{'播放量':a,'歌单名称':b,'歌单链接':c,'用户名称':d,'用户链接':e},play_num,song_title,song_link,user_title,user_link))
            return data
    
        # 解析网页源代码,获取下一页链接
        def parse4link(self,html):
            html_elem = etree.HTML(html)
            href = html_elem.xpath('//div[@id="m-pl-pager"]/div[@class="u-page"]/a[@class="zbtn znxt"]/@href')
            if not href:
                return None
            else:
                return 'https://music.163.com/#' + href[0]
    
        # 开始爬取网页
        def crawl(self):
            # 爬取数据
            print('爬取数据')
            html = self.get_page(self.originURL)
            data = self.parse4data(html)
            self.data.extend(data)
            link = self.parse4link(html)
            while(link):
                html = self.get_page(link)
                data = self.parse4data(html)
                self.data.extend(data)
                link = self.parse4link(html)
                time.sleep(random.random())
            # 处理数据,按播放量进行排序
            print('处理数据')
            data_after_sort = sorted(self.data,key=lambda item:int(item['播放量'].replace('万','0000')),reverse=True)
            # 写入文件
            print('写入文件')
            with open('netease.json','w',encoding='utf-8') as f:
                for item in data_after_sort:
                    json.dump(item,f,ensure_ascii=False)
    
    if __name__ == '__main__':
        spider = Netease_spider()
        spider.crawl()
        print('Finished')
    

    这样,得到目前网易云音乐中播放量排名前十的歌单如下(哈哈,又可以愉快的听歌啦):

    1. 2018年度最热新歌TOP100
    2. 听说你也在找好听的华语歌
    3. 精选 | 网络热歌分享
    4. 隔壁老樊的孤单
    5. 温柔暴击 | 沉溺于男友音的甜蜜乡
    6. 谁说翻唱不好听
    7. 若是心怀旧梦 就别再无疾而终
    8. KTV必点:有没有一首歌,唱着唱着就泪奔
    9. 化妆拍照BGM.
    10. 会讲故事的男声 歌词唱的太像自己

    注意:本项目代码仅作学习交流使用!!!

    版权声明:本博客属于个人维护博客,未经博主允许不得转载其中文章。
  • 相关阅读:
    postgresql修改postgres用户密码
    centos7 安装netstat命令工具
    sqlplus、lsnrctl命令工具不可用(libclntsh.so.11.1)
    oracle修改数据文件目录
    oracle数据库主主复制
    Spring--quartzJob配置
    TimerTask--spring配置
    SSM+Apache shiro--ehcache缓存清理
    SSM+Apache shiro--自定义realm
    ssm+Apache shiro--配置文件
  • 原文地址:https://www.cnblogs.com/wsmrzx/p/10684680.html
Copyright © 2011-2022 走看看