Java爬虫实例

一个简单的爬虫程序主要分为两部分:

1、抓取网站资源,也就是我们通过浏览器看到的页面资源(HTML源码)。

2、制定筛选规则,筛选出我们想要的数据。

 

      这里就以爬取csdn首页的文章信息为例实现一个简单的Java爬虫。我这里是个spring boot项目,jdk版本1.8。不得不说新版eclipse自带maven,自己再安装个STS组件构建srping boot项目简直方便快捷。话不多说直接放代码。

这里是我的pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>sqlTest</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>sqlTest</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.25</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
		    <groupId>org.jsoup</groupId>
		    <artifactId>jsoup</artifactId>
		    <version>1.11.3</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

既然是spring boot项目 下面就是properties文件(最基本的配置,就不写注解了):

spring.datasource.url=jdbc:mysql://localhost:3306/myTest?useUnicode=true&characterEncoding=utf8
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456

spring.jpa.database=MYSQL
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
#spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect

server.port=8084
server.servlet.context-path=/test

下面双手奉上实现类:

package com.example.demo;

import java.io.IOException;
import java.util.List;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.servlet.http.HttpServletRequest;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.demo.bean.PUser;
import com.example.demo.dao.UserDao;

@Controller
public class TestController {

	@Autowired(required = true)
	private UserDao userDao;

	@Autowired
	private EntityManager em;

	private Query query;

	@Transactional
	@RequestMapping("/hello1")    //该方法的访问路径
	public String test(Model model, HttpServletRequest request) {

		String url1 = "https://www.csdn.net";//csdn首页的地址
		crawler(url1);//开始爬虫
		System.out.println("爬虫结束");
		return "index.html";
	}

	//爬取csdn首页的文章信息
	private void crawler(String urlString) {
		try {

			// 文章名称
			String regex = ";}\">\\s[\\S\\s]+\\s<\\/a>\\s<\\/h2";
			// regex = "[\\u4e00-\\u9fa5]+";
			Pattern pattern = Pattern.compile(regex);
			//作者
			regex = ">\\s[\\u4e00-\\u9fa5_a-zA-Z0-9\\s,!“”?()\\|-]+\\s<\\/a";
			Pattern pattern2 = Pattern.compile(regex);
			
			Document document = Jsoup.connect(urlString).get();//通过url直接抓取网页的全部源码
			
			//把上面的document复制到笔记本 会发现每个文章区域所在div的class属性为list_con
			//Jsoup为我们封装了通过class筛选元素的方法 省的我们通过正则表达式自己筛选
			Elements elements = document.getElementsByClass("list_con");
			//因为首页有很多文章 所以需要遍历每个文章区域的源码
			for (Element element : elements) {
				if (element == null) {
					continue;
				}
				//转换为String类型 方便筛选
				String data = element.toString();
				
				String essayName = "";//文章名称
				String autherName = "";//作者
				String url = "";//文章地址
				String time = "";//时间
				String readNum = "";//阅读数
				String commonNum = "";//评论数
				
				//上面得到的data数据还是包括很多html源码 接下来 我们就要从这一小堆源码中是,筛选出我们需要的信息

				// 筛选出文章名称
				Matcher matcher = pattern.matcher(data);
				if (matcher.find()) {
					essayName = matcher.group();
					if(essayName.length()>20) {//这个正则可能需要匹配两次才能匹配到
						Matcher matcher2 = pattern.matcher(essayName.substring(1));
						if(matcher2.find()) {
							essayName = matcher2.group();
						}
					}
					essayName = essayName.substring(essayName.indexOf('>') + 2, essayName.indexOf('<') - 1);
					System.out.println("文章名称:"+essayName);
				}
				
				//查看源码 发现在这个div里第一个href属性就是对应文章地址
				int start = data.indexOf("href=\"");
				int end = data.indexOf("target", start);
				url = data.substring(start+6, end-2);
				System.out.println("文章地址:"+url);

				// 筛选出作者
				start = data.indexOf("class=\"name\"");
				end = data.indexOf("</dd>", start);
				String data2 = data.substring(start, end);
				matcher = pattern2.matcher(data2);
				if (matcher.find()) {
					autherName = matcher.group();
					autherName = autherName.substring(autherName.indexOf('>') + 2, autherName.indexOf('<') - 1);
					System.out.println("作者:"+autherName);
				}
				
				//筛选出时间
				start = data.indexOf("class=\"time\"");
				end = data.indexOf("</dd>", start);
				time = data.substring(start+13, end);
				time = time.replaceAll(" ", "");
				time = time.replaceAll("[\t\n\r]", "");
				System.out.println("时间:"+time);
				
				if(autherName.indexOf("laogt1")>=0) {
					System.out.println(data);
				}
				
				//筛选出阅读数
				start = data.indexOf("class=\"num\"");
				if(start>=0) {
					end = data.indexOf("</span>", start);
					readNum = data.substring(start+12, end);
				}
				System.out.println("阅读数:"+readNum);
				
				//筛选出评论数
				int start2 = data.indexOf("class=\"num\"", end);
				if(start2>=0) {
					int end2 = data.indexOf("</span>", start2);
					commonNum = data.substring(start2+12, end2);
				}				
				System.out.println("评论数:"+commonNum+"\n");
				//插入数据库
				String sql = "insert into csdn_essay (essay_name,url,auther_name,time,read_num,common_num) values ('"+essayName+"','"+url+"','"+autherName+"','"+time+"','"+readNum+"','"+commonNum+"')";
				em.createNativeQuery(sql).executeUpdate();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
}

以上是爬虫的工作成果:

 

    首页默认只能爬到38条数据 ,用浏览器访问可以看到,初次加载也是38条数据,但是每当滑到页面底部会自动继续加载10条数据。所以我们判断每次加载应该是传了什么参数来控制加载的数据,好奇心促使我用burp Suite拦截了以下请求包,结果发现:

    前两个参数每次访问都是一样的,而第三个参数show_offset看名字就非常可疑,前10位应该是个时间按戳,后面的我也不知道是啥,在url里拼接了这个参数并且修改了一下参数值,发现得到的数据是不大一样,但是跟之前的对比有重复的。 想来用浏览器每次访问csdn首页看到的推荐的文章还不一样呢,应该是后台有什么算法根据这个参数值得到了对应的的数据,这里就不深究了,有兴趣的小伙伴可以一起探讨一下,欢迎评论区留言~~~~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值