天天看點

jsoup爬取豆瓣電影top250

文章目錄

    • 0.準備工作
    • 1. 分析
    • 2. 構思
    • 3. 程式設計
      • 3.1 定義一個bean,用于儲存電影的資料
      • 3.2 按照之前的構思進行程式設計
    • 4.效果圖
    • 5.擷取資源
      • 5.1GitHub
      • 5.2百度雲

0.準備工作

下載下傳jsoup的jar包,有兩種方式:

  1. 使用maven架構進行建構
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
		<dependency>
			<groupId>org.jsoup</groupId>
			<artifactId>jsoup</artifactId>
			<version>1.12.1</version>
		</dependency>
           
  1. 到 https://mvnrepository.com/artifact/org.jsoup/jsoup 下載下傳jar包(jsoup-1.12.1.jar),然後加到類路徑中。

1. 分析

豆瓣電影top25的第一個頁面網址是 https://movie.douban.com/top250,我們檢查該網頁(google浏覽器:ctrl+shift+c)可以發現,ol标簽後面 li 标簽即為每個電影的内容。

jsoup爬取豆瓣電影top250
jsoup爬取豆瓣電影top250

以下是格式化了的li标簽的内容:(這是第一個電影《肖申克的救贖》)

<li>
	<div class="item">
		<div class="pic">
			<em class="">1</em>
			<a href="https://movie.douban.com/subject/1292052/">
				<img width="100" alt="肖申克的救贖"
					src="https://img3.doubanio.com/view/photo/s_ratio_poster/public/p480747492.webp" class="">
			</a>
		</div>
		<div class="info">
			<div class="hd">
				<a href="https://movie.douban.com/subject/1292052/" class="">
					<span class="title">肖申克的救贖</span> <span class="title">&nbsp;/&nbsp;The Shawshank Redemption</span> <span
						class="other">&nbsp;/&nbsp;月黑高飛(港) / 刺激1995(台)</span>
				</a>
				<span class="playable">[可播放]</span>
			</div>
			<div class="bd">
				<p class="">
					導演: 弗蘭克·德拉邦特 Frank Darabont &nbsp;&nbsp;&nbsp;主演: 蒂姆·羅賓斯 Tim Robbins /...
					<br>
					1994 &nbsp;/&nbsp;美國&nbsp;/&nbsp;犯罪 劇情
				</p>
				<div class="star">
					<span class="rating5-t"></span> <span class="rating_num" property="v:average">9.7</span> <span
						property="v:best" content="10.0"></span> <span>1539997人評價</span>
				</div>
				<p class="quote">
					<span class="inq">希望讓人自由。</span>
				</p>
			</div>
		</div>
	</div>
</li>
           

之後的工作基本上圍繞這段代碼來開展。

2. 構思

講代碼之前我們要現有思路,下面是我的思路:

  1. 擷取“單頁-一個電影”的資料
  2. 擷取“單頁-所有電影-25個”的資料
  3. 擷取“所有頁面-所有電影-25+10=250個”的資料

雖然這和我一開始想法有些差異,但是方向是一樣的。

3. 程式設計

3.1 定義一個bean,用于儲存電影的資料

定義Movie類,用于儲存電影的相關資料。包含:排名,電影名,電影的豆瓣頁網址,(國家,放映年份),平均評分,評價人數,引用(一句話評語)。

注意:為了友善在控制台檢視資料和儲存資料,這裡定義了兩個tostring方法,第一個是自動生成的,便于在控制台檢視資料;第二個是自己定義的,為了友善儲存資料到本地txt,如果你需要儲存TXT檔案,請選擇第二種形式。

Movie.java

代碼:

/**
 * @Title Movie.java
 * @Package xyz.yansheng.top250
 * @Description TODO
 * @author yansheng
 * @date 2019-08-12 15:34:16
 * @version v1.0
 */
package xyz.yansheng.top250;

/**
 * <p>Title: Movie</p>
 * <p>Description: 定義Movie類,用于儲存電影的相關資料。包含:排名,電影名,電影的豆瓣頁網址,(國家,放映年份),平均評分,評價人數,引用(一句話評語)。</p>
 * <p>Company: </p>
 * @author yansheng
 * @date 2019-08-12 15:34:16
 * @version v1.0 
 */
public class Movie {

	//	 排名,電影名,電影的豆瓣頁網址,(國家,放映年份),平均評分,評價人數,引用(一句話評語)

	private Integer rank;
	private String title;
	private String url;
	private Double ratingNum;
	private Integer ratingPeopleNum;
	private String quote;

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public void setRank(Integer rank) {
		this.rank = rank;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public Double getRatingNum() {
		return ratingNum;
	}

	public void setRatingNum(Double ratingNum) {
		this.ratingNum = ratingNum;
	}

	public Integer getRatingPeopleNum() {
		return ratingPeopleNum;
	}

	public void setRatingPeopleNum(Integer ratingPeopleNum) {
		this.ratingPeopleNum = ratingPeopleNum;
	}

	public String getQuote() {
		return quote;
	}

	public void setQuote(String quote) {
		this.quote = quote;
	}

	@Override
	public String toString() {
		
		// 形式1.為了友善控制台列印
		return "Movie [rank=" + rank + ", title=" + title + ", url=" + url + ", ratingNum=" + ratingNum
				+ ", ratingPeopleNum=" + ratingPeopleNum + ", quote=" + quote + "]";
		
		// 形式2.為了友善儲存資料到本地txt
		// 在将資料寫到本地txt儲存時,建議用下面這個格式,資料比較幹淨,有利用導入到資料庫等。
		//return rank + "," + title + "," + url + "," + ratingNum + "," + ratingPeopleNum + "," + quote + "\n";
	}

}
           

3.2 按照之前的構思進行程式設計

為了友善起見,我直接将這個方法定義在一個類中。所有方法在main方法裡面進行測試,可以按照方法進行測試。

注意:方法4,選用第二種tostring方法(上面有提到的)。

下面這個是

CrawlMovie.java

檔案中的

main()

方法,先看這個測試代碼,你會比較容易了解主要的幾個方法的作用:

public static void main(String[] args) {

		// 1.擷取“單頁-一個電影”的資料
		// 測試方法:Movie crawlMovie()
		// crawlMovie();

		// 2.擷取“單頁-所有電影-25個”的資料
		// 測試方法:Movie crawlMovie()
		/*final String URL = "https://movie.douban.com/top250";
		ArrayList<Movie> movies2 = crawlMovies(URL);
		for (Movie movie : movies2) {
			System.out.println(movie.toString());
		}*/
		
		// 3.擷取“所有頁面-所有電影-25+10=250個”的資料
		// 測試方法:ArrayList<Movie> crawlAllMovies()
		ArrayList<Movie> movies3 = crawlAllMovies();
		for (Movie movie : movies3) {
			System.out.println(movie.toString());
		}

		// 4.為了簡單起見,這裡僅僅是将資料儲存為txt檔案,不儲存到excel或者是資料庫。
		// 測試方法:void writeMovie(ArrayList<Movie> movies)
		writeMoviesToTxt(movies3);
	}
           

CrawlMovie.java

的所有代碼:

/**
 * @Title CrawlMovie.java
 * @Package xyz.yansheng.top250
 * @Description TODO
 * @author yansheng
 * @date 2019-08-12 15:47:06
 * @version v1.0
 */
package xyz.yansheng.top250;

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

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

/**
 * <p>Title: </p>
 * <p>Description: </p>
 * <p>Company: </p>
 * @author yansheng
 * @date 2019-08-12 15:47:06
 * @version v1.0 
 */
public class CrawlMovie {

	/**
	 * @Title main
	 * @author yansheng
	 * @version v1.0
	 * @date 2019-08-12 15:47:06
	 * @Description 爬取豆瓣電影top250:https://movie.douban.com/top250
	 */
	public static void main(String[] args) {

		// 1.擷取“單頁-一個電影”的資料
		// 測試方法:Movie crawlMovie()
		// crawlMovie();

		// 2.擷取“單頁-所有電影-25個”的資料
		// 測試方法:Movie crawlMovie()
		/*final String URL = "https://movie.douban.com/top250";
		ArrayList<Movie> movies2 = crawlMovies(URL);
		for (Movie movie : movies2) {
			System.out.println(movie.toString());
		}*/
		
		// 3.擷取“所有頁面-所有電影-25+10=250個”的資料
		// 測試方法:ArrayList<Movie> crawlAllMovies()
		ArrayList<Movie> movies3 = crawlAllMovies();
		for (Movie movie : movies3) {
			System.out.println(movie.toString());
		}

		// 4.為了簡單起見,這裡僅僅是将資料儲存為txt檔案,不儲存到excel或者是資料庫。
		// 測試方法:void writeMovie(ArrayList<Movie> movies)
		writeMoviesToTxt(movies3);
	}

	// 1.擷取“單頁-一個電影”的資料
	public static void crawlMovie() {

		// 1.擷取網頁
		final String URL = "https://movie.douban.com/top250";

		Document document = null;
		try {
			document = Jsoup.connect(URL).get();
		} catch (IOException e) {
			e.printStackTrace();
		}
		//		System.out.println("document:" + document);

		Movie movie = new Movie();

		// 2.選擇具體的電影的項,注意first方法,這裡先隻選取第一個進行測試
		Element itemElement = document.select("ol li").first();
		//		System.out.println("item:" + item.toString());

		// 3.1電影排名
		Element rankElement = itemElement.selectFirst("em");
		String rankString = rankElement.text();
		System.out.println("rankString:" + rankString.toString());
		movie.setRank(new Integer(rankString));

		// 3.2電影網址
		Element urlElement = itemElement.select("div.hd a").first();
		String urlString = urlElement.attr("href");
		System.out.println("urlString:" + urlString.toString());
		movie.setUrl(urlString);

		// 3.3電影名
		Element titleElement = urlElement.select("span.title").first();
		String titleString = titleElement.text();
		System.out.println("titleString:" + titleString.toString());
		movie.setTitle(titleString);

		// 3.4評分
		Element ratingNumElement = itemElement.select("div.star span.rating_num").first();
		String ratingNumString = ratingNumElement.text();
		System.out.println("ratingNumString:" + ratingNumString.toString());
		movie.setRatingNum(new Double(ratingNumString));

		// 3.5評價人數
		Element ratingPeopleNumElement = itemElement.select("div.star span").last();
		String ratingPeopleNumString = ratingPeopleNumElement.text();
		System.out.println("ratingPeopleNumString:" + ratingPeopleNumString.toString());

		// 注意這裡文本是:1539997人評價,我們需要選取其中人數,進行裁剪
		movie.setRatingPeopleNum(
				new Integer(ratingPeopleNumString.substring(0, ratingPeopleNumString.length() - 3)));

		// 3.6 一句話簡評
		Element quoteElement = itemElement.select("p.quote span.inq").first();
		String quoteString = quoteElement.text();
		System.out.println("quoteString:" + quoteString.toString());
		movie.setQuote(quoteString);

		System.out.println(movie.toString());
	}

	// 2.擷取“單頁-所有電影-25*1=25個”的資料
	public static ArrayList<Movie> crawlMovies(String URL) {

		// 1.擷取網頁
		//final String URL = "https://movie.douban.com/top250";

		Document document = null;
		try {
			document = Jsoup.connect(URL).get();
		} catch (IOException e) {
			e.printStackTrace();
		}
		//		System.out.println("document:" + document);

		// 2.選擇具體的電影的項,注意這裡和上面第一個不同,這裡是選取的所有電影的項
		Elements itemElement = document.select("ol li");

		ArrayList<Movie> movies = new ArrayList<Movie>(25);

		for (Element element : itemElement) {
			Movie movie = new Movie();
			// 3.1電影排名
			Element rankElement = element.selectFirst("em");
			String rankString = rankElement.text();
			movie.setRank(new Integer(rankString));

			// 3.2電影網址
			Element urlElement = element.select("div.hd a").first();
			String urlString = urlElement.attr("href");
			movie.setUrl(urlString);

			// 3.3電影名
			Element titleElement = urlElement.select("span.title").first();
			String titleString = titleElement.text();
			movie.setTitle(titleString);

			// 3.4評分
			Element ratingNumElement = element.select("div.star span.rating_num").first();
			String ratingNumString = ratingNumElement.text();
			movie.setRatingNum(new Double(ratingNumString));

			// 3.5評價人數
			Element ratingPeopleNumElement = element.select("div.star span").last();
			String ratingPeopleNumString = ratingPeopleNumElement.text();
			movie.setRatingPeopleNum(
					new Integer(ratingPeopleNumString.substring(0, ratingPeopleNumString.length() - 3)));

			// 3.6 一句話簡評
			Element quoteElement = element.select("p.quote span.inq").first();
			// 注意:這裡可能會沒有簡評,如125的《我不是藥神》,字元串會為null,如果是null,置為空字元串,否則會出現NPE問題
			String quoteString = null;
			if (quoteElement == null) {
				quoteString = "";
			} else {
				quoteString = quoteElement.text();
			}
			movie.setQuote(quoteString);

			movies.add(movie);
		}

		return movies;
	}

	// 3.擷取“所有頁面-所有電影-25+10=250個”的資料
	public static ArrayList<Movie> crawlAllMovies() {

		ArrayList<Movie> movies = new ArrayList<Movie>(250);

		/*注意檢視網址之間的特點,然後拼接字元串:
		 * 第1頁. https://movie.douban.com/top250
		 * 2. https://movie.douban.com/top250?start=25&filter=
		 * 3. https://movie.douban.com/top250?start=50&filter=
		 * 
		 * 通過觀察我們可以發現:(如果有懷疑直接到浏覽器測試拼接的網址是否正确)
		 * (1)前面都是一樣的:“https://movie.douban.com/top250”,
		 * (2)後面加一個查詢串:“?start=25*(-1)”,其中i表示頁數,根據意思我們可以知道其實是:指定指定該頁是從排名第幾的電影開始,
		 * 即如果是“?start=20”,那麼該頁第一個就是21。
		 * (3)按照(2)的意思,其實第一個頁面就是:https://movie.douban.com/top250?start=0&filter=
		 * (4)而最後面的"&filter=",其實有沒有都無所謂。
		 */
		String prefix = "https://movie.douban.com/top250";

		// 為了友善起見我先将網址拼接好,後面直接就可以用了
		ArrayList<String> urlList = new ArrayList<String>(10);
		for (int i = 0; i < 11; i++) {
			String url = prefix + "?start=" + new Integer(i * 25).toString() + "&filter=";
			urlList.add(url);
		}

		// 這裡直接調用上面的方法2(找到每頁的電影),将得到的(25個電影)集合添加到(250個電影)集合,通過循環周遊10個頁面。
		for (String url : urlList) {
			movies.addAll(crawlMovies(url));
		}
		
		return movies;
	}

	// 4.為了簡單起見,這裡僅僅是将資料儲存為txt檔案,不儲存到excel或者是資料庫。
	public static void writeMoviesToTxt(ArrayList<Movie> movies) {

		// 4.1 先将每個電影對象轉化為字元串
		ArrayList<String> moviesString = new ArrayList<String>(250);
		for (Movie movie : movies) {
			moviesString.add(movie.toString());
		}

		// 4.2寫位元組流
		try (FileOutputStream out = new FileOutputStream("豆瓣電影top250.txt");) {
			for (String string : moviesString) {
				out.write(string.getBytes());
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

           

4.效果圖

  1. 控制台輸出:(部分截圖)
    jsoup爬取豆瓣電影top250
  2. 儲存的txt檔案:(部分截圖)
    jsoup爬取豆瓣電影top250
  3. 處理後的excel表格資料:(部分截圖)
    jsoup爬取豆瓣電影top250

通過觀察我們可以發現,不是評分高排名就靠前!!

5.擷取資源

5.1GitHub

github倉庫:https://github.com/yansheng836/jsoup-test#user-content-2-爬取豆瓣電影top250

5.2百度雲

資源清單:

jsoup爬取豆瓣電影top250

說明:

  1. Excel表格資料是經過處理的,該程式沒有實作該功能(儲存資料到excel的功能)。
  2. movie.sql

    是mysql資料庫的一個表的轉儲sql檔案,直接在mysql執行該檔案可得到以資料庫表:
    jsoup爬取豆瓣電影top250

百度雲盤連結:https://pan.baidu.com/s/1fXr8psMndIzdu5r656m1ow

提取碼:f36z