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

标签:java,html,解析
0
投稿

猜你喜欢

  • Spring Cloud Gateway入门解读

    2023-03-14 12:01:00
  • C#多线程之Thread中Thread.IsAlive属性用法分析

    2023-07-11 11:44:12
  • Android垂直切换的圆角Banner与垂直指示器相关介绍与应用详解

    2023-01-09 02:59:15
  • SpringBoot 上传文件判空以及格式检验流程

    2023-01-19 05:07:36
  • java-for循环问题

    2022-01-22 12:46:53
  • Android UI设计与开发之实现应用程序只启动一次引导界面

    2022-03-01 19:47:29
  • Spring实战之Bean的作用域singleton和prototype用法分析

    2023-03-23 20:48:37
  • Unity3D 单例模式和静态类的使用详解

    2022-09-22 08:16:19
  • Android使用Handler实现下载文件功能

    2023-05-21 03:23:08
  • Hibernate双向多对多映射关系配置代码实例

    2022-12-26 22:30:30
  • Mybatis查询时,区分大小写操作

    2021-08-11 14:10:54
  • Unity多屏幕设置的具体方案

    2023-12-12 12:05:24
  • Java遍历Properties所有元素的方法实例

    2022-09-08 14:58:24
  • 如何利用C#正则表达式判断是否是有效的文件及文件夹路径

    2022-03-06 23:34:45
  • 解决使用json-lib包实现xml转json时空值被转为空中括号的问题

    2022-10-20 02:12:14
  • 使用Logback日志保存到相对路径的操作

    2021-10-11 16:42:30
  • Android Studio卡很久(loading)的问题解决办法

    2023-12-08 02:22:23
  • MyBatis动态SQL特性详解

    2022-10-03 03:21:50
  • spring cglib 与 jdk 动态代理

    2021-07-19 20:28:43
  • Hystrix Dashboard断路监控仪表盘的实现详细介绍

    2022-07-29 05:27:26
  • asp之家 软件编程 m.aspxhome.com