【Java爬虫学习】WebMagic框架爬虫学习实战一:爬取网易云歌单信息,并存入mysql中

时间:2024-03-29 12:50:30

最近,需要使用Java进行爬虫编写,就去学了Java的爬虫。因为之前学习了Scrapy框架,所以学Java的爬虫使用了WebMagic框架,这个框架是基于Scrapy框架开发的。大家有兴趣可以去看看操作文档:

http://webmagic.io/docs/zh/

 这个框架是国人开发的,所以说明文档都是中文,简单易懂。

导入WebMagic框架的方法在操作文档中有,在这就不讲述了(建议看这篇文章前,先去看完操作文档。我是导入jar包使用

我使用的版本是0.7.3。 

【Java爬虫学习】WebMagic框架爬虫学习实战一:爬取网易云歌单信息,并存入mysql中

这个项目是用webmagic框架抓取网易云歌单,将爬取到的内容存入mysql中。这个demo之前用Python实现过,有兴趣的可以去看一下我之前的这篇文章    https://blog.csdn.net/chibuqikendeji/article/details/81225795 

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();
			}
		}
		
	}
	
}

如果大家发现有错误,希望可以提出来。谢谢。

emmmm,忘记贴结果图了,重新放上来,有两页,这里只放一页。这是程序完成时保存的信息,可能与现在的不符合,见谅。

【Java爬虫学习】WebMagic框架爬虫学习实战一:爬取网易云歌单信息,并存入mysql中