爬取网易云音乐精彩评论

题图:by google from Instagram

(一)

故事的小黄花 从出生那年就飘着 童年的荡秋千 随记忆一直晃到现在 Re So So Si Do Si La So La Si Si Si Si La Si La So 吹着前奏望着天空 我想起花瓣试着掉落 ……

小编猴哥有个爱好,喜欢一边听着熟悉的旋律,一边看着网易云音乐歌曲中的评论,特别是精彩评论。

评论内容,让人泫然流涕的故事,就是让人深思的段子。

(二)

某天,猴哥突发奇想,想将自己平时喜欢听的歌曲的精彩评论爬取下来。以后就可以直接阅读这些评论,无须打开网页。

说干就干。猴哥打开浏览器访问网易云音乐,随便点击某个歌曲页面。现在大多数网站都采用 Ajax 技术来获取数据。所以需要先判断网页是否采用该技术。

有个谷歌浏览器插件名为 Toggle JavaScript,它能控制页面中 javascript 启用或者禁用。

正常的页面长这样:

点击查看大图

当禁用页面 JavaScript 脚本之后,正常显示数据页面会变成一个空白页面。

点击查看大图

因此,可以断定网易云音乐加载数据方式采用 Ajax。

Ajax 技术可以在不刷新页面的情况下,利用嵌在 HTML 文档中的 JavaScript 脚本向服务器请求数据,然后更新到页面。想进一步确认数据来源,需要知道请求域名以及请求参数。

这就需要借助浏览器的开发者工具(一般按 F12 键会显示),根据抓取数据包进行分析。因为我们已经确定网站采用 Ajax ,所以直接在选择 XHR 过滤器过滤出所有请求。

点击查看大图

然后依次对每个 url 链接的 HTTP 请求进行分析,着重观察 HeadersPreview 选项。最后,猴哥发现 R_SO_4_186001?csrf_token= 请求中有我们需要的信息。Preview 中有字段跟精彩评论中用户名一致。

点击查看大图

继续切换到 Headers 确认请求域名以及请求需要携带的参数。

点击查看大图

那么爬取思路是:使用 POST 方式携带参数 paramsencSecKey 向该地址 http://music.163.com/weapi/v1/resource/comments/R_SO_4_186001?csrf_token= 发起HTTP 请求。返回结果中的 Json 数据就是用户评论数据。

(三)

既然思路明确,编写代码就是容易多了。

这里,猴哥使用列表来保存想爬取精彩评论的歌曲。

songs_url_list = [
    'http://music.163.com/#/song?id=186016',  # 晴天
    'http://music.163.com/#/song?id=186001',  # 七里香
    'http://music.163.com/#/song?id=27876900',  # Here We Are Again 《喜剧之王》电影插曲
    'http://music.163.com/#/song?id=439915614',  # 刚好遇见你
    'http://music.163.com/#/song?id=139774',  # The truth that you leave
    'http://music.163.com/#/song?id=29567189',  # 理想
    'http://music.163.com/#/song?id=308353',  # 钟无艳
    'http://music.163.com/#/song?id=31445772',  # 理想三旬
    'http://music.163.com/#/song?id=439915614',  # 刚好遇见你
    'http://music.163.com/#/song?id=28815250',  # 平凡之路
    'http://music.163.com/#/song?id=25706282',  # 夜空中最亮的星
    'http://music.163.com/#/song?id=436514312',  # 成都
]

然后截取每个链接中 id 字段的值。

def get_song_id(url):
    """ 从 url 中截取歌曲的 id """
    song_id = url.split('=')[1]
    return song_id

接着根据 id 拼接处请求的 url 地址,再使用 requests 发起 HTTP 请求。

for each in songs_url_list:
    start_spider(get_song_id(each))
    time.sleep(random.randint(5, 8))

def start_spider(song_id):
    """ 评论数据采用 AJAX 技术获得, 下面才是获取评论的请求地址 """
    url = 'http://music.163.com/weapi/v1/resource/comments/R_SO_4_{}?csrf_token='.format(song_id)

    headers = {
        'User-agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 UBrowser/6.2.3964.2 Safari/537.36',
        'Origin': 'http://music.163.com',
        'Referer': 'http://music.163.com/song?id={}'.format(song_id),
    }

    formdata = {
        'params': '57Wh2mgebLOOPQVBc+B2wz4sCCH/nXZFEoTc/XNySiqT0V7ZxUADzDNgTXXhYgAJ5BNMryMgxhdwNzF1GyxDZo3iR9/YYbWgCAQHC5DCDuObqvxNcOcnQDaRqJCrqQcrEABW1SwKitfbD3wMEyB4tJu+rU8goSwg2FP/PBBLs9DVs1iWdWGjV6CdrocA36Rs',
        'encSecKey': '63774137ba4f5cc60d1b6a3bc14985a9563a7bfdec4f3e74297ffc07514adf18f90620933a01c2db4ca989cc4e1dfc49789981424c294a34e48c2cbe7aa51533a5cc5b5776a9e499cd08770bc596655dbe8e001d1ed5fd47a27dd195128480820cc67a799d341f95d447e3522851f2b64ad1cb8350e2015b265b9e684179351c',
    }

    response = requests.post(url, headers=headers, data=formdata)
    print('请求 [ ' + url + ' ], 状态码为 ')
    print(response.status_code)
    # get_hot_comments(response.text)
    # 将数据写到 CSV 文件中
    write_to_file(get_hot_comments(response.text))

因为请求返回结果是 Json 数据,我们只需要精彩评论(HotComments)内容,所以需要对数据进行处理下。

def get_hot_comments(response):
    """ 获取精彩评论
    请求返回结果是 Json 数据格式, 使用 json.loads(response) 将其转化为字典类型, 就可以使用 key-value 形式获取值
    """
    data_list = []
    data = {}

    for comment in json.loads(response)['hotComments']:
        data['userId'] = comment['user']['userId']
        data['nickname'] = comment['user']['nickname']
        data['content'] = comment['content']
        data['likedCount'] = comment['likedCount']
        data_list.append(data)
        data = {}
    # print(data_list)
    return data_list

最后将数据保存到 CSV 文件中。

def write_to_file(datalist):
    print('开始将数据持久化……')
    file_name = '网易云音乐精彩评论.csv'

    with codecs.open(file_name, 'a+', 'GBK') as csvfile:
        filednames = ['用户Id', '昵称', '评论内容', '点赞数']
        writer = csv.DictWriter(csvfile, fieldnames=filednames)

        writer.writeheader()
        for data in datalist:
            print(data)
            try:
                writer.writerow({filednames[0]: data['userId'],
                                 filednames[1]: data['nickname'],
                                 filednames[2]: data['content'],
                                 filednames[3]: data['likedCount']})
            except UnicodeEncodeError:
                print("编码错误, 该数据无法写到文件中, 直接忽略该数据")

写到这里,小伙伴们应该了解如何爬取运用 Ajax 技术加载数据的网站了。可能某些网站的请求携带的参数只能使用一次,那就进一步数据包中 js 代码。推断出加密方式,自己再用代码还原。

哈哈,最后请允许我贴下爬取结果。

原文发布于微信公众号 - 极客猴(Geek_monkey)

原文发表时间:2018-06-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏北京马哥教育

用Python获取磁力种子

现在我们使用迅雷等工具下载资源的时候,基本上都只需要一个叫做磁力链接的东西就可以了,非常方便。

5183
来自专栏何俊林

Android Multimedia框架总结(十四)Camera框架初识及自定义相机案例

前言:国庆节告一段落,又是新一月,上月主要是围绕MediaPlayer相关展开,从今天开始,开始分析多媒体框架中的Camera模块,看下今天的Agenda: C...

3146
来自专栏编程

三种主流Web架构

往期精选 做WEB好几年了,各种语言和技术都稍有涉猎。今天心血来潮,突然想总结一下。其实不论什么技术,什么需求,通常WEB开发就是通过WEB前端管理一个或大或小...

7097
来自专栏Java3y

图书管理系统【总结】

感想 该项目是目前为止,我写过代码量最多的项目了.....虽然清楚是没有含金量的【跟着视频来写的】,但感觉自己也在进步中...... 写的过程中,出了不少的问题...

4015
来自专栏张宁的专栏

反爬虫和抗DDOS攻击技术实践

企鹅媒体平台媒体名片页反爬虫技术实践,分布式网页爬虫技术、利用人工智能进行人机识别、图像识别码、频率访问控制、利用无头浏览器PhantomJS、Selenium...

1.9K1
来自专栏流媒体人生

流媒体协议介绍(rtp/rtcp/rtsp/rtmp/mms/hls)

         Real-time Transport Protocol)是用于Internet上针对多媒体数据流的一种传输层协议。RTP协议详细说明了在互联...

7743
来自专栏游戏杂谈

【转】Android开发在路上:少去踩坑,多走捷径

本文是我订阅“腾讯大讲堂”公众帐号时,他们推送的一篇文章,但在腾讯大讲堂官网上我并没有找到这篇文章,不过其它专门“爬”公众号文章的网站倒是有。我觉得写的很不错。...

1193
来自专栏Java后端技术

几款效率神器助你走上人生巅峰之园友推荐

  在上篇文章中,我给大家推荐了我工作和生活中最常用的效率软件,引起了猿友的热烈反响,那么这篇文章,我们就来扒一扒猿友留言推荐的效率软件,望诸君笑纳~

1862
来自专栏Golang语言社区

谈谈go语言编程的并发安全

问题起因 在分布式存储开源项目 Weed-FS 中, 我发现了一个地方非并发安全(not concurrency-safety), 所以提交了一个 Weed-F...

4186
来自专栏FreeBuf

构建一个小巧的来电显示迷惑工具

但幸运的是,在正式任务开始之前我大约拥有两周的时间,让我去学习了解有关的技术知识。经过两周左右的折腾,我学到了许多关于调制解调器的安全测试技术,以及电话和Ast...

1870

扫码关注云+社区

领取腾讯云代金券