1. 程式人生 > >【Java爬蟲學習】WebMagic框架爬蟲學習實戰一:爬取網易雲歌單資訊,並存入mysql中

【Java爬蟲學習】WebMagic框架爬蟲學習實戰一:爬取網易雲歌單資訊,並存入mysql中

最近,需要使用Java進行爬蟲編寫,就去學了Java的爬蟲。因為之前學習了Scrapy框架,所以學Java的爬蟲使用了WebMagic框架,這個框架是基於Scrapy框架開發的。大家有興趣可以去看看操作文件:

 這個框架是國人開發的,所以說明文件都是中文,簡單易懂。

匯入WebMagic框架的方法在操作文件中有,在這就不講述了(建議看這篇文章前,先去看完操作文件。我是匯入jar包使用

我使用的版本是0.7.3。 

webmagic的各個模組不用再重複說,就說程式碼實現(預設您看完了操作文件

首先,建立一個Site物件,用於配置爬蟲,包括抓取間隔、重試次數,請求頭部等。

	private Site site = Site.me().setSleepTime(1000).setRetryTimes(3).addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0");

然後編寫process方法。process方法是定製爬蟲邏輯的核心部分,在這裡制定篩選規則。

page.putField()方法可以將資料儲存為key:value的形式,然後交給pipeline檔案處理。

後續網站的url和第一頁的url相差最後一個數值,所以採用字串拼接就可以。

然後使用page.addTargetRequest()方法將後續url加入爬取序列中去。

(basic是在前面定義的靜態成員) 

url="https://music.163.com/discover/playlist/?order=hot&cat=%E5%85%A8%E9%83%A8&limit=35&offset="

	public void process(Page page) {
		//將資料交給pipeline檔案處理
		page.putField("name",page.getHtml().xpath("//li/p[@class=dec]/a/text()").all());
		page.putField("src",page.getHtml().xpath("//li/p[@class=dec]/a/@href").all());
		page.putField("clicknum",page.getHtml().xpath("//li//div[@class=bottom]/span[@class=nb]/text()").all());
		page.putField("author",page.getHtml().xpath("//li//a[@class=nm]/text()").all());
		page.putField("homepage",page.getHtml().xpath("//li//a[@class=nm]/@href").all());
		//連續爬取後續網頁
		int flag = 35;
		String nexturl = null;
		for (int i=flag; i<1436;){
			nexturl = basic+i;
			page.addTargetRequest(nexturl);
			i+=35;
		}
		
	}

接下來在main函式中建立爬蟲,啟動三個執行緒進行爬取。

public static void main(String[] args){
		Spider.create(new music163())
		.addUrl(basic+"0")
		.addPipeline(new music163Pipeline())
		.thread(3)
		.run();
	}

將資料交給pipeline,在pipeline檔案中進行資料持久化,pipeline實現Pipeline介面

在pipeline檔案中,我使用List物件去存放需要持久化的內容。

items.get("key")方法用於從items物件中提取出對應key的值。

然後將歌單地址和作者主頁進行了url拼接

public void process(ResultItems items, Task t) {
		List<String> list_name = new ArrayList<String>();	//歌單名稱
		List<String> list_src = new ArrayList<String>();	//歌單地址
		List<String> list_clicknum = new ArrayList<String>();	//歌單播放量
		List<String> list_author = new ArrayList<String>();		//歌單作者
		List<String> list_author_homepage = new ArrayList<String>();//作者主頁
		//建立歌單地址和作者主頁地址的域 url
		String basicsrc = "https://music.163.com";
		//提取所有需要的資訊
		list_name.addAll(items.get("name"));
		list_src.addAll(items.get("src"));
		list_clicknum.addAll(items.get("clicknum"));
		list_author.addAll(items.get("author"));
		list_author_homepage.addAll(items.get("homepage"));
		int len=list_src.size();
		for(int i=0; i<len; i++){
			//修改歌單地址
			list_src.set(i, basicsrc+list_src.get(i));
			//修改作者個人主頁地址
			list_author_homepage.set(i, basicsrc+list_author_homepage.get(i));
		}

最後需要將資料持久化到mysql中。

將資料插入到資料庫中一般有四步:

  1. 載入資料庫驅動
  2. 連線資料庫
  3. 建立PrepareStatement物件,用於執行sql語句
  4. 關閉資源

下面的程式碼是根據這四步進行的,可以對照著看。

注意點:

1. DriverManager.getConnection() 方法的引數是url,user,password。

2. 批量處理:設定為手動提交,加入批量處理,進行批量處理,手動提交。

3. 將資料放入sql語句中,可以一次寫入一行,也可以一次將一列寫入。我這裡是使用迴圈,一次寫入一行。寫入的資訊與資料庫中對應位置的資料型別要相同。

4. 最後記得要關閉資源,資源關閉的順序。

		//建立連線物件
		Connection conn = null;
		//載入資料庫驅動
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			System.out.println("載入驅動失敗!");
			e.printStackTrace();
		}
		
	//連線資料庫
		String url = "jdbc:mysql://localhost:3306/test";  //這是資料庫的位置
		//進行連線
		try {
			conn = DriverManager.getConnection(url, "root", "lanqiyu0328");
			//因為需要批量提交資料,所以設定conn的提交方式為手動
			conn.setAutoCommit(false);
		} catch (SQLException e) {
			System.out.println("連線失敗!");
			e.printStackTrace();
		}
		
		//建立PrepareStatement物件,用來執行sql語句
		String sql = "insert ignore into music_list value(?,?,?,?,?)";
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(sql);  //建立PreparedStatement物件
			//將資料一次放入sql語句裡面
			int num = list_name.size();
			for(int i=0; i<num; i++){
				pstmt.setString(1, list_name.get(i));
				pstmt.setString(2, list_src.get(i));
				pstmt.setString(3, list_clicknum.get(i));
				pstmt.setString(4, list_author.get(i));
				pstmt.setString(5, list_author_homepage.get(i));
				//將資料加入批量處理
				pstmt.addBatch();
			}
			//批量處理資料之後手動提交
			pstmt.executeBatch();
			conn.commit();
			
		} catch (SQLException e) {
			System.out.println("物件建立失敗!");
			e.printStackTrace();
		}
		
		//關閉資源,先關閉PreparedStatement物件,然後關閉連線物件
		if (pstmt!=null){
			try {
				pstmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (conn!=null){
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

最後將PageProcessor和Pipeline程式碼貼上

PageProcessor:

package music163;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.fastjson.JSON;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.processor.PageProcessor;
import us.codecraft.webmagic.selector.Json;
/**
 * 爬取網頁版網易雲中歌單的基本資訊,並且將資料存入資料庫中
 */
public class music163 implements PageProcessor {
	private Site site = Site.me().setSleepTime(1000).setRetryTimes(3).addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0");
	//建立一個靜態成員用於拼接url。
	private static String basic = "https://music.163.com/discover/playlist/?order=hot&cat=%E5%85%A8%E9%83%A8&limit=35&offset=";
	@Override
	public Site getSite() {
		return site;
	}

	@Override
	public void process(Page page) {
		//將資料交給pipeline檔案處理
		page.putField("name",page.getHtml().xpath("//li/p[@class=dec]/a/text()").all());
		page.putField("src",page.getHtml().xpath("//li/p[@class=dec]/a/@href").all());
		page.putField("clicknum",page.getHtml().xpath("//li//div[@class=bottom]/span[@class=nb]/text()").all());
		page.putField("author",page.getHtml().xpath("//li//a[@class=nm]/text()").all());
		page.putField("homepage",page.getHtml().xpath("//li//a[@class=nm]/@href").all());
		//連續爬取後續網頁
		int flag = 35;
		String nexturl = null;
		for (int i=flag; i<1436;){
			nexturl = basic+i;
			page.addTargetRequest(nexturl);
			i+=35;
		}
		
	}

	public static void main(String[] args){
		Spider.create(new music163())
		.addUrl(basic+"0")
		.addPipeline(new music163Pipeline())
		.thread(3)
		.run();
	}
}

Pipeline:

package music163;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.pipeline.Pipeline;

public class music163Pipeline implements Pipeline {
	//處理item資料
	@Override
	public void process(ResultItems items, Task t) {
		List<String> list_name = new ArrayList<String>();	//歌單名稱
		List<String> list_src = new ArrayList<String>();	//歌單地址
		List<String> list_clicknum = new ArrayList<String>();	//歌單播放量
		List<String> list_author = new ArrayList<String>();		//歌單作者
		List<String> list_author_homepage = new ArrayList<String>();//作者主頁
		//建立歌單地址和作者主頁地址的域 url
		String basicsrc = "https://music.163.com";
		//提取所有需要的資訊
		list_name.addAll(items.get("name"));
		list_src.addAll(items.get("src"));
		list_clicknum.addAll(items.get("clicknum"));
		list_author.addAll(items.get("author"));
		list_author_homepage.addAll(items.get("homepage"));
		int len=list_src.size();
		for(int i=0; i<len; i++){
			//修改歌單地址
			list_src.set(i, basicsrc+list_src.get(i));
			//修改作者個人主頁地址
			list_author_homepage.set(i, basicsrc+list_author_homepage.get(i));
		}
		
		//建立連線物件
		Connection conn = null;
		//載入資料庫驅動
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			System.out.println("載入驅動失敗!");
			e.printStackTrace();
		}
		
	//連線資料庫
		String url = "jdbc:mysql://localhost:3306/test";  //這是資料庫的位置
		//進行連線
		try {
			conn = DriverManager.getConnection(url, "root", "lanqiyu0328");
			//因為需要批量提交資料,所以設定conn的提交方式為手動
			conn.setAutoCommit(false);
		} catch (SQLException e) {
			System.out.println("連線失敗!");
			e.printStackTrace();
		}
		
		//建立PrepareStatement物件,用來執行sql語句
		String sql = "insert ignore into music_list value(?,?,?,?,?)";
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(sql);  //建立PreparedStatement物件
			//將資料一次放入sql語句裡面
			int num = list_name.size();
			for(int i=0; i<num; i++){
				pstmt.setString(1, list_name.get(i));
				pstmt.setString(2, list_src.get(i));
				pstmt.setString(3, list_clicknum.get(i));
				pstmt.setString(4, list_author.get(i));
				pstmt.setString(5, list_author_homepage.get(i));
				//將資料加入批量處理
				pstmt.addBatch();
			}
			//批量處理資料之後手動提交
			pstmt.executeBatch();
			conn.commit();
			
		} catch (SQLException e) {
			System.out.println("物件建立失敗!");
			e.printStackTrace();
		}
		
		//關閉資源,先關閉PreparedStatement物件,然後關閉連線物件
		if (pstmt!=null){
			try {
				pstmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (conn!=null){
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
	
}

如果大家發現有錯誤,希望可以提出來。謝謝。