解析html生成Word文档

news2025/1/15 13:12:17

内容:读取html文件中的文本内容,然后生成Word文档导出。

事例场景:需求开发完成之后需要写文档(代码修改清单),文档内容就是这次需求修改/新增的所有代码,需要列出修改的文件路径以及代码片段,并且用不同的颜色标注区分。
例图:

如果手动复制粘贴并且标注,会相当麻烦。所以这里记录一下,使用代码来简单处理,生成所需文档。
 

第一步:功能测试完成后,把代码合并到新的分支,然后进入页面就能查看到这次的合并记录。


第二步:保存这个页面到本地,然后执行程序,生成Word文档。
 

功能实现说明

项目:SpringBoot
html文件来源:gitee。如果是gitLab的页面,可能页面标签元素就不太一样,需要修改一下代码中解析html读取标签的方式。

代码实现逻辑:使用jsoup读取并解析html文件,然后使用poi生成Word文档。

解析html需要先搞清楚html中的标签元素,然后再用代码读取。

开始编码
Maven依赖:

		<!-- poi 读取,生成Word文档、Excel文档-->
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml</artifactId>
			<version>3.15</version>
		</dependency>
		<!-- 解析html -->
		<dependency>
			<groupId>org.jsoup</groupId>
			<artifactId>jsoup</artifactId>
			<version>1.8.3</version>
		</dependency>

Java代码:
 

package com.example.demo16.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTShd;

import java.io.File;
import java.io.FileOutputStream;

@Slf4j
public class CodeChangeDetailUtil {

    public static void main(String[] args) throws Exception {
        String htmlPath="E:/Downloads/code_01.html";
        String saveWordPath="E:/Downloads/code_0112.docx";
        codeChangeDetailOutPutWorld(htmlPath, saveWordPath);
    }

    /**
     * 旧代码标识:old
     * 新代码标识:new
     */
    private static final String CODE_TYPE_OLD = "old";
    private static final String CODE_TYPE_NEW = "new";

    /**
     * 解析HTML文件内容生成Word
     * @param htmlPath      HTML文件路径
     * @param saveWordPath  Word文档保存路径
     * @throws Exception
     */
    public static void codeChangeDetailOutPutWorld(String htmlPath, String saveWordPath) throws Exception {
        XWPFDocument doc = new XWPFDocument();
        Document document = Jsoup.parse(new File(htmlPath), "utf8");
        Elements elements = document.getElementsByClass("files");
        // 文件元素集合
        Elements diffFileElements = elements.get(0).getElementsByClass("diff-file");
        createHeader(doc, "修改清单(数量:" + diffFileElements.size() + ")");
        for (Element element : diffFileElements) {
            String headerText = element.getElementsByClass("header").get(0)
                    .getElementsByTag("a")
                    .get(0).text();
            createNullLine(doc, 1);
            createText(doc, headerText, "");
        }
        createNullLine(doc, 2);
        createHeader(doc, "程序修改记录");
        for (Element element : diffFileElements) {
            // 文件头,文件路径
            String headerText = element.getElementsByClass("header").get(0)
                    .getElementsByTag("a")
                    .get(0).text();
            //log.info("headerText:{}",headerText);
            // 得到文件路径/名称
            createNullLine(doc, 2);
            createTextHeader(doc, headerText);
            // 文件内容元素,代码存放在表格的行中,一行代码一行,这里获取表格的所有行
            if (null == findTableElements(element)) {
                log.error("该文件内容可能被折叠,请在原页面搜索【差异被折叠,点击展开】点击展开后再保存html文件,文件:{}", headerText);
                throw new Exception("该文件内容可能被折叠,请在原页面搜索【差异被折叠,点击展开】点击展开后再保存html文件");
            }
            Elements trElements = findTableElements(element).get(0)
                    .getElementsByTag("tbody").get(0)
                    .getElementsByTag("tr");
            // 遍历所有的行,得到文件内容
            for (Element tr : trElements) {
                String lineContent = tr.getElementsByClass("line_content").get(0).text();
                // 旧代码
                if(tr.getElementsByClass("old").size()>0){
                    createText(doc, lineContent, CODE_TYPE_OLD);
                    continue;
                }
                // 新代码
                if(tr.getElementsByClass("new").size()>0){
                    createText(doc, lineContent, CODE_TYPE_NEW);
                    continue;
                }
                createText(doc, lineContent, "");
            }
        }
        FileOutputStream fileOutputStream = new FileOutputStream(saveWordPath);
        doc.write(fileOutputStream);
        fileOutputStream.close();
        log.info("文档生成成功,存储路径:{}", saveWordPath);
    }

    /**
     * 创建标题
     * @param doc
     * @param headerText    标题文本
     */
    private static void createHeader(XWPFDocument doc, String headerText) {
        // 创建标题
        XWPFParagraph paragraph = doc.createParagraph();
        //标题等级,1,2,3...
        paragraph.setStyle("1");
        //设置对齐
        paragraph.setAlignment(ParagraphAlignment.LEFT);
        XWPFRun run = paragraph.createRun();
        run.setColor("000000");
        run.setText(headerText);
        run.setFontFamily("黑体");
        run.setFontSize(22);
        // 加粗
        run.setBold(true);
    }

    /**
     * 生成文件路径
     * @param doc
     * @param contentText   文本内容
     */
    private static void createTextHeader(XWPFDocument doc, String contentText) {
        XWPFParagraph paragraph = doc.createParagraph();
        // 左对齐
        paragraph.setAlignment(ParagraphAlignment.LEFT);
        XWPFRun contentRun = paragraph.createRun();
        contentRun.setFontSize(11);
        // 前面创建空行
        createNullLine(doc, 2);
        contentRun.setText(contentText);
        // 加粗
        contentRun.setBold(true);
    }

    /**
     * 文件内容
     * @param doc
     * @param contentText   文本内容
     * @param textType  代码内容标识
     */
    private static void createText(XWPFDocument doc, String contentText, String textType) {
        XWPFParagraph paragraph = doc.createParagraph();
        // 左对齐
        paragraph.setAlignment(ParagraphAlignment.LEFT);
        XWPFRun contentRun = paragraph.createRun();
        contentRun.setFontFamily("Consolas");
        contentRun.setFontSize(9);
        contentRun.setText(contentText);
        // 突出显示
        CTShd ctShd = contentRun.getCTR().addNewRPr().addNewShd();
        // old:旧代码,new:新代码
        if (CODE_TYPE_OLD.equals(textType)) {
            ctShd.setFill("FFEFD5");
        } else if (CODE_TYPE_NEW.equals(textType)) {
            ctShd.setFill("CCFF99");
        }
    }

    /**
     * 创建空行
     * @param doc
     * @param lineNum   行数
     */
    private static void createNullLine(XWPFDocument doc, int lineNum) {
        XWPFParagraph paragraph = doc.createParagraph();
        XWPFRun contentRun = paragraph.createRun();
        for (int i = 0; i <lineNum; i++) {
            contentRun.setText("\n");
        }
    }

    /**
     * 查找表格元素
     * @param element   元素对象
     * @return
     */
    private static Elements findTableElements(Element element) {
        Elements trElements_temp = element.getElementsByClass("diff-content");
        for (Element element_t : trElements_temp) {
            int tableSize = element_t.getElementsByTag("table").size();
            if (tableSize > 0) {
                return element_t.getElementsByTag("table");
            }
        }
        return null;
    }
}

具体页面具体分析。主要就是利用jsoup读取html内容并处理,jsoup的使用细节可以参考官方文档。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1197858.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaWeb Day09 Mybatis-基础操作01-增删改查

目录 环境准备 ①Emp.sql ②Emp.java 一、删除 ①Mapper层 ②测试类 ③预编译SQL&#xff08;查看mybatis日志&#xff09; 1.性能 2.安全 ④总结 二、新增 ①Mapper层 ②测试类 ③结果 ④新增&#xff08;主键返回&#xff09; 1.Mapper层 2.测试类 ⑤总结​…

leetCode 493 翻转对 归并分治 + 图解

493. 翻转对 - 力扣&#xff08;LeetCode&#xff09; 给定一个数组 nums &#xff0c;如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对。你需要返回给定数组中的重要翻转对的数量。 求"小和"问题是&#xff0c;当我 j 来到一个位置的时…

【研究】Splunk 字段是否被加工过

1: 背景: 最近用户有个疑问,就是有些字段的输出有点问题,不确定是否被加工过。 2: 查找问题: index=abc sourcetype=def123 发现字段: city_shanghai 的输出可能有点问题。 3: 排查问题: 先去这个splunk search head cluster 的页面: server 的查找如下: 登入so1 s…

“Git实践指南:深入探索开发测试上线、分支管理与标签“

文章目录 引言一、Git的分支的使用1.分支2.标签3.分支与标签的关系4. 分支在实际中的作用5. 四个环境以及各自的功能特点6. 分支策略分支应用场景 二、Git的标签3.1 标签的基本使用3.3 标签的共享与推送 总结 引言 在现代软件开发中&#xff0c;版本控制是一个关键的环节&…

kubernetes prometheus监控

目录 一、部署prometheus 二、 部署nginx监控实例 三、部署prometheus-adapter 一、部署prometheus 清理镜像方便后面一次性上传 docker rmi docker images | grep -v REPOSITORY | awk {print $1":"$2} 删除 docker load -i kube-prometheus-stack-0.58.0.tar…

计蒜客详解合集(3)期

目录 T1236——分苹果 T1113——整理药名 T1153——整数奇偶排列 T1249——漂亮的字符串 T1168——统计素数个数 T1160——甲流病人筛选 T1236——分苹果 分享一道特别简单的题。 蒜头君要把一堆苹果分给 个小朋友&#xff0c;要使每个人都能拿到苹果&#xff0c;而目每…

Python学习笔记--自定义类型的枚举

三、自定义类型的枚举 但有些时候我们需要控制枚举的类型&#xff0c;那么我们可以 Enum 派生出自定义类来满足这种需要。通过修改上面的例子&#xff1a; #!/usr/bin/env python3 # -*- coding: UTF-8 -*- from enum import Enum, uniqueEnum(Month, (Jan, Feb, Mar, Apr, M…

相机内外参实践之点云投影矢量图

目录 概述 涉及到的坐标变换 深度值可视化 3D点云的2D投影实现 实现效果 参考文献 概述 Camer的内外参在多模态融合中主要涉及到坐标系变换&#xff0c;即像素坐标、相机坐标以及其他坐标系。这篇就针对点云到图像的投影与反投影做代码实践&#xff0c;来构建一张具有深度…

【2023CANN训练营第二季】——Ascend C算子开发进阶—Ascend C Tiling计算

了解Tiling基本概念 在这一小节中接触到了一个新的概念&#xff0c;叫Tiling计算&#xff0c;指的是在Ascend C 算子开发过程中&#xff0c;矢量的算子流程分为3个基本任务&#xff1a;CopyIn&#xff0c;Compute&#xff0c;CopyOut。CopyIn任务负责将Global Memory上的输入T…

Python 列表元素里面含有字典或者列表进行排序

大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 如果有什么疑惑/资料需要的可以点击文章末尾名片领取源码 示例1&#xff1a;列表里面含有列表进行排序 s [[1, 2], [100, 2], [33, 3], [25, 6]] s.sort(keylambda k: k[0]) print(s)结果&#xff1a; [[1, 2], [25, 6], [33, 3…

基于FPGA的PS端的Si5340的控制

1、功能 Si5340/41-D可以输出任意频率&#xff0c;当然有范围&#xff0c;100Hz1GHz。外部输入为24M或者4854M的XTAL&#xff0c;VCO在13500~14256Mhz之间&#xff0c;控制接口采用IIC或者SPI。 芯片架构图 2、IIC控制方式 3、直接上控制代码 使用米联客ZU3EG&#xff0c;将…

Learn runqlat in 5 minutes

内容预告 learn X in 5 系列第一篇. 本篇主要介绍进程时延统计方式和 rawtracepoint. runqlat "高负载场景下应用为何卡顿", "进程 A 为什么得不到调度". 当我们在工作生活中产生这样的疑问, 目标进程的调度时延是一个不错的观测切入点. runqlat 可以帮…

SQL必知会(二)-SQL查询篇(5)-用通配符进行过滤

第6课、用通配符进行过滤 LIKE&#xff1a;匹配文本 LIKE&#xff1a;针对未知值进行过滤。通配符搜索只能用于文本字段。 1&#xff09;百分号%通配符 %表示任何字符出现任意次数。 需求&#xff1a;找出所有以词 Fish 起头的产品 SELECT prod_id, prod_name FROM Product…

Linux-基础知识

1.快捷键 ctrlc 强制停止 ctrld 退出或登出 history 查看历史命令&#xff08;&#xff01;/ctrlr输入内容去匹配历史命令&#xff09; 光标移动快捷键 ctrla,跳到命令开头 ctrle,跳到命令结尾 ctrl键盘左键&#xff0c;向左跳一个单词 ctrl键盘右键&…

Python 使用tkinter的Menu菜单command参数与bind方法共用触发事件

用普通函数作为媒介&#xff0c;使用event_generate()方法模拟触发bind()事件来创建一个模拟的event对象&#xff0c;并将其传递给绑定的事件处理函数。 运行结果 示例代码 import tkinter as tk# 菜单事件 def menuEvent(event):print(event.x, event.y)label.config(textf鼠…

【Linux】Centos7 shell实现MySQL5.7 tar 一键安装

&#x1f984; 个人主页——&#x1f390;个人主页 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341;&#x1fa81;&#x1f341;&#x1fa81;&#x1f341;&#x1fa81;&#x1f341; 感谢点赞和关注 &#xff0c;每天进步一点点&#xff01;加油&#xff01;&…

SpringBoot集成easyexcel实现动态模板导出

添加依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-o…

补坑:Java的字符串String类(3):再谈String

不太熟悉字符串的可以看看这两篇文章 补坑&#xff1a;Java的字符串String类&#xff08;1&#xff09;-CSDN博客 补坑&#xff1a;Java的字符串String类&#xff08;2&#xff09;&#xff1a;一些OJ题目-CSDN博客 字符串创建对象 public static void main(String[] args) …

【pytorch深度学习】使用张量表征真实数据

使用张量表征真实数据 本文为书pytorch深度学习实战的一些学习笔记和扩展知识&#xff0c;涉及到的csv文件等在这里不会给出&#xff0c;但是我会尽量脱离这一些文件将书本想要表达的内容给展示出来。 文章目录 使用张量表征真实数据1. 加载图像文件2. 改变布局3. 加载目录下…

Nacos入门到运行-超详细~windwos

&#x1f4da;目录 ⚙️简介:⚡️Nacos下载⌛解压到文件⚙️配置信息☘️修改 application.properties ⛵运行程序☘️安全问题☄️程序出现问题查看方式 ⛳Nacos开启鉴权⚡️跳过Token获取数据⚓接口请求&#xff1a; ✍️结束&#xff1a; ⚙️简介: Nacos:正如官网说的,一个…