解析html生成Word文档

内容:读取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.mfbz.cn/a/133267.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Dart笔记:一些代码生成工具站点的介绍

Dart笔记&#xff1a; 一些代码生成工具站点的介绍 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/1343…

力扣138:随机链表的复制

力扣138&#xff1a;随机链表的复制 题目描述&#xff1a; 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff…

基于SSM的培训机构运营系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

【3】Gradle-快速入门使用【Gradle概念】

目录 【3】Gradle-快速入门使用【Gradle概念】Gradle任务查看可用任务了解任务探索任务依赖性 依赖关系了解传递依赖关系查看项目依赖项添加版本目录 【可选】 插件使用插件查看插件提供的任务配置插件 增量构建启用缓存使用构建缓存步骤总结 个人主页: 【⭐️个人主页】 需要您…

JVM虚拟机:垃圾回收器之CMS(老年代)

本文重点 在前面的课程中我们学习了Serial和PO垃圾回收器,本文将学习一种新的在老年代使用的垃圾回收器CMS。 特点 CMS收集器是一种以获取最短回收停顿时间为目标的收集器(还是会有短暂的STW),适合互联网或者B/S系统的服务器上,这类应用尤其重视服务器的响应速度,希望…

反转链表 --- 递归回溯算法练习三

目录 1. 分析题意 2. 分析算法原理 2.1. 递归思路&#xff1a; 1. 挖掘子问题&#xff1a; 3. 编写代码 3.1. step 1&#xff1a; 3.2. step 2&#xff1a; 3.3. step 3&#xff1a; 3.4. 递归代码&#xff1a; 1. 分析题意 力扣原题链接如下&#xff1a; 206. 反转链…

巧用ADB安卓调试工具,在双十一直播间轻松回复文字领取优惠!

微信改版了&#xff0c;现在看到我们全凭缘分&#xff0c;为了不错过【全栈工程师修炼指南】重要内容及福利&#xff0c;大家记得按照上方步骤设置「接收文章推送」哦~ 关注回复【学习交流群】加入【SecDevOps】学习交流群! 文章目录&#xff1a; 1.前言简述 描述: 通过前面几篇…

【AICFD案例教程】进气歧管分析

AICFD是由天洑软件自主研发的通用智能热流体仿真软件&#xff0c;用于高效解决能源动力、船舶海洋、电子设备和车辆运载等领域复杂的流动和传热问题。软件涵盖了从建模、仿真到结果处理完整仿真分析流程&#xff0c;帮助工业企业建立设计、仿真和优化相结合的一体化流程&#x…

【那些反爬和反反爬】xpath根据兄弟节点定位元素

其实这篇不涉及什么高大上的反爬&#xff0c;但是实在不知道要把这篇文章归类到哪里&#xff0c;就直接先扔这里吧。 先吐槽一句&#xff1a;萌娘百科绝对是我再也不想爬第二次的网站。 第二句&#xff1a;&#xff08;真理&#xff09;把网站弄得越乱越让人摸不着头脑&#…

【2024提前批/秋招笔试汇总2】——大疆-嵌入式软件-2023.08.06

一、 单选题&#xff08;40分&#xff09; 1. 以下关于GPU的特点描述不准确的是&#xff1a; A.GPU无法使用共享内存结构&#xff0c;提高通信速度 B.GPU的并行数据处理可以大幅度提高运算能力 C.GPU使用高速全局内存可以进一步提升运算速度 D.GPU的计算能力比CPU强 2.下列关…

技术架构-单机架构

前言 从今天开始系统学习 Docker 课程&#xff0c;总结下 Docker 是什么&#xff0c;用来做什么&#xff0c;架构是怎样的。 注&#xff1a;&#xff08;1&#xff09;当浏览器 / APP访问服务器时&#xff0c;如果服务器适用的时 http 协议&#xff0c;那么默认端口时80&#…

Learn runqlat in 5 minutes

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

CentOs7 NAT模式连接网络

1.配置动态网络 1.1 检查主机网卡配置 检查主机的网络设置 进入控制面板&#xff0c;找到网络共享中心 查看适配器是否都已经开启 1.2 设置虚拟机的网络配置 打开虚拟机网络配置设置&#xff0c;对网卡VMnet8 进行设置 记住网关 全部选择应用&#xff0c;确定 1.3 设置…

数据结构:树的基本概念(二叉树,定义性质,存储结构)

目录 1.树1.基本概念1.空树2.非空树 2.基本术语1.结点之间的关系描述2.结点、树的属性描述3.有序树、无序树4.森林 3.树的常考性质 2.二叉树1.基本概念2.特殊二叉树1.满二叉树2.完全二叉树3.二叉排序树4.平衡二叉树 3.常考性质4.二叉树的存储结构1.顺序存储2.链式存储 1.树 1.…

PyTorch技术和深度学习——三、深度学习快速入门

文章目录 1.线性回归1&#xff09;介绍2&#xff09;加载自由泳冠军数据集3&#xff09;从0开始实现线性回归模型4&#xff09;使用自动求导训练线性回归模型5&#xff09;使用优化器训练线性回归模型 2.使用torch.nn模块构建线性回归模型1&#xff09;使用torch.nn.Linear训练…

文件改名:避免繁琐操作,利用筛选文件批量重命名技巧优化文件管理

在我们的日常生活和工作中&#xff0c;我们经常需要处理大量的文件&#xff0c;从文档、图片到音频和视频等。在这些情况下&#xff0c;一个高效的文件管理策略至关重要。文件重命名的必要性主要体现在两个方面。首先&#xff0c;对于大量文件&#xff0c;手动进行重命名不仅费…

邻接矩阵储存图实现深度优先遍历(C++)

目录 基本要求&#xff1a; 图的结构体&#xff1a; 图的构造&#xff1a; 图的深度优先&#xff08;DFS&#xff09;&#xff1a; 图的打印输出&#xff1a; 完整代码&#xff1a; 测试数据&#xff1a; 运行结果&#xff1a; 通过给出的图的顶点和边的信息&#xff0c…

Sprint Boot 学习路线 4

微服务 Spring Microservices是一个框架&#xff0c;它使用Spring框架更容易地构建和管理基于微服务的应用程序。微服务是一种架构风格&#xff0c;其中一个大型应用程序被构建为一组小型、独立可部署的服务。每个服务具有明确定义的职责&#xff0c;并通过API与其他服务通信。…

S7-1200PLC和SMART PLC开放式以太网通信(UDP双向通信)

S7-1200PLC的以太网通信UDP通信相关介绍还可以参考下面文章链接: 博途PLC开放式以太网通信TRCV_C指令应用编程(运动传感器UDP通信)-CSDN博客文章浏览阅读2.8k次。博途PLC开放式以太网通信TSENG_C指令应用,请参看下面的文章链接:博途PLC 1200/1500PLC开放式以太网通信TSEND_…

Flink之Table API SQL连接器

连接器 Table API & SQL连接器1.概述2.支持连接器 DataGen连接器1.概述2.SQL客户端执行3.Table API执行 FileSystem连接器1.创建FileSystem映射表2.创建source数据源表3.写入数据4.解决异常5.查询fileTable6.查看HDFS Kafka连接器1.添加kafka连接器依赖2.重启yarn-session、…
最新文章