Java如何解析html中的内容并存到数据库详解
作者:吳名氏 发布时间:2023-11-29 05:08:32
一、前言
最近接到一个任务,需要爬取五级行政区划的所有数据(大概71万条数据在),需要爬取的网站:行政区划 - 行政区划代码查询 发现这个网站不是用接口请求的,而且直接返回html代码,所以,去看了一下Java是如何解析html里面的内容
二、准备工作
我选用的是使用jsoup进行html的读取和解析,需要加入如下依赖:
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.8.3</version>
</dependency>
jsoup 是一款 Java 的HTML 解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jquery的操作方法来取出和操作数据。它是基于MIT协议发布的。
jsoup的主要功能如下:
1、从一个URL,文件或字符串中解析HTML;
2、使用DOM或CSS选择器来查找、取出数据;
3、可操作HTML元素、属性、文本;
示例代码:
//获取html的文档对象
Document doc = Jsoup.parse("http://www.dangdang.com");
//获取页面下id="content"的标签
Element content = doc.getElementById("content");
//获取页面下的a标签
Elements links = content.getElementsByTag("a");
for (Element link : links) {
//获取a标签下的href的属性值
String linkHref = link.attr("href");
//获取a标签下的文本内容
String linkText = link.text();
}
Elements这个对象提供了一系列类似于DOM的方法来查找元素,抽取并处理其中的数据。具体如下:
1、查找元素
getElementById(String id)
getElementsByTag(String tag)
getElementsByClass(String className)
getElementsByAttribute(String key) (and related methods)
Element siblings: siblingElements(), firstElementSibling(), lastElementSibling();nextElementSibling(), previousElementSibling()
Graph: parent(), children(), child(int index)
2、元素数据
attr(String key)获取属性
attr(String key, String value)设置属性
attributes()获取所有属性
id(), className() and classNames()
text()获取文本内容
text(String value) 设置文本内容
html()获取元素内
HTMLhtml(String value)设置元素内的HTML内容
outerHtml()获取元素外HTML内容
data()获取数据内容(例如:script和style标签)
tag() and tagName()
3、操作HTML和文本
append(String html), prepend(String html)
appendText(String text), prependText(String text)
appendElement(String tagName), prependElement(String tagName) html(String value)
三、开始爬取网站数据
直接上代码:
Test.java:
@Slf4j
@SpringBootTest
class Test {
@Resource
private PositionService positionService;
/**
* 爬取省市区网站
*/
@Test
public void test2() throws InterruptedException {
//一共五级
for (int i = 0 ; i < 5 ; i++) {
if (i == 0) {
List<PositionEntity> positionEntities = PositionUtils.reqPosition(PositionUtils.URL_HEAD);
savePosition(positionEntities, null, i);
continue;
}
List<Position> positions = positionService.findListByLevel(i);
for (Position parentPosition : positions) {
List<PositionEntity> positionEntities = PositionUtils.reqPosition(String.format("%s%s%s", PositionUtils.URL_HEAD, parentPosition.getSn(), PositionUtils.URL_TAIL));
savePosition(positionEntities, parentPosition, i);
}
}
}
/**
* 报错地址信息
*/
private void savePosition(List<PositionEntity> positionEntities, Position parentPosition, int i){
for (PositionEntity entity : positionEntities) {
Position position = new Position();
position.setSn(entity.getCode());
position.setFullInitials(PinyinUtils.strFirst2Pinyin((parentPosition != null ? parentPosition.getFullName() : "")+entity.getName()));
position.setFullName((parentPosition != null ? parentPosition.getFullName() : "")+entity.getName());
position.setLevel(i + 1);
position.setName(entity.getName());
position.setOrderNumber(0);
position.setPsn(parentPosition != null ? parentPosition.getSn() : 0L);
long count = positionService.countBySn(position.getSn());
if (count == 0) {
positionService.savePosition(position);
}
}
}
}
PositionService.java:
public interface PositionService {
void savePosition(Position position);
long countBySn(Long sn);
List<Position> findListByLevel(Integer level);
}
PositionServiceImpl.java:
@Service
public class PositionServiceImpl extends ServiceImpl<PositionMapper, Position> implements PositionService {
@Override
public void savePosition(Position position) {
baseMapper.insert(position);
}
@Override
public long countBySn(Long sn) {
return baseMapper.selectCount(new QueryWrapper<Position>().lambda().eq(Position::getSn, sn));
}
@Override
public List<Position> findListByLevel(Integer level) {
return baseMapper.selectList(new QueryWrapper<Position>().lambda().eq(Position::getLevel, level));
}
}
PositionMapper.java:
@Repository
public interface PositionMapper extends BaseMapper<Position> {
}
Position.java:
@Data
@TableName("position")
@EqualsAndHashCode()
public class Position implements Serializable {
@TableId(type = IdType.AUTO)
private Integer id;
/**
* 编码
*/
private Long sn;
/**
* 上级地址编码
*/
private Long psn;
/**
* 名称
*/
private String name;
/**
* 简称
*/
private String shortName;
/**
* 层级
*/
private Integer level;
/**
* 区号
*/
private String code;
/**
* 邮政编码
*/
private String zip;
/**
* 拼音
*/
private String spell;
/**
* 拼音首字母
*/
private String spellFirst;
/**
* 地址全名
*/
private String fullName;
/**
* 地址全名拼音首字母
*/
private String fullInitials;
/**
* 排序
*/
private Integer orderNumber;
}
PositionMapper.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wkf.workrecord.dao.PositionMapper">
</mapper>
PositionUtils.java:
public class PositionUtils {
public final static String URL_HEAD = "https://xingzhengquhua.bmcx.com/";
public final static String URL_TAIL = "__xingzhengquhua/";
public static List<PositionEntity> reqPosition(String url) throws InterruptedException {
String htmlStr = HttpUtils.getRequest(url);
//解析字符串为Document对象
Document doc = Jsoup.parse(htmlStr);
//获取body元素,获取class="fc"的table元素
Elements table = doc.body().getElementsByAttributeValue("bgcolor", "#C5D5C5");
//获取tbody元素
Elements children;
children = table.first().children();
//获取tr元素集合
Elements tr = children.get(0).getElementsByTag("tr");
List<PositionEntity> result = new ArrayList<>();
//遍历tr元素,获取td元素,并打印
for (int i = 3; i < tr.size(); i++) {
Element e1 = tr.get(i);
Elements td = e1.getElementsByTag("td");
if (td.size() < 2) {
break;
}
String name = td.get(0).getElementsByTag("td").first().getElementsByTag("a").text();
String code = td.get(1).getElementsByTag("td").first().getElementsByTag("a").text();
if (CheckUtils.isEmpty(name) || CheckUtils.isEmpty(code)) {
continue;
}
result.add(new PositionEntity(name, Long.parseLong(code)));
}
//防止ip被封
Thread.sleep(10000);
return result;
}
}
PinyinUtils.java:
public class PinyinUtils {
private final static HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
static {
format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
format.setVCharType(HanyuPinyinVCharType.WITH_V);
}
/**
* 字符串转拼音
*
* @param str
* 中文字符串
* @param fill
* 分隔符
* @return 返回中文的拼音串
*/
public static String str2Pinyin(String str, String fill) {
if (null == str) {
return null;
}
try {
StringBuilder sb = new StringBuilder();
if (fill == null)
fill = "";
boolean isCn = true;
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (i > 0 && isCn) {
sb.append(fill);
}
if (c == ' ') {
sb.append(fill);
}
// 1、判断c是不是中文
if (c >= '\u4e00' && c <= '\u9fa5') {
isCn = true;
String[] piyins = PinyinHelper.toHanyuPinyinStringArray(c, format);
if (null == piyins || 0 >= piyins.length) {
continue;
}
sb.append(piyins[0]);
} else {
// 不是中文
if (c >= 'A' && c <= 'Z') {
sb.append((char)(c + 32));
} else {
sb.append(c);
}
isCn = false;
}
}
return sb.toString();
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
}
return null;
}
/**
* 拼音首字母
*
* @param str
* 中文字符串
* @return 中文字符串的拼音首字母
*/
public static String strFirst2Pinyin(String str) {
if (null == str) {
return null;
}
try {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
// 1、判断c是不是中文
if (c >= '\u4e00' && c <= '\u9fa5') {
String[] piyins = PinyinHelper.toHanyuPinyinStringArray(c, format);
if (null == piyins || 0 >= piyins.length) {
continue;
}
sb.append(piyins[0].charAt(0));
} else {
// 不是中文
if (c >= 'A' && c <= 'Z') {
sb.append((char)(c + 32));
} else {
sb.append(c);
}
}
}
return sb.toString();
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
}
return null;
}
}
来源:https://blog.csdn.net/qq_37284798/article/details/125410786


猜你喜欢
- 一 概述GC(Garbage Collection),在程序运行过程中内存空间是有限的,为了更好的的使用有限的内存空间,GC会将不再使用的对
- Java是面向对象的编程语言,在我们开发Java应用的程序员的专业术语里,Java这个单词其实指的是Java开发工具,也就是JDK(Java
- 本文实例为大家分享了在Android中如何实现下拉导航选择菜单效果的全过程,供大家参考,具体内容如下关于下拉导航选择菜单效果在新闻客户端中用
- BufferedReader读取本地文件在使用BufferedWriter写入文件时,如果忘记关闭文件(close)同时也没有调用flush
- 最近项目中遇到了华为虚拟按键适配的问题,主页是个RecylerView(如下图),如果不做适配,在界面初始化完毕后,虚拟按键会遮挡页面或者空
- 本文实例为大家分享了java实现文件上传下载的具体代码,供大家参考,具体内容如下1.上传单个文件Controller控制层import ja
- 前言有时候我们想克隆一个List去做别的事,而不影响原来的List,我们直接在list后面加上小点点,发现并没有Clone这样的扩展函数。这
- 1.服务配置中心1.1 服务配置中心介绍首先我们来看一下,微服务架构下关于配置文件的一些问题:1.配置文件相对分散。在一个微服务架构下,配置
- 一、项目简述功能包括: 仓库管理,出入库管理,仓库人员管理,基本信息管理, 供应商信息,系统管理等等。二、项目运行环境配置: Jdk1.8
- 前言Spring 提供了 ApplicationContext 事件机制,可以发布和监听事件,这个特性非常有用。Spring 内置了一些事件
- ViewPager是一个常用的Android组件,不过通常我们使用ViewPager的时候不能实现左右无限循环滑动,在滑到边界的时候会看到一
- Thread parameterThread_t = null; private void Print_DetailForm_S
- Java RandomAccessFile 指定位置实现文件读取与写入RandomAccessFile是属于随机读取类,是可以对文件本身的内
- 今天给大家介绍一下如何实现一款简约时尚的安卓登陆界面。大家先看一下效果图当用户输入时动态出现删除按钮 现在先罗列一下技术点:1.如何使用圆角
- 基本步骤三数取中在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。在此我们采用三数取中法,也就是取左端、中间
- 什么是树?简单认识树 在生活中,有杨树,石榴树,枣树,而在计算机中的树呢,是一种非线性结构,是由 n(n>=0) 个有限节点
- 什么是异步?为什么要用它?异步编程提供了一个非阻塞的,事件驱动的编程模型。 这种编程模型利用系统中多核执行任务来提供并行,因此提供了应用的吞
- a.在.xaml文件中拖入一个datagrid,然后添加列名,使用Binding="{Binding 数据库中的列名称}"
- 本文实例讲解了Android自动提取短信验证码解决方案,分享给大家供大家参考,具体内容如下<uses-permission andro
- 前几天工作中一段业务代码需要一个变量每天从1开始递增。为此自己简单的封装了一个线程安全的计数器,可以让一个变量每天从1开始递增。当然了,如果