• Java如何解析html里面的内容并存到数据库


    一、前言

            最近接到一个任务,需要爬取五级行政区划的所有数据(大概71万条数据在),需要爬取的网站:行政区划 - 行政区划代码查询 发现这个网站不是用接口请求的,而且直接返回html代码,所以,去看了一下Java是如何解析html里面的内容

    二、准备工作

            我选用的是使用jsoup进行html的读取和解析,需要加入如下依赖:

    1. <dependency>
    2. <groupId>org.jsoup</groupId>
    3. <artifactId>jsoup</artifactId>
    4. <version>1.8.3</version>
    5. </dependency>

            jsoup 是一款 Java 的HTML 解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jquery的操作方法来取出和操作数据。它是基于MIT协议发布的。

            jsoup的主要功能如下:

            1、从一个URL,文件或字符串中解析HTML;

            2、使用DOM或CSS选择器来查找、取出数据;

            3、可操作HTML元素、属性、文本; 

            示例代码:

    1. //获取html的文档对象
    2. Document doc = Jsoup.parse(input, "UTF-8", "http://www.dangdang.com");
    3. //获取页面下id="content"的标签
    4. Element content = doc.getElementById("content");
    5. //获取页面下的a标签
    6. Elements links = content.getElementsByTag("a");
    7. for (Element link : links) {
    8. //获取a标签下的href的属性值
    9. String linkHref = link.attr("href");
    10. //获取a标签下的文本内容
    11. String linkText = link.text();
    12. }

            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:

    1. @Slf4j
    2. @SpringBootTest
    3. class Test {
    4. @Resource
    5. private PositionService positionService;
    6. /**
    7. * 爬取省市区网站
    8. */
    9. @Test
    10. public void test2() throws InterruptedException {
    11. //一共五级
    12. for (int i = 0 ; i < 5 ; i++) {
    13. if (i == 0) {
    14. List<PositionEntity> positionEntities = PositionUtils.reqPosition(PositionUtils.URL_HEAD);
    15. savePosition(positionEntities, null, i);
    16. continue;
    17. }
    18. List<Position> positions = positionService.findListByLevel(i);
    19. for (Position parentPosition : positions) {
    20. List<PositionEntity> positionEntities = PositionUtils.reqPosition(String.format("%s%s%s", PositionUtils.URL_HEAD, parentPosition.getSn(), PositionUtils.URL_TAIL));
    21. savePosition(positionEntities, parentPosition, i);
    22. }
    23. }
    24. }
    25. /**
    26. * 报错地址信息
    27. */
    28. private void savePosition(List<PositionEntity> positionEntities, Position parentPosition, int i){
    29. for (PositionEntity entity : positionEntities) {
    30. Position position = new Position();
    31. position.setSn(entity.getCode());
    32. position.setFullInitials(PinyinUtils.strFirst2Pinyin((parentPosition != null ? parentPosition.getFullName() : "")+entity.getName()));
    33. position.setFullName((parentPosition != null ? parentPosition.getFullName() : "")+entity.getName());
    34. position.setLevel(i + 1);
    35. position.setName(entity.getName());
    36. position.setOrderNumber(0);
    37. position.setPsn(parentPosition != null ? parentPosition.getSn() : 0L);
    38. long count = positionService.countBySn(position.getSn());
    39. if (count == 0) {
    40. positionService.savePosition(position);
    41. }
    42. }
    43. }
    44. }

             PositionService.java:

    1. public interface PositionService {
    2. void savePosition(Position position);
    3. long countBySn(Long sn);
    4. List<Position> findListByLevel(Integer level);
    5. }

             PositionServiceImpl.java:

    1. @Service
    2. public class PositionServiceImpl extends ServiceImpl<PositionMapper, Position> implements PositionService {
    3. @Override
    4. public void savePosition(Position position) {
    5. baseMapper.insert(position);
    6. }
    7. @Override
    8. public long countBySn(Long sn) {
    9. return baseMapper.selectCount(new QueryWrapper<Position>().lambda().eq(Position::getSn, sn));
    10. }
    11. @Override
    12. public List<Position> findListByLevel(Integer level) {
    13. return baseMapper.selectList(new QueryWrapper<Position>().lambda().eq(Position::getLevel, level));
    14. }
    15. }

            PositionMapper.java:

    1. @Repository
    2. public interface PositionMapper extends BaseMapper<Position> {
    3. }

            Position.java:

    1. @Data
    2. @TableName("position")
    3. @EqualsAndHashCode()
    4. public class Position implements Serializable {
    5. @TableId(type = IdType.AUTO)
    6. private Integer id;
    7. /**
    8. * 编码
    9. */
    10. private Long sn;
    11. /**
    12. * 上级地址编码
    13. */
    14. private Long psn;
    15. /**
    16. * 名称
    17. */
    18. private String name;
    19. /**
    20. * 简称
    21. */
    22. private String shortName;
    23. /**
    24. * 层级
    25. */
    26. private Integer level;
    27. /**
    28. * 区号
    29. */
    30. private String code;
    31. /**
    32. * 邮政编码
    33. */
    34. private String zip;
    35. /**
    36. * 拼音
    37. */
    38. private String spell;
    39. /**
    40. * 拼音首字母
    41. */
    42. private String spellFirst;
    43. /**
    44. * 地址全名
    45. */
    46. private String fullName;
    47. /**
    48. * 地址全名拼音首字母
    49. */
    50. private String fullInitials;
    51. /**
    52. * 排序
    53. */
    54. private Integer orderNumber;
    55. }

            PositionMapper.xml:

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    3. <mapper namespace="com.wkf.workrecord.dao.PositionMapper">
    4. </mapper>

            PositionUtils.java:

    1. public class PositionUtils {
    2. public final static String URL_HEAD = "https://xingzhengquhua.bmcx.com/";
    3. public final static String URL_TAIL = "__xingzhengquhua/";
    4. public static List<PositionEntity> reqPosition(String url) throws InterruptedException {
    5. String htmlStr = HttpUtils.getRequest(url);
    6. //解析字符串为Document对象
    7. Document doc = Jsoup.parse(htmlStr);
    8. //获取body元素,获取class="fc"的table元素
    9. Elements table = doc.body().getElementsByAttributeValue("bgcolor", "#C5D5C5");
    10. //获取tbody元素
    11. Elements children;
    12. children = table.first().children();
    13. //获取tr元素集合
    14. Elements tr = children.get(0).getElementsByTag("tr");
    15. List<PositionEntity> result = new ArrayList<>();
    16. //遍历tr元素,获取td元素,并打印
    17. for (int i = 3; i < tr.size(); i++) {
    18. Element e1 = tr.get(i);
    19. Elements td = e1.getElementsByTag("td");
    20. if (td.size() < 2) {
    21. break;
    22. }
    23. String name = td.get(0).getElementsByTag("td").first().getElementsByTag("a").text();
    24. String code = td.get(1).getElementsByTag("td").first().getElementsByTag("a").text();
    25. if (CheckUtils.isEmpty(name) || CheckUtils.isEmpty(code)) {
    26. continue;
    27. }
    28. result.add(new PositionEntity(name, Long.parseLong(code)));
    29. }
    30. //防止ip被封
    31. Thread.sleep(10000);
    32. return result;
    33. }
    34. }

            PinyinUtils.java:

    1. public class PinyinUtils {
    2. private final static HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
    3. static {
    4. format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
    5. format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
    6. format.setVCharType(HanyuPinyinVCharType.WITH_V);
    7. }
    8. /**
    9. * 字符串转拼音
    10. *
    11. * @param str
    12. * 中文字符串
    13. * @param fill
    14. * 分隔符
    15. * @return 返回中文的拼音串
    16. */
    17. public static String str2Pinyin(String str, String fill) {
    18. if (null == str) {
    19. return null;
    20. }
    21. try {
    22. StringBuilder sb = new StringBuilder();
    23. if (fill == null)
    24. fill = "";
    25. boolean isCn = true;
    26. for (int i = 0; i < str.length(); i++) {
    27. char c = str.charAt(i);
    28. if (i > 0 && isCn) {
    29. sb.append(fill);
    30. }
    31. if (c == ' ') {
    32. sb.append(fill);
    33. }
    34. // 1、判断c是不是中文
    35. if (c >= '\u4e00' && c <= '\u9fa5') {
    36. isCn = true;
    37. String[] piyins = PinyinHelper.toHanyuPinyinStringArray(c, format);
    38. if (null == piyins || 0 >= piyins.length) {
    39. continue;
    40. }
    41. sb.append(piyins[0]);
    42. } else {
    43. // 不是中文
    44. if (c >= 'A' && c <= 'Z') {
    45. sb.append((char)(c + 32));
    46. } else {
    47. sb.append(c);
    48. }
    49. isCn = false;
    50. }
    51. }
    52. return sb.toString();
    53. } catch (BadHanyuPinyinOutputFormatCombination e) {
    54. e.printStackTrace();
    55. }
    56. return null;
    57. }
    58. /**
    59. * 拼音首字母
    60. *
    61. * @param str
    62. * 中文字符串
    63. * @return 中文字符串的拼音首字母
    64. */
    65. public static String strFirst2Pinyin(String str) {
    66. if (null == str) {
    67. return null;
    68. }
    69. try {
    70. StringBuilder sb = new StringBuilder();
    71. for (int i = 0; i < str.length(); i++) {
    72. char c = str.charAt(i);
    73. // 1、判断c是不是中文
    74. if (c >= '\u4e00' && c <= '\u9fa5') {
    75. String[] piyins = PinyinHelper.toHanyuPinyinStringArray(c, format);
    76. if (null == piyins || 0 >= piyins.length) {
    77. continue;
    78. }
    79. sb.append(piyins[0].charAt(0));
    80. } else {
    81. // 不是中文
    82. if (c >= 'A' && c <= 'Z') {
    83. sb.append((char)(c + 32));
    84. } else {
    85. sb.append(c);
    86. }
    87. }
    88. }
    89. return sb.toString();
    90. } catch (BadHanyuPinyinOutputFormatCombination e) {
    91. e.printStackTrace();
    92. }
    93. return null;
    94. }
    95. }

  • 相关阅读:
    【接口自动化测试入门】接口测试基础(超详细~)
    Spring boot 如何使用视图解析器 thymeleaf 模板引擎来渲染页面 详解一
    协程Part1-boost.Coroutine.md
    WPS中XLS表格使用的技巧记录
    「微服务 | Nginx」upstream 模块负载均衡算法详解
    NFT 推荐|史蒂夫·青木 NFT 作品集
    20221104英语学习
    react 高价组件HOC实现组件复用
    免费word转换pdf的软件
    基于java+SpringBoot+HTML+Mysql纪念币抢购销售网站
  • 原文地址:https://blog.csdn.net/qq_37284798/article/details/125410786