Java爬虫的使用案例及简单总结
通过三个简单的案例,来实现的,都是不加验证的情况下. 如果有拼图验证网上也有对应的实现方法自行查找即可. 这里仅仅是一个简单的Demo, 练习使用
0. 爬取网站的配置:
article:
config:
#中央新闻网-三农头条数据部分
ntvUrl: https://www.ntv.cn/
# 全国农技推广网- 农技动态部分
nongJi: https://www.natesc.org.cn/dtxx/index?CategoryId=959cd01c-e9fa-43d9-a04b-48317bdc3794
# 农技网基础路径
nongJiBaseUrl: https://www.natesc.org.cn
# 中国农网-热点推荐数据
farmerUrl: https://www.farmer.com.cn/
1. 依赖导入
<!-- 作用是把html界面封装为一个Document对象 类似于Python的 Beautiful Soup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.13.1</version>
</dependency>
<!-- 这个是处理未进行界面渲染的问题, 比如农机网的全国农技推广网-农技动态部分通过HTTPClient获取的就是未渲染的 -->
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.43.0</version>
</dependency>
<!-- 上面依赖这个jar包 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
2. 配置类
package com.saas.prod.common;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author : Cookie
* date : 2023/11/15
*/
@Component
@Data
// prefix 中的配置只能是小写,否则编译过不去好像
@ConfigurationProperties(prefix = "article.config")
public class ArticleMessageConfig {
/**
* 中央新闻网 三农头条数据url
*/
private String ntvUrl;
/**
* 农技中心动态
*/
private String nongJi;
/**
* 农技基础url
*/
private String nongJiBaseUrl;
/**
* 中国农网url
*/
private String farmerUrl;
}
中央新闻网-三农动态部分
@Override
public void insertNtvData() {
String url = messageConfig.getNtvUrl();
String html = HttpClientUtil.doGetForHTML(url);
Document document = Jsoup.parse(html);
Elements elements = document.getElementsByClass("question_rank");
Elements aElements = elements.get(0).select("a");
for (Element aElement : aElements) {
String href = aElement.attr("href");
String title = aElement.getElementsByTag("p").attr("title");
ArticleMessage build = ArticleMessage.builder()
.title(title)
.insertTime(LocalDateTime.now())
.build();
if (getHref(href, build))
break;
}
}
/**
* 三农头条数据填充
*
* @param url 路径
* @param build 对象
*/
private boolean getHref(String url, ArticleMessage build) {
String html = HttpClientUtil.doGetForHTML(url);
Document document = Jsoup.parse(html);
Elements article = document.select("div.art_con");
Elements select = document.select("p.mb10");
String source = select.select("span").get(0).text();
String createTime = select.select("span").get(1).text();
build.setSource(source);
build.setTime(createTime.split(" ")[0]);
build.setArticle(article.toString().replace("<div class=\"art_con\">", "<div id=\"content\">"));
String todayStartStr = getTodayStartStr();
// 如果是当天的新闻再插入到数据库,因为旧数据都已经插入了也就没有必要执行了
if (todayStartStr.compareTo(build.getTime()) <= 0) {
if (0 == articleMessageMapper.selectByTitle(build.getTitle()))
articleMessageMapper.insert(build);
} else {
// 说明剩余的数据都是旧数据,就不在操作直接返回
return true;
}
return false;
}
中国农网-热点推荐数据
@Override
public void insertFarmerData() {
Document parse = Jsoup.parse(HttpClientUtil.doGetForHTML(messageConfig.getFarmerUrl()));
Elements elements = parse.getElementsByClass("area-center w-486");
Elements a = elements.select("a");
for (Element element : a) {
String href = element.attr("href");
String title = element.text();
ArticleMessage build = ArticleMessage.builder()
.title(title)
.build();
if (setMessageForFarmer(href, build))
break;
}
}
/**
* 中国农网获取数据填充
*
* @param href 链接
* @param build 数据对象
*/
private boolean setMessageForFarmer(String href, ArticleMessage build) {
try {
Document parse = Jsoup.parse(HttpClientUtil.doGetForHTML(href));
Elements elementsByClass = parse.getElementsByClass("index-introduce");
Elements elements = elementsByClass.select("li");
String source = elements.first().select("span").text();
String date = elementsByClass.select("ul").first().select("div").first().select("span").text();
Elements article = parse.getElementsByClass("textList");
build.setSource(source);
build.setTime(date.split(" ")[0]);
build.setInsertTime(LocalDateTime.now());
build.setArticle(article.toString().replace("<div class=\"textList\">", "<div id=\"content\">"));
String todayStartStr = getTodayStartStr();
// 如果是当天的新闻再插入到数据库,因为旧数据都已经插入了也就没有必要执行了
if (todayStartStr.compareTo(build.getTime()) <= 0) {
if (0 == articleMessageMapper.selectByTitle(build.getTitle()))
articleMessageMapper.insert(build);
} else {
return true;
}
} catch (Exception e) {
// 这里有异常的及时提醒我查找问题
MailUtils.sendMail("yfs1024@163.com", "数据插入异常", "路径:" + href);
log.error("数据插入异常:{},路径{}", e.getMessage(), href);
return true;
}
return false;
}
全国农技推广网- 农技动态部分
@Override
public void insertNongJiData() {
Document document1 = getParsedDocument(messageConfig.getNongJi());
if (document1 == null)
return;
Element newsUl = document1.getElementById("newsUl");
Elements elements = newsUl.select("a");
for (Element element : elements) {
String href = element.attr("href");
String title = element.attr("title");
String finalUrl = messageConfig.getNongJiBaseUrl() + href;
ArticleMessage build = ArticleMessage.builder()
.title(title)
.build();
// 填充其他数据
if (setMessageForNongji(build, finalUrl))
break;
}
}
/**
* 填充农技动态中心数据
*
* @param build 对象
* @param finalUrl 路径
*/
private boolean setMessageForNongji(ArticleMessage build, String finalUrl) {
try {
Document parsedDocument = getParsedDocument(finalUrl);
if (parsedDocument == null)
return true;
Element newsTime = parsedDocument.getElementById("newsTime");
Elements elements = newsTime.getElementsByClass("span1");
String date = elements.first().getElementsByClass("span2").text();
String source = elements.get(1).getElementsByClass("span2").text();
Element content = parsedDocument.getElementById("content");
String finalContent = content.toString().replace("src=\"", "src=\"" + messageConfig.getNongJiBaseUrl());
build.setTime(date);
build.setSource(source);
build.setInsertTime(LocalDateTime.now());
// 填充文本数据包括图片
build.setArticle(finalContent);
String todayStartStr = getTodayStartStr();
// 插入当天的
if (todayStartStr.compareTo(date) <= 0) {
if (0 == articleMessageMapper.selectByTitle(build.getTitle()))
articleMessageMapper.insert(build);
} else {
// 说明剩余的数据都是旧数据,就不在操作直接返回
return true;
}
} catch (Exception e) {
MailUtils.sendMail("yfs1024@163.com", "数据插入异常", "路径:" + finalUrl);
log.error("数据插入异常:{},路径{}", e.getMessage(), finalUrl);
}
return false;
}
示例HTML:
<div class="index-introduce">
<ul>
<li>来源:
<span>中国农网</span>
</li>
<li>编辑:
<span>暴佳然</span>
</li>
<li>作者:
<span>杨志华</span>
</li>
<div>
<span>2023-11-17 08:45:11</span>
</div>
</ul>
<div class="fontSize">
<img class="add" src="https://www.farmer.com.cn//Public/newNongWang/images/字体变大.png?time=657" alt="">
<img class="sub" src="https://www.farmer.com.cn//Public/newNongWang/images/字体减小.png?time=277" alt="">
</div>
</div>
Document parse = Jsoup.parse(HttpClientUtil.doGetForHTML(href));
// 通过class获取节点
Elements elementsByClass = parse.getElementsByClass("index-introduce");
// 获取节点中所有的li标签数据
Elements elements = elementsByClass.select("li");
// 获取第一个li中的span标签中的文本内容 也就是这里的: 中国农网
String source = elements.first().select("span").text();
// 获取ul标签,再获取ul中的div获取span中的文本
String date = elementsByClass.select("ul").first().select("div").first().select("span").text();
方法总结:
获取标签的常用方法
getElementsByClass
// 通过class定位
Elements elementsByClass = parse.getElementsByClass("index-introduce");
getElementsById
// 通过id定位
Element newsTime = parsedDocument.getElementById("newsTime");
select
这里方法有很多选择方式,详细在最下面的链接中
通过Element对象获取对应的标签
getElementsByTag
通过标签获取
String title = aElement.getElementsByTag("p").attr("title");
获取属性常用方法
attr
String href = element.attr("href");
String title = element.attr("title");
总结:
- 通过HttpClient获取到界面的html
- 通过JSoup进行解析, 获得一个Document对象
- 通过方法一层一层的获取到其中的数据,或者属性
其他的常用方法链接:
jsoup使用指南