Java爬取網易雲音樂民謠並匯入Excel分析

SnailClimb發表於2017-12-21

前言

考慮到這裡有很多人沒有接觸過Java網路爬蟲,所以我會從很基礎的Jsoup分析HttpClient獲取的網頁講起。瞭解這些東西可以直接看後面的“正式進入案例”,跳過前面這些基礎。我匯入的是“Excel轉換工具XXL-EXCEL”這個開源專案然後在這個基礎上寫的這個Demo(下面會詳細分析).看了這篇文章還有問題,記得給我留言我會盡力幫你解決。如果文章哪裡有問題或者需要改進,也請留言告訴我,共同進步。

專案分析

幾天前用HttpClien+Jsoup把網易雲音樂的民謠抓取下來,可惜顯示效果是下面這樣的:

沒有匯入Excel之前
想著如果能像前幾天用開源的爬蟲爬取知乎後然後匯入Excel簡單分析也好一點,如下圖(下圖只是簡單的對匯入進去的一個人的關注者做了一個排序):
知乎爬蟲Excel分析
通過查閱文件知道Apache下有一個專門操作office各種文件的API:POI,用這個API的話操作起來原理也很簡單,但是很麻煩需要寫很多程式碼,而且條理也不是很清楚。所以去Github上找了找有沒有更方便的方法,Github有很多關於這方面的開源專案,經過各種嘗試,最終選擇了:《Java物件和Excel轉換工具XXL-EXCEL》,通過這個框架可以靈活的進行Java物件和Excel文件相互轉換。

然後最終就是下面這個效果:

網易雲Excel分析

案例基礎

①Jsoup解析網頁分析

Maven依賴:

		<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
		<dependency>
			<groupId>org.jsoup</groupId>
			<artifactId>jsoup</artifactId>
			<version>1.7.2</version>
		</dependency>
複製程式碼

請求網頁和解析網頁,我們都以推酷網:www.tuicool.com/為例子。 先看一個例項(本例項獲取的是推酷網首頁的熱門文章的資訊):

package news;

import java.io.IOException;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class TuicoolHotArticles {

	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		String url = "https://www.tuicool.com/";
		Document document = Jsoup.connect(url)
				.userAgent("Mozilla/5.0 (Windows NT 6.1; rv:30.0) Gecko/20100101 Firefox/30.0").get();

		// #id: 通過ID查詢元素,比如:#logo
		Elements items = document.select(".list_article_item ");
		System.out.println(items.size());
		for (Element item : items) {
			// 標題。(類名title下的 a標籤的title屬性獲取)
			String title = item.select(".title a").attr("title");
			// 標題圖片地址(類名article_thumb_image下的 img標籤的src屬性獲取)
			String picture_href = item.select(".article_thumb_image img").attr("src");
			// 時間 。(類名tip下的最後一個span標籤的文字獲取)
			String date = item.select(".tip span").last().text();
			// 作者 。(類名tip下的第一個span標籤的文字獲取)
			String author = item.select(".tip span").first().text();

			System.out.println("標題:               " + title);
			System.out.println("標題圖片地址:" + picture_href);
			System.out.println("釋出日期           " + date);
			System.out.println("作者                  " + author);
			System.out.println();
			System.out.println();
		}
	}

}

複製程式碼

例項結果:

結果
例項分析:

  1. 網頁元素分析

元素位置->右鍵->檢視元素(Inspent Element)

檢視元素
2. Jsoup解析 通過元素分析我們即可獲取所在div類名以及元素的其他屬性,通過這些屬性我們即可獲取該元素。上面程式碼我已經給你詳細的註釋,如果還有問題可以留言給我。

如果想了解更詳細的Jsoup操作請檢視 jsoup中文文件

②HttpClient請求網頁分析

Maven依賴:

		<!-- httpclient -->
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.5.4</version>
		</dependency>
複製程式碼

下面是一個獲取推酷首頁原始碼的例子:

package www.java1234.com.httpclientDemo2;

import java.io.IOException;

import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

/**
 * TODO 在www.java1234.com.httpclientDemo.HelloWorld2我們獲取推酷網熱門文章
 * 我們這裡通過模擬瀏覽器的方法來重新獲取
 * 
 * @date 2017年12月16日
 */
public class GetTuicoolHotArticles {

	public static void main(String[] args) throws ClientProtocolException, IOException {
		// TODO Auto-generated method stub
		CloseableHttpClient httpClient = HttpClients.createDefault(); // 建立httpClient例項
		HttpGet httpGet = new HttpGet("https://www.tuicool.com/"); // 建立httpget例項
		// 設定請求頭資訊Uer-Agent模擬瀏覽器
		httpGet.setHeader("User-Agent",
				"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0");
		CloseableHttpResponse response = httpClient.execute(httpGet); // 執行httpget請求
		// 獲取響應狀態
		//System.out.println("響應狀態為:" + response.getStatusLine());
		HttpEntity entity = response.getEntity(); // 獲取返回實體
		 System.out.println("網頁內容:" + EntityUtils.toString(entity, "utf-8"));
		// 獲取響應內容型別Content-Type
		// System.out.println("content-type:" + entity.getContentType());
		response.close(); // response關閉
		httpClient.close(); // httpClient關閉
	}

}
複製程式碼

這個例子有一點需要說明的是因為推酷網設定了簡單的反爬所以我們這裡必須設定請求頭資訊Uer-Agent模擬瀏覽器訪問,程式碼已經註釋。

如果想深入學習HttpClient,請檢視HttpClient官網

③Maven的簡單使用

網上有很多這面的教程,而且Maven這個工具真的很實用,不會的可以在網上找一找教程。再不會留言找我,我教,?嘿嘿。

正式進入本案例

完整原始碼下載:github.com/Snailclimb/… XXL-EXCEL的Maven依賴

<!-- http://repo1.maven.org/maven2/com/xuxueli/xxl-excel-core/ -->
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-excel</artifactId>
    <version>1.0.0</version>
</dependency>
複製程式碼

我一共新建了三個類:

類結構
Wangyiyun.java:網易雲音樂實體類。

WangyiyunMinyao.java:網易雲音樂工具類,用於獲取存放歌曲物件打得Arraylist集合。

TestWangyiyun.java:測試類,測試獲取網易雲音樂民謠資訊並且輸出到Excel中。 因為一次抓取的是一個頁面,所以抓取到控制檯有輸出,速度可能有點慢。 Wangyiyun.java

package com.xuxueli.poi.excel.test.model;

import org.apache.poi.hssf.util.HSSFColor;

import com.xuxueli.poi.excel.annotation.ExcelField;
import com.xuxueli.poi.excel.annotation.ExcelSheet;

@ExcelSheet(name = "網易雲音樂民謠分析", headColor = HSSFColor.HSSFColorPredefined.LIGHT_GREEN)
public class Wangyiyun {

	@ExcelField(name = "歌曲介紹")
	private String description;

	@ExcelField(name = "歌曲連結地址")
	private String href;

	@ExcelField(name = "歌曲播放次數")
	private int playNums;

	public Wangyiyun() {
	}

	public Wangyiyun(String description, String href, int playNums) {
		this.description = description;
		this.href = href;
		this.playNums = playNums;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getHref() {
		return href;
	}

	public void setHref(String href) {
		this.href = href;
	}

	public int getPlayNums() {
		return playNums;
	}

	public void setPlayNums(int playNums) {
		this.playNums = playNums;
	}

	@Override
	public String toString() {
		return "Wangyiyun [歌曲介紹=" + description + ", 歌曲連結地址=" + href + ", 歌曲播放次數=" + playNums + "]";
	}

}

複製程式碼

WangyiyunMinyao.java

package utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.xuxueli.poi.excel.test.model.Wangyiyun;
/**
 * TODO 該類用於獲取儲存在Arraylist中的歌曲物件
 * @date 2017年12月21日
 */

public class WangyiyunMinyao {

	/**
	 * 該方法爬取了歌曲標題和連結地址並且呼叫了GetTheNumberOfPlays()方法用於獲取歌曲連結地址頁面的詳細播放次數
	 * 
	 * @param url_str
	 * @param charset
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public static ArrayList<Wangyiyun> Crawl(String url_str, String charset)
			throws ClientProtocolException, IOException {
		// 獲取
		CloseableHttpClient hc=HttpClients.createDefault();
		//HttpClient hc = new DefaultHttpClient();
		HttpGet hg = new HttpGet(url_str);
		HttpResponse response = hc.execute(hg);
		HttpEntity entity = response.getEntity();

		InputStream htm_in = null;
		ArrayList<Wangyiyun> arrayList = new ArrayList<>();
		if (entity != null) {
			htm_in = entity.getContent();
			String htm_str = InputStream2String(htm_in, charset);
			Document doc = Jsoup.parse(htm_str);
			Elements links = doc.select("div[class=g-bd]").select("div[class=g-wrap p-pl f-pr]")
					.select("ul[class=m-cvrlst f-cb]").select("div[class=u-cover u-cover-1");
			for (Element link : links) {
				Elements lin = link.select("a");
				// 歌曲描述
				String description = lin.attr("title");
				// 歌曲連結地址
				String href = lin.attr("href");
				href = "http://music.163.com" + href;
				/*
				 * System.out.print(re_title + "       ");
				 * System.out.print(re_url + "       "); int nums =
				 * GetTheNumberOfPlays(re_url, charset);
				 */
				int nums =GetTheNumberOfPlays(href, charset);
				Wangyiyun wangyiyun = new Wangyiyun(description, href, nums);
				arrayList.add(wangyiyun);
			}
		}
		return arrayList;
	}

	/**
	 * 該方法爬取歌曲連結地址頁面的播放次數
	 * 
	 * @param url_str
	 * @param charset
	 * @throws ClientProtocolException
	 * @throws IOExceptionGet
	 *             the number of plays
	 */
	public static int GetTheNumberOfPlays(String url_str, String charset) throws ClientProtocolException, IOException {
		CloseableHttpClient hc=HttpClients.createDefault();
		HttpGet hg = new HttpGet(url_str);
		HttpResponse response = hc.execute(hg);
		HttpEntity entity = response.getEntity();

		InputStream htm_in = null;
		int nums = 0;
		if (entity != null) {
			htm_in = entity.getContent();
			String htm_str = InputStream2String(htm_in, charset);
			Document doc = Jsoup.parse(htm_str);
			String links = doc.select("div[class=u-title u-title-1 f-cb]").select("div[class=more s-fc3]")
					.select("strong").text();
			nums = Integer.parseInt(links);
		}
		return nums;
	}

	/*
	 * public static void saveHtml(String filepath, String str) {
	 * 
	 * try { OutputStreamWriter outs = new OutputStreamWriter(new
	 * FileOutputStream(filepath, true), "utf-8");
	 * outs.write("http://www.dailystrength.org" + str + "\r\n"); outs.close();
	 * } catch (IOException e) { System.out.println("Error at save html...");
	 * System.out.println(str); e.printStackTrace(); } }
	 */

	public static String InputStream2String(InputStream in_st, String charset) throws IOException {
		BufferedReader buff = new BufferedReader(new InputStreamReader(in_st, charset));
		StringBuffer res = new StringBuffer();
		String line = "";
		while ((line = buff.readLine()) != null) {
			res.append(line);
		}
		return res.toString();
	}
}
複製程式碼

TestWangyiyun.java

package com.xuxueli.poi.excel.test;

import java.io.IOException;
import java.util.ArrayList;

import org.apache.http.client.ClientProtocolException;

import com.xuxueli.poi.excel.ExcelExportUtil;
import com.xuxueli.poi.excel.test.model.Wangyiyun;

import utils.WangyiyunMinyao;

public class TestWangyiyun {

	public static void main(String[] args) throws ClientProtocolException, IOException {
		int page = 0;
		// 這裡只爬取了3頁資料
		ArrayList<Wangyiyun> wangyiyunArraylist=new ArrayList<>();
		for (int i = 0; i < 3; i++) {
			// 爬取
			String url_str = "http://music.163.com/discover/playlist/?order=hot&cat=民謠&limit=35&offset=" + page;
			ArrayList<Wangyiyun> arrayList = WangyiyunMinyao.Crawl(url_str, "utf-8");
			wangyiyunArraylist.addAll(arrayList);
			for (Wangyiyun wangyiyun : arrayList) {
				System.out.println(wangyiyun);
			}
			
			/**
			 * Excel匯出:Object 轉換為 Excel
			 */			
			// page引數加35(這個35是分析民謠欄)
			page = page + 35;
		}
		String filePath = "wangyiyun.xls";
		ExcelExportUtil.exportToFile(filePath, wangyiyunArraylist, wangyiyunArraylist);
	}

}
複製程式碼

容易出現的錯誤

匯入Maven依賴或者匯入我的Maven專案後專案上會有感嘆號,這種情況應該是Jar包下載出錯。(筆主在這裡卡了很長時間)

解決辦法:

Java爬取網易雲音樂民謠並匯入Excel分析
檢視錯誤->找到出錯的Jar->找到對應jar包存放位置->刪除該jar包->去官網自己下載。

補充

剛才知道大家可能因為jar包下載錯誤或者各種問題有問題。 下面是我的專案的效果圖。目前已經更新到Github。實在還有問題的可以把我的專案拷貝下來看看。 完整原始碼下載:github.com/Snailclimb/…(包含匯入Excel的工具原始碼)

效果圖

相關文章