使用webcollector爬虫技术获取网易云音乐全部歌曲

最近在知乎上看到一个话题,说使用爬虫技术获取网易云音乐上的歌曲,甚至还包括付费的歌曲,哥瞬间心动了,这年头,好听的流行音乐或者经典老歌都开始收费了,只能听不能下载,着实很郁闷,现在机会来了,于是开始研究爬虫技术,翻阅各种资料,最终选择网友们一致认为比较好用的webcollector框架来实现。

首先,我们来认识一下webcollector,webcollector是一个无需配置,便于二次开发的爬虫框架,它提供精简的API,只需少量代码即可实现一个功能强大的爬虫,webcollector+hadoop是webcollector的hadoop版本,支持分布式爬取。并且在2.x版本中提供了selenium,可以处理javaScript生成的数据。我们边说便看图,看图说话,便于理解:

以上就是webcollector的架构图,我们来简单分析一下:

  • CrawlDB: 任务数据库,爬虫的爬取任务(类似URL列表)是存放在CrawlDB中的,CrawlDB根据DbUpdater和Generator所选插件不同,可以有多种形式,如文件、Redis、MySQL、MongoDB等。
  • Injector: 种子注入器,负责第一轮爬取时,向CrawlDB中提交爬取任务。在断点续爬的时候,不需要通过Injector向CrawlDB注入种子,因为CrawlDB中已有爬取任务。
  • Generator: 任务生成器,任务生成器从CrawlDB获取爬取任务,并进行过滤(正则、爬取间隔等),将任务提交给抓取器。
  • Fetcher: 抓取器,Fetcher是爬虫最核心的模块,Fetcher负责从Generator中获取爬取任务,用线程池来执行爬取任务,并对爬取的网页进行链接解析,将链接信息更新到CrawlDB中,作为下一轮的爬取任务。在网页被爬取成功/失败的时候,Fetcher会将网页和相关信息以消息的形式,发送到Handler的用户自定义模块,让用户自己处理网页内容(抽取、存储)。
  • DbUpdater: 任务更新器,用来更新任务的状态和加入新的任务,网页爬取成功后需要更新CrawlDB中的状态,对网页做解析,发现新的连接,也需要更新CrawlDB。
  • Handler: 消息发送/处理器,Fetcher利用Handler把网页信息打包,发送到用户自定义操作模块。
  • User Defined Operation: 用户自定义的对网页信息进行处理的模块,例如网页抽取、存储。爬虫二次开发主要就是自定义User Defined Operation这个模块。实际上User Defined Operation也是在Handler里定义的。
  • RequestFactory: Http请求生成器,通过RequestFactory来选择不同的插件,来生成Http请求,例如可以通过httpclient插件来使用httpclient作为爬虫的http请求,或者来使用可模拟登陆新浪微博的插件,来发送爬取新浪微博的http请求。
  • ParserFactory: 用来选择不同的链接分析器(插件)。爬虫之所以可以从一个网页开始,向多个网页不断地爬取,就是因为它在不断的解析已知网页中的链接,来发现新的未知网页,然后对新的网页进行同样的操作。

爬取逻辑:

第一层:爬取一个网页,http://www.apache.org/,解析网页,获取3个链接,将3个链接保存到CrawlDB中,设置状态为未爬取。同时将http://www.apache.org/的爬取状态设置为已爬取。结束第一轮。

第二层,找到CrawlDB中状态为未爬取的页面(第一层解析出来的3个链接),分别爬取,并解析网页,一共获得8个链接。和第一层操作一样,将解析出的链接放入CrawlDB,设置为未爬取,并将第二层爬取的三个页面,状态设置为已爬取。

第三层,找到CrawlDB中状态为未爬取的页面(第二层解析出来的8个链接)……………..

每一层都可以作为一个独立的任务去运行,所以可以将一个大型的广度遍历任务,拆分成一个一个小任务。爬虫里有个参数,设置爬取的层数,指的就是这个。

插件机制:

框架图中的 Injector、Generator、Request(由RequestFactory生成)、Parser(由ParserFactory生成)、DbUpdater、Response都是以插件实现的。制作插件往往只需要自定义一个实现相关接口的类,并在相关Factory内指定即可。

WebCollector内置了一套插件(cn.edu.hfut.dmic.webcollector.plugin.redis)。基于这套插件,可以把WebCollector的任务管理放到redis数据库上,这使得WebCollector可以爬取海量的数据(上亿级别)。

对于用户来说,关注的更多的不是爬虫的爬取流程,而是对每个网页要进行什么样的操作。对网页进行抽取、保存还是其他操作,应该是由用户自定义的。

所以我们使用WebCollector来写爬虫不用那么麻烦,只用集成爬虫框架里的BreadthCrawler类并重写visit方法即可,我们先来看下官网爬取知乎的例子:

  /*visit函数定制访问每个页面时所需进行的操作*/
    @Override
    public void visit(Page page) {
        String question_regex="^http://www.zhihu.com/question/[0-9]+";
        if(Pattern.matches(question_regex, page.getUrl())){
            System.out.println("正在抽取"+page.getUrl());
            /*抽取标题*/
            String title=page.getDoc().title();
            System.out.println(title);
            /*抽取提问内容*/
            String question=page.getDoc().select("div[id=zh-question-detail]").text();
            System.out.println(question);

        }
    }

    /*启动爬虫*/
    public static void main(String[] args) throws IOException{
        ZhihuCrawler crawler=new ZhihuCrawler();
        crawler.addSeed("http://www.zhihu.com/question/21003086");
        crawler.addRegex("http://www.zhihu.com/.*");
        crawler.start(5);
    }

}

我们来简单分析一下:

  1. visit()方法

    在整个抓取过程中,只要抓到一个复合的页面,wc都会回调该方法,并传入一个包含了所有页面信息的page对象。

  2. addSeed()

    添加种子,种子链接会在爬虫启动之前加入到上面所说的抓取信息中并标记为未抓取状态.这个过程称为注入。

  3. addRegex

    为一个url正则表达式, 过滤不必抓取的链接比如.js .jpg .css等,或者指定抓取链接的规则。比如我使用时有个正则为:http://news.hexun.com/2015-01-16/[0-9]+.html, 那么我的爬虫则只会抓取http://news.hexun.com/2015-01-16/172431075.html,http://news.hexun.com/2015-01-16/172429627.html 等news.hexun.com域名下2015-01-16日期的.html结尾的链接。

  4. start()

    表示启动爬虫,传入参数5表示抓取5层(深度为5),这个深度为5怎么理解呢,当只添加了一个种子, 抓这个种子链接为第1层, 解析种子链接页面跟据正则过滤想要的链接保存至待抓取记录. 那么第2层就是抓取1层保存的记录并解析保存新记录,依次类推。

至此,我们已经对webcollector有了一个大致的了解,更深入的理论知识我们就不再往下追究,毕竟高端的东西是需要更恒久的毅力和耐心去不断挖掘的,而目前我们只需要掌握简单的应用即可实现一个爬虫。

(一)需求分析:

OK,那我们先来分析一下我们此次的需求,我们要使用webcollector爬虫技术获取网易云音乐全部歌曲,我们先来看下一个网易云音乐的歌曲页面链接:http://music.163.com/#/album?id=2884361,我们会发现这个链接后面带有参数,传不同的id,可以得到不同的歌曲,所以,这就是一个模版,我们可以遍历整个网易云音乐,把其中url与上面类似的网页提取出来就可以得到网易云音乐的所有歌曲了,对吧?

那么,第二个问题,我们如何获取音乐的真实地址呢?这个通常是要用到抓包工具的,通过抓包工具获取HTTP请求下的头信息,从而得到请求的真实路径,我们通过抓包分析得到网易云音乐有一个api接口,可以得到歌曲的真实地址,api地址:http://music.163.com/api/song/detail,我们发现这个接口有几个参数:

  • id 传入上面得到的歌曲的id
  • ids ids是由id拼接而成的,ids = ‘%5B+‘ + id + ‘%5D‘(这里的%5B...%5d是js传参的时候防止乱码加的,这个在之前的项目里有遇到过)

然后我们可以把上面的API复制进浏览器,我们会得到一段json,里面有歌曲的音频源地址。

好了,经过分析,我们已经准备好了我们需要的东西,接下来就可以开始动手操作了。

(二)开发

开发就相对很简单,没有太多的类,只是普通的Java工程,引入相应的jar包即可。

1.进入WebCollector官方网站下载最新版本所需jar包。最新版本的jar包放在webcollector-version-bin.zip中。

2.打开Eclipse,选择File->New->Java Project,按照正常步骤新建一个Java项目。

在工程根目录下新建一个文件夹lib,将刚下载的webcollector-version-bin.zip解压后得到的所有jar包放到lib文件夹下。将jar包引入到build path中。

3、新建一个类继承BreadthCrawler,重写visit方法进行url的正则匹配,抽取出url,歌曲Id,歌曲名称,演唱者,url。以及真实路径。过程很简单,我们直接看代码:

package com.ax.myBug;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.csvreader.CsvWriter;

import cn.edu.hfut.dmic.webcollector.model.CrawlDatums;
import cn.edu.hfut.dmic.webcollector.model.Page;
import cn.edu.hfut.dmic.webcollector.plugin.berkeley.BreadthCrawler;
import org.json.*;  

/**
 * 获取网易云音乐所有歌曲写入csv文件
 * @author AoXiang
 */
public class GetAllSongs extends BreadthCrawler {

    private CsvWriter r = null;

    public void closeCsv() {
        this.r.close();
    }
    /**
     * 转换字节流
     * @param instream
     * @return
     * @throws IOException
     */
    public static byte[] readInputStream(InputStream instream) throws IOException {
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[]  buffer = new byte[1204];
        int len = 0;
        while ((len = instream.read(buffer)) != -1){
            outStream.write(buffer,0,len);
        }
        instream.close();
        return outStream.toByteArray();
    }
    /**
     * 根据URL获得网页源码
     * @param url 传入的URL
     * @return String
     * @throws IOException
     */
    public static String getURLSource(URL url) throws IOException {
        HttpURLConnection conn = (HttpURLConnection)url.openConnection();
        conn.setRequestMethod("GET");
        conn.setConnectTimeout(500000);//时间可以设置的久一点,如果控制台经常提示read time out
        InputStream inStream = conn.getInputStream();
        byte[] data = readInputStream(inStream);
        String htmlSource = new String(data);
        return htmlSource;
    }
    /**
     * 重写构造函数
     * @param crawlPath 爬虫路径
     * @param autoParse 是否自动解析
     */
    public GetAllSongs(String crawlPath, boolean autoParse) throws FileNotFoundException {
        super(crawlPath, autoParse);
        // 逗号进行分割,字符编码为GBK
        this.r = new CsvWriter("songId.csv", ‘,‘, Charset.forName("GBK"));
    }
    @Override
    public void visit(Page page, CrawlDatums next) {
        // 继承覆盖visit方法,该方法表示在每个页面进行的操作
        // 参数page和next分别表示当前页面和下个URL对象的地址
        // 生成文件songId.csv,第一列为歌曲id,第二列为歌曲名字,第三列为演唱者,第四列为歌曲信息的URL
        // 网易云音乐song页面URL地址正则
        String song_regex = "^http://music.163.com/song\\?id=[0-9]+";
        // 创建Pattern对象                          http://music.163.com/#/song?id=110411
        Pattern songIdPattern = Pattern.compile("^http://music.163.com/song\\?id=([0-9]+)");
        Pattern songInfoPattern = Pattern.compile("(.*?)-(.*?)-");
        // 对页面进行正则判断,如果有的话,将歌曲的id和网页标题提取出来,否则不进行任何操作
        if (Pattern.matches(song_regex, page.getUrl())) {
            // 将网页的URL和网页标题提取出来,网页标题格式:歌曲名字-歌手-网易云音乐
            String url = page.getUrl();
            @SuppressWarnings("deprecation")
            String title = page.getDoc().title();
            String songName = null;
            String songSinger = null;
            String songId = null;
            String infoUrl = null;
            String mp3Url = null;
            // 对标题进行歌曲名字、歌手解析
            Matcher infoMatcher = songInfoPattern.matcher(title);
            if (infoMatcher.find()) {
                songName = infoMatcher.group(1);
                songSinger = infoMatcher.group(2);
            }
            System.out.println("正在抽取:" + url);
            // 创建Matcher对象,使用正则找出歌曲对应id
            Matcher idMatcher = songIdPattern.matcher(url);
            if (idMatcher.find()) {
                songId = idMatcher.group(1);
            }
            System.out.println("歌曲:" + songName);
            System.out.println("演唱者:" + songSinger);
            System.out.println("ID:" + songId);
            infoUrl = "http://music.163.com/api/song/detail/?id=" + songId + "&ids=%5B+" + songId + "%5D";
            try {
                URL urlObject = new URL(infoUrl);
                // 获取json源码
                String urlsource = getURLSource(urlObject);
                JSONObject j = new JSONObject(urlsource);
                JSONArray a = (JSONArray) j.get("songs");
                JSONObject aa = (JSONObject) a.get(0);
                mp3Url = aa.get("mp3Url").toString();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            String[] contents = {songId, songName, songSinger, url, mp3Url};
            try {
                this.r.writeRecord(contents);
                this.r.flush();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    /**
     * 歌曲id爬虫开始
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        URL url = new URL("http://music.163.com/api/song/detail/?id=110411&ids=%5B110411%5D");
        String urlsource = getURLSource(url);
        System.out.println(urlsource);
        JSONObject j = new JSONObject(urlsource);
        JSONArray a = (JSONArray) j.get("songs");
        JSONObject aa = (JSONObject) a.get(0);
        System.out.println(aa.get("mp3Url"));
        GetAllSongs crawler = new GetAllSongs("crawler", true);
        // 添加初始种子页面http://music.163.com
        crawler.addSeed("http://music.163.com/#/album?id=604667405");
        // 设置采集规则为所有类型的网页
        crawler.addRegex("http://music.163.com/.*");
        // 设置爬取URL数量的上限
        crawler.setTopN(500000000);
        // 设置线程数
        crawler.setThreads(30);
        // 设置断点采集
        crawler.setResumable(false);
        // 设置爬虫深度
        crawler.start(5);
    }
}

(三)测试

直接运行Java程序,查看控制台

然后去到我们的workSpace,我们会发现提取出的歌曲信息已经写入了csv文件,

我们打开文件,可以看到里面已经拿到了我们想要的数据

OK,经过一番折腾,我们已经大功告成了,是不是很简单呢?当然,学习的过程也是很曲折的,有了这个技术,我们不仅可以爬取网易云音乐,还可以爬取各类新闻网站,拿到他们的数据来为我们自己所用,当然,现在的很多网站在安全方面都做的相当不错,或者比较抠门,不愿意资源共享,采用了反爬机制,所以,我们要做的就是更深入的更全面的了解爬虫技术,掌握其中的要领和精髓,灵活运用,那么,我相信再密不透风的网站我们也能爬的进去。因为我们的目标是星辰大海!

附上项目源码以及已经爬取的17万多的网易云音乐歌曲Excel:https://git.oschina.net/AuSiang/myBug/attach_files

时间: 2024-10-29 10:46:18

使用webcollector爬虫技术获取网易云音乐全部歌曲的相关文章

Python 获取网易云音乐热门评论

--> html { line-height: 1.6 } body { font-family: -apple-system-font, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif; background-color: #f3f3f3; line-height: inherit } body.ric

如何用Python网络爬虫爬取网易云音乐歌词

前几天小编给大家分享了数据可视化分析,在文尾提及了网易云音乐歌词爬取,今天小编给大家分享网易云音乐歌词爬取方法. 本文的总体思路如下: 找到正确的URL,获取源码: 利用bs4解析源码,获取歌曲名和歌曲ID: 调用网易云歌曲API,获取歌词: 将歌词写入文件,并存入本地. 本文的目的是获取网易云音乐的歌词,并将歌词存入到本地文件.整体的效果图如下所示: 基于Python网易云音乐歌词爬取 赵雷的歌曲 本文以民谣歌神赵雷为数据采集对象,专门采集他的歌曲歌词,其他歌手的歌词采集方式可以类推,下图展示

使用爬虫抓取网易云音乐热门评论生成好玩的词云

互联网爬虫是一个很有意思的技术,借由爬虫,我们可以做到很多好玩的事情--这其中就包括爬取评论. 词云就是个更好玩的技术,通过技术方法分析词语出现频率,生成可视化的图形,将文字内容用图形呈现,想想就很意思. 这次,我们就试着把这两个技术结合起来吧. 前言 网易云音乐一直是我向往的"神坛",听音乐看到走心的评论的那一刻,高山流水.于是今天来抓取一下歌曲的热门评论.并做成词云来展示,看看相对于这首歌最让人有感受的评论内容是什么. 做成词云的好处就是直观以及美观, 其他的我也想不出来有什么了.

使用python获取网易云音乐无损音频教程

博客园主页:http://www.cnblogs.com/handoing/ github项目:https://github.com/handoing/get-163-music 环境:Python 2.7.10 1.首先clone我们的项目: 项目目录: getMusic.py是我们要用到的. 2.现在我们随意访问一个网易云音乐的歌单,如图: 地址栏里的id及为我们所需要的,例子id为495132141 3.如果本地没有requests库的话需要先安装一个: 进入项目目录,执行python g

·「python爬虫入门」网易云音乐下载

2015-6-2 今天把昨天Git上看的一个下载网易云音乐歌单歌曲的脚本尝试看懂并修改 Git地址:https://github.com/keli/netease-music #! /usr/bin/env python # -*- coding: utf-8 -*- import urllib2 import json import os import sys import md5 import string import random # Set cookie cookie_opener =

获取网易云音乐歌曲URL

http://music.163.com/api/song/detail/?id=36990266&ids=%5B36990266%5D&csrf_token= 把ID换掉,输入到浏览器地址栏 http://m2.music.126.net/OjgrxW7zsP6QzPE9baQULA==/1415071473003094.mp3 找到与这个类似的就是了

网易云音乐评论爬虫(2):歌曲的全部评论

用过网易云音乐听歌的朋友都知道,网易云音乐每首歌曲后面都有很多评论,热门歌曲的评论更是接近百万或者是超过百万条.现在我就来分享一下如何爬取网易云音乐歌曲的全部评论,由于网易云音乐的评论都做了混淆加密处理,因此我们需要深入了解它的加密过程之后才能爬取到网易云音乐歌曲的全部评论. 加qq群813622576或vx;tanzhouyiwan免费领取Python学习资料 一,首先分析数据的请求方式 网易云音乐歌曲页面的URL形式为https://music.163.com/#/song?id=歌曲id号

python爬取网易云音乐歌曲评论信息

网易云音乐是广大网友喜闻乐见的音乐平台,区别于别的音乐平台的最大特点,除了"它比我还懂我的音乐喜好"."小清新的界面设计"就是它独有的评论区了------各种故事汇,各种金句频出.我们可以透过歌曲的评论数来判断一个歌者的市场分量和歌曲的流行度.言归正传,如果我们想要简单爬取指定歌曲的评论内容来做词云或者其他相关数据分析,有没有容易上手的好方法呢? 首先,我们打开网易云音乐的网页版:https://music.163.com/,随便选择一首歌曲,如林志炫版本的<

Java 爬虫入门(网易云音乐和知乎实例)

最近公司赶项目,过上了996的生活,周日还要陪老婆,实在没时间静下来写点东西,于是导致了swift编写2048的第三篇迟迟没有开工,在此说声抱歉,尽量抽时间在这周末补出来. 首先来介绍下爬虫的作用,爬虫主要用于大批量抓取网站中我们所需数据,其实就是模拟出http请求,之后解析分析所得的数据获取我们需要的信息的这么一个过程. 由于网上已经有很多现成的爬虫框架了,这里就不重复造轮子了,先给大家说一下原理,大家可以自己尝试写一个,至于具体实现这篇只带来一个框架的使用实例,让大家可以根据例子快速写出所需