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