<div style="font-size:16px;">
<p>起因</p>
<p>前两天在知乎上看到一个帖子《网易云音乐有哪些评论过万的歌曲?》,一时技痒,用Java实现了一个简单的爬虫,这里简单记录一下。</p>
<p>最终的结果开放出来了,大家可以随意访问,请戳这里>>>>>> 网易云音乐爬虫结果。</p>
<p>爬虫简介</p>
<p align="center">网络爬虫是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本,一个通用的网络爬虫大致包含以下几个步骤:</p>
<p>网络爬虫的大致流程如上图所示,无论你是做什么样的爬虫应用,整体流程都是大同小异。现在,我们就根据网易云音乐来定制一个专门爬取音乐评论数量的特定网络爬虫。</p>
<p>前期准备</p>
<p>网页类型分析</p>
<p>首先,我们需要对网易云音乐整个网站有个大致的了解,进入网易云音乐首页,浏览后发现其大概有这么几种类型的URL:推荐页面</p>
<p>排行榜列表以及排行榜页面</p>
<p>歌单列表以及歌单页面</p>
<p>主播电台列表以及主播电台页面</p>
<p>歌手列表以及歌手页面</p>
<p>专辑列表(新碟上架)以及专辑页面</p>
<p>歌曲页面</p>
<p>我们最终需要爬取的数据在歌曲页面中,该页面里包含了歌曲的名称以及歌曲的评论数量。</p>
<p>另外,我们还需要尽可能多的获取歌曲页面,这些信息我们可以从前面6种类型的页面拿到。其中,歌单列表以及歌单页面结构最简单,歌单列表直接分页就可以拿到。因此,我们选择歌单页面作为我们的初始页面,然后歌单列表--歌单--歌曲一路爬下去即可。</p>
<p>设计数据模型</p>
<p>通过上述分析,我们可以知道我们要做两件事情,一是爬取页面歌单列表--歌单--歌曲,二是将最终的结果存储起来。因此,我们只需要两个对象,一个用来存储页面相关的信息,url、页面类型、是否被爬过(html和title作为临时数据存储),另外一个用来存储歌曲相关信息,url、歌曲名,评论数。因此,model类如下:</p>
<p>public class WebPage {<!-- --></p>
<p>public enum PageType {<!-- --></p>
<p>song, playlist, playlists;</p>
<p>}</p>
<p>public enum Status {<!-- --></p>
<p>crawled, uncrawl;</p>
<p>}</p>
<p>private String url;</p>
<p>private String title;</p>
<p>private PageType type;</p>
<p>private Status status;</p>
<p>private String html;</p>
<p>...</p>
<p>}</p>
<p>public class Song {<!-- --></p>
<p>private String url;</p>
<p>private String title;</p>
<p>private Long commentCount;</p>
<p>...</p>
<p>}</p>
<p>获取网页内容并解析</p>
<p>根据之前的分析,我们需要爬的页面有三种:歌单列表、歌单以及歌曲。为了验证想法的可行性,我们先用代码来解析这三种类型的网页,我们将网页内容获取以及解析的代码都放入CrawlerThread当中。</p>
<p>获取html</p>
<p>无论想要从什么网站中拿到数据,获取其html代码都是最最基础的一步,这里我们使用jsoup来获取页面信息,在CrawlerThread中添加如下代码:</p>
<p>private boolean fetchHtml(WebPage webPage) throws IOException {<!-- --></p>
<p>Connection.Response response = Jsoup.connect(webPage.getUrl()).timeout(3000).execute();</p>
<p>webPage.setHtml(response.body());</p>
<p>return response.statusCode() / 100 == 2 ? true : false;</p>
<p>}</p>
<p>public static void main(String[] args) throws Exception {<!-- --></p>
<p>WebPage playlists = new WebPage("http://music.163.com/#/discover/playlist/?order=hot&cat=%E5%85%A8%E9%83%A8&limit=35&offset=0", PageType.playlists);</p>
<p>CrawlerThread crawlerThread = new CrawlerThread();</p>
<p>crawlerThread.fetchHtml(playlists);</p>
<p>System.out.println(playlists.getHtml());</p>
<p>}</p>
<p>运行后即可看到html文本的输出</p>
<p>解析歌单列表页面</p>
<p>得到html后,我们来解析歌单列表,拿到页面中的所有歌单,Jsoup包含了html解析相关的功能,我们无需添加其他依赖,直接在CrawlerThread中添加如下代码:</p>
<p>private List parsePlaylist(WebPage webPage) {<!-- --></p>
<p>Elements songs = Jsoup.parse(webPage.getHtml()).select("ul.f-hide li a");</p>
<p>return songs.stream().map(e -> new WebPage(BASE_URL + e.attr("href"), PageType.song, e.html())).collect(Collectors.toList());</p>
<p>}</p>
<p>public static void main(String[] args) throws Exception {<!-- --></p>
<p>WebPage playlists = new WebPage("http://music.163.com/discover/playlist/?order=hot&cat=%E5%85%A8%E9%83%A8&limit=35&offset=0", PageType.playlists);</p>
<p>CrawlerThread crawlerThread = new CrawlerThread();</p>
<p>crawlerThread.fetchHtml(playlists);</p>
<p>System.out.println(crawlerThread.parsePlaylists(playlists));</p>
<p>}</p>
<p>解析歌单页面</p>
<p>和歌单列表页面类似,只需要将歌曲相 |
|