java爬虫

java也能实现类似python request的爬虫,并且机制也是类似的,这里也以爬取贴吧为例。

首先是main方法,这里和python版本的处理类似,都需要先定义一个url(这里命名的变量是path),通过for循环实现翻页,使用JDBC存入数据库。

public static void main(String[] args){
	// 初始url贴吧首页;编码方式 utf-8
	String path = "https://tieba.baidu.com/f?kw=%E5%8D%8E%E4%B8%AD%E5%B8%88%E8%8C%83%E5%A4%A7%E5%AD%A6&ie=utf-8";
	String encoding = "utf-8";
	// for循环实现翻页
	for(int i = 50;i < 500;i+=50) {
		String result = GetPageHtml(path,encoding);
		path = "https://tieba.baidu.com/f?kw=%E5%8D%8E%E4%B8%AD%E5%B8%88%E8%8C%83%E5%A4%A7%E5%AD%A6&ie=utf-8&pn="+i;
		//WriteDownloadPage(result);  
     		System.out.println(CCNUtieba(result));  //用于查看抓取结果
	}
	// JDBC数据库存储
	Sql();
}

设置缓冲区,放置抓取数据

//0.全局缓冲区,临时放置抓取数据
	static ArrayList<String> a = new ArrayList<String>();   
	static ArrayList<String> b = new ArrayList<String>();
	static ArrayList<String> c = new ArrayList<String>();
	static ArrayList<String> d = new ArrayList<String>();

通过建立输入输出流来获取html源码

// 1.获取网页html源码
public static String GetPageHtml(String path, String encoding) {
	URL pageURL = null;
	StringBuilder pageBuffer = null;
	try {
		pageURL = new URL(path);
		BufferedReader reader = new BufferedReader(new InputStreamReader(pageURL.openStream(),encoding));
		String line;
		pageBuffer = new StringBuilder();
		while((line = reader.readLine())!=null){
			pageBuffer.append(line);
			}
	}
	catch(Exception e) {
		System.out.println("error");
	}
	return pageBuffer.toString();
}

将获得的html字符串存入本地,有时抓取结果为空,有两个原因:①正则表达式编写有误。②网页html源码有问题,可能不是直接加载数据。

// 2.将结果写入本地txt文件,用于检查html源码是否正确
public static void WriteDownloadPage(String str){
	FileWriter fw = null;
	StringReader fr = null; 
	try{
		// txt文件存储地址
		String fileName = "F:\\test.txt";  
		fw = new FileWriter(fileName);
		fr = new StringReader(str);
		char[] ch = new char[1024];
		int length = 0;
		while((length=fr.read(ch))!=-1){
			fw.write(ch,0,length);
		}
	}
	catch(Exception e){
		System.out.println("error");
	}
	finally{
		if(fw!=null){
			try{
				fw.close();
			}
			catch(IOException e){
				System.out.println(e);
			}
		}
		if(fr!=null){
			try{
				fw.close();
			}
			catch(IOException e){
				System.out.println(e);
			}
		}
	}
}

正则表达式匹配目标数据项。

// 3.爬取百度贴吧目录页上的链接、标题、作者及发布时间数据项
public static String CCNUtieba(String source) {	
	// 创建动态数组
	ArrayList<String> link = new ArrayList<String>();
	ArrayList<String> title = new ArrayList<String>();
	ArrayList<String> author = new ArrayList<String>();
	ArrayList<String> time = new ArrayList<String>();
		
	// 正则表达式1获取帖子链接
	String regex1 = "\"threadlist_title pull_left j_th_tit \">            <a rel=\"noreferrer\" href=\"(.+?)\"";
	Pattern p1 = Pattern.compile(regex1);
	Matcher m1 = p1.matcher(source);
	for(;m1.find();) {
		link.add("https://tieba.baidu.com"+m1.group(1));
	}
		
	// 正则表达式2获取帖子标题
	String regex2 = "class=\"j_th_tit \">(.+?)</a></div><div class";
	Pattern p2 = Pattern.compile(regex2);
	Matcher m2 = p2.matcher(source);
	for(;m2.find();) {
		// replaceAll方法过滤帖子标题中的emoji表情,emoji表情的编码方式是utf8mb4
		title.add(m2.group(1).replaceAll("[\\ud800\\udc00-\\udbff\\udfff\\ud800-\\udfff]", ""));
	}
		
	// 正则表达式3获取帖子作者
	String regex3 = "title=\"主题作者:(.+?)\"";
	Pattern p3 = Pattern.compile(regex3);
	Matcher m3 = p3.matcher(source);
	for(;m3.find();) {
		// replaceAll方法过滤发帖者名字中的emoji表情,emoji表情的编码方式是utf8mb4
		author.add(m3.group(1).replaceAll("[\\ud800\\udc00-\\udbff\\udfff\\ud800-\\udfff]", ""));
	}
		
	// 正则表达式4获取发帖时间
	String regex4 = "title=\"创建时间\">(.+?)</span>";
	Pattern p4 = Pattern.compile(regex4);
	Matcher m4 = p4.matcher(source);
	for(;m4.find();) {
		time.add(m4.group(1));
	}
		
	// 创建Result,拼接数据
	ArrayList<String> Result = new ArrayList<String>();  
		
	for(int i=0;i<link.size();i++) {
		Result.add("  链接:"+link.get(i)+"  标题:"+title.get(i)+"  作者:"+author.get(i)+"  发帖时间:"+time.get(i)+"\n");
		a.add(i,link.get(i));
		b.add(i,title.get(i));
		c.add(i,author.get(i));
		d.add(i,time.get(i));
	}
		
	String str= Result.toString();
	return str;
}

数据库存储,将爬取数据通过JDBC存储。

// 4.数据库操作,爬取数据通过JDBC存储
public static void Sql(){
		
	// 声明Connection对象
	Connection con;
	// 驱动程序名
	String driver = "com.mysql.jdbc.Driver";
	// URL指向要访问的数据库
	String url = "jdbc:mysql://localhost:3306/CCNUtieba";
	// MySQL配置时的用户名
	String user = "root";
	//MySQL配置时的密码
	String password = "root";
		
	try {
		//加载驱动程序
            Class.forName(driver);
            con = DriverManager.getConnection(url,user,password);
	    if(!con.isClosed())
	    System.out.println("成功连接数据库!");
	    PreparedStatement psql;
	    psql = con.prepareStatement("insert into ccnu (link,title,author,time) "+"values(?,?,?,?)");
	    for(int i=0;i<a.size();i++)
	    { 	
	    	psql.setString(1, a.get(i));
	    	psql.setString(2, b.get(i));
	    	psql.setString(3, c.get(i));
	    	psql.setString(4, d.get(i));
	    	psql.executeUpdate();  
	    }
	        con.close();
	        } catch(ClassNotFoundException e) {   
	        //数据库驱动类异常处理
	        	System.out.println("无法找到驱动!");   
	            e.printStackTrace();   
	        } catch(SQLException e) {
	        //数据库连接失败异常处理
	        	e.printStackTrace();  
	        	}catch (Exception e) {
	             // TODO: handle exception
	            e.printStackTrace();
	         }finally{
	             System.out.println("数据已成功存储至数据库");
	             }
}

可以看出,几乎都是发送请求、得到html源码然后regex正则匹配并存入数据库,但java版本会比python版本“笨重”很多,因此python爬虫通常用的更广泛。完整代码如下

package SpiderDemo;

import java.io.*;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.regex.*;

class SpiderDemo{
	
	//0.全局缓冲区,临时放置抓取数据
	static ArrayList<String> a = new ArrayList<String>();   
	static ArrayList<String> b = new ArrayList<String>();
	static ArrayList<String> c = new ArrayList<String>();
	static ArrayList<String> d = new ArrayList<String>();
	
	// 1.获取网页html源码
	public static String GetPageHtml(String path, String encoding) {
		URL pageURL = null;
		StringBuilder pageBuffer = null;
		try {
			pageURL = new URL(path);
			BufferedReader reader = new BufferedReader(new InputStreamReader(pageURL.openStream(),encoding));
			String line;
			pageBuffer = new StringBuilder();
			while((line = reader.readLine())!=null){
				pageBuffer.append(line);
				}
		}
		catch(Exception e) {
			System.out.println("error");
		}
		return pageBuffer.toString();
	}

	// 2.将结果写入本地txt文件,用于检查html源码是否正确
	public static void WriteDownloadPage(String str){
		FileWriter fw = null;
		StringReader fr = null; 
		try{
			// txt文件存储地址
			String fileName = "F:\\test.txt";  
			fw = new FileWriter(fileName);
			fr = new StringReader(str);
			char[] ch = new char[1024];
			int length = 0;
			while((length=fr.read(ch))!=-1){
				fw.write(ch,0,length);
			}
		}
		catch(Exception e){
			System.out.println("error");
		}
		finally{
			if(fw!=null){
				try{
					fw.close();
				}
				catch(IOException e){
					System.out.println(e);
				}
			}
			if(fr!=null){
				try{
					fw.close();
				}
				catch(IOException e){
					System.out.println(e);
				}
			}
		}
	}
	
	// 3.爬取百度贴吧目录页上的链接、标题、作者及发布时间数据项
	public static String CCNUtieba(String source) {
		
		// 创建动态数组
		ArrayList<String> link = new ArrayList<String>();
		ArrayList<String> title = new ArrayList<String>();
		ArrayList<String> author = new ArrayList<String>();
		ArrayList<String> time = new ArrayList<String>();
		
		// 正则表达式1获取帖子链接
		String regex1 = "\"threadlist_title pull_left j_th_tit \">            <a rel=\"noreferrer\" href=\"(.+?)\"";
		Pattern p1 = Pattern.compile(regex1);
		Matcher m1 = p1.matcher(source);
		for(;m1.find();) {
			link.add("https://tieba.baidu.com"+m1.group(1));
		}
		
		// 正则表达式2获取帖子标题
		String regex2 = "class=\"j_th_tit \">(.+?)</a></div><div class";
		Pattern p2 = Pattern.compile(regex2);
		Matcher m2 = p2.matcher(source);
		for(;m2.find();) {
			// replaceAll方法过滤帖子标题中的emoji表情,emoji表情的编码方式是utf8mb4
			title.add(m2.group(1).replaceAll("[\\ud800\\udc00-\\udbff\\udfff\\ud800-\\udfff]", ""));
		}
		
		// 正则表达式3获取帖子作者
		String regex3 = "title=\"主题作者:(.+?)\"";
		Pattern p3 = Pattern.compile(regex3);
		Matcher m3 = p3.matcher(source);
		for(;m3.find();) {
			// replaceAll方法过滤发帖者名字中的emoji表情,emoji表情的编码方式是utf8mb4
			author.add(m3.group(1).replaceAll("[\\ud800\\udc00-\\udbff\\udfff\\ud800-\\udfff]", ""));
		}
		
		// 正则表达式4获取发帖时间
		String regex4 = "title=\"创建时间\">(.+?)</span>";
		Pattern p4 = Pattern.compile(regex4);
		Matcher m4 = p4.matcher(source);
		for(;m4.find();) {
			time.add(m4.group(1));
		}
		
		// 创建Result,拼接数据
		ArrayList<String> Result = new ArrayList<String>();  
		
		for(int i=0;i<link.size();i++) {
			Result.add("  链接:"+link.get(i)+"  标题:"+title.get(i)+"  作者:"+author.get(i)+"  发帖时间:"+time.get(i)+"\n");
			a.add(i,link.get(i));
			b.add(i,title.get(i));
			c.add(i,author.get(i));
			d.add(i,time.get(i));
		}
		
		String str= Result.toString();
		return str;
		}
	
	// 4.数据库操作,爬取数据通过JDBC存储
	public static void Sql(){
		
		// 声明Connection对象
		Connection con;
	    // 驱动程序名
		String driver = "com.mysql.jdbc.Driver";
		// URL指向要访问的数据库
		String url = "jdbc:mysql://localhost:3306/CCNUtieba";
		// MySQL配置时的用户名
		String user = "root";
		//MySQL配置时的密码
		String password = "root";
		
		try {
			//加载驱动程序
		    Class.forName(driver);
		    con = DriverManager.getConnection(url,user,password);
		    if(!con.isClosed())
		    	System.out.println("成功连接数据库!");
		    PreparedStatement psql;
		    psql = con.prepareStatement("insert into ccnu (link,title,author,time) "+"values(?,?,?,?)");
		    for(int i=0;i<a.size();i++)
		    { 	
		    	psql.setString(1, a.get(i));
		    	psql.setString(2, b.get(i));
		    	psql.setString(3, c.get(i));
		    	psql.setString(4, d.get(i));
		    	psql.executeUpdate();  
		    }
		        con.close();
		        } catch(ClassNotFoundException e) {   
		        //数据库驱动类异常处理
		        	System.out.println("无法找到驱动!");   
		            e.printStackTrace();   
		        } catch(SQLException e) {
		        //数据库连接失败异常处理
		        	e.printStackTrace();  
		        	}catch (Exception e) {
		             // TODO: handle exception
		            e.printStackTrace();
		         }finally{
		             System.out.println("数据已成功存储至数据库");
		             }
		}
	
	// 5.main方法,调用其他模块实现爬虫
	public static void main(String[] args){
		
		// 初始url贴吧首页;编码方式 utf-8
		String path = "https://tieba.baidu.com/f?kw=%E5%8D%8E%E4%B8%AD%E5%B8%88%E8%8C%83%E5%A4%A7%E5%AD%A6&ie=utf-8";
		String encoding = "utf-8";
		
		// for循环实现翻页
		for(int i = 50;i < 500;i+=50) {
			String result = GetPageHtml(path,encoding);
			path = "https://tieba.baidu.com/f?kw=%E5%8D%8E%E4%B8%AD%E5%B8%88%E8%8C%83%E5%A4%A7%E5%AD%A6&ie=utf-8&pn="+i;
			//WriteDownloadPage(result);  
     		System.out.println(CCNUtieba(result));  //用于查看抓取结果
		}
		// JDBC数据库存储
		Sql();
	}
}