解析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.mzph.cn/news/138514.shtml

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

相关文章

Python实战:绘制直方图的示例代码,数据可视化获取样本分布特征

文章目录 一、初步二、参数三、绘图类型四、多组数据直方图对比Python技术资源分享1、Python所有方向的学习路线2、学习软件3、精品书籍4、入门学习视频5、实战案例6、清华编程大佬出品《漫画看学Python》7、Python副业兼职与全职路线 一、初步 对于大量样本来说&#xff0c;如…

systemctl enable docker.service报错“Failed to execute operation: Bad message“

将docker加入到开机自启&#xff0c;报错&#xff1a; 解决&#xff1a; 重新粘贴复制&#xff1a; [Unit] DescriptionDocker Application Container Engine Documentationhttps://docs.docker.com Afternetwork-online.target firewalld.service Wantsnetwork-online.target…

VS c++多文件编译

前言&#xff1a;记录下我这个菜鸡学习的过程&#xff0c;如有错误恳请指出&#xff0c;不胜感激&#xff01; 1.简单多文件编译调试 文件目录&#xff1a; 编译&#xff1a; -g选项是告诉编译器生成调试信息&#xff0c;这样可以在程序崩溃或出现错误时更容易地进行调试。这…

Windows下安装Anaconda5.3.1+Python3.8+TensorFlow2.13.0-CPU版本总结

Python3.8安装可以参考博文https://janus.blog.csdn.net/article/details/55274849 进行安装即可。 【1】Anaconda 清华的开源软件镜像站&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/下载&#xff0c;这里选择的是5.3.1版本。 然后正常安装就可以&am…

华为ensp:交换机接口划分vlan

现在要把 e0/0/1 接口放入vlan1 e0/0/2 接口放入vlan2 e0/0/3 接口放入vlan3 默认所有接口都在vlan1所以 e0/0/0 接口不用动 1.创建vlan 进入系统视图模式 直接输入 vlan 编号 即可创建对应vlan vlan 编号 vlan 2 创建vlan2 vlan 3 创建vlan3 2.将接口进入vlan…

HALSTM32通用定时器+EXTI实现单击/双击/长按功能

HALSTM32通用定时器EXTI实现单击/双击/长按功能 ✨在使用USB功率计的时候&#xff0c;发现上面的一个按键实现多画面功能切换&#xff0c;于是探索了一下是如何实现的&#xff0c;将其实现的基本思路以及综合网上收集的相关实现方法&#xff0c;粗陋的整理了一下&#xff0c;将…

用趋动云GPU部署自己的Stable Diffusion

注&#xff1a;本文内容来自于对DataWhale的开源学习项目——免费GPU线上跑AI项目实践的学习&#xff0c;参见&#xff1a;Docs&#xff0c;引用了多处DataWhale给出的教程。 1.创建项目 1&#xff09;进入趋动云用户工作台&#xff0c;在当前空间处选择注册时系统自动生成的…

开发知识点-NodeJs-npm/Pnpm/Vite/Yarn包管理器

包管理器 vue-cli-service 不是内部或外部命令&#xff0c;也不是可运行的程序npm 全局变量pnpmPnpm介绍ViteYarn ‘vue-cli-service’ 不是内部或外部命令&#xff0c;也不是可运行的程序 yarn yarn add vue-amap yarn add vue-amap ant-design-vue npm 全局变量 换主机 新…

tomcat下载与使用教程

1. tomcat下载 官网&#xff1a;https://tomcat.apache.org/ 镜像地址&#xff1a;https://mirrors.huaweicloud.com/apache/tomcat/ 1、选择一个版本下载&#xff0c;官网下载速度缓慢&#xff0c;推荐镜像 2、对压缩包进行解压&#xff0c;无需进行安装&#xff0c;解压放…

Verilog 之 initial 模块与always 模块的用法与差异

文章目录 initial语法和用法特点和注意事项用途 always语法和用法特点和注意事项用途 二者差异 initial 在 Verilog 中&#xff0c;initial 块是用来在模拟开始时执行一次性初始化操作的一种建模方式。它通常用于模拟初始条件或进行一次性的初始化设置&#xff0c;而且只会在模…

经典OJ题:奇偶链表

目录 题目&#xff1a; 示例&#xff1a; 解题思路&#xff1a; 方法一&#xff1a;双链表链接法 图例&#xff1a; 代码演示&#xff1a; 解题效果&#xff1a; 方法二&#xff1a;奇偶指针 图例&#xff1a; 代码演示&#xff1a; 题目&#xff1a; 给定单链表…

栈 和 队列

什么是栈? 一种特殊的线性表&#xff0c;只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出&#xff08;LIFO - Last In First Out&#xff09;的原则。   从数据结构的角度来看&…

使用 pubsub-js 进行消息发布订阅

npm 包地址 github 包地址 pubsub-js 是一个轻量级的 JavaScript 基于主题的消息订阅发布库 &#xff0c;压缩后小于1b。它具有使用简单、性能高效、支持多平台等优点&#xff0c;可以很好地满足各种需求。 功能特点&#xff1a; 无依赖同步解耦ES3 兼容。pubsub-js 能够在…

基于Quartz实现动态定时任务

生命无罪&#xff0c;健康万岁&#xff0c;我是laity。 我曾七次鄙视自己的灵魂&#xff1a; 第一次&#xff0c;当它本可进取时&#xff0c;却故作谦卑&#xff1b; 第二次&#xff0c;当它在空虚时&#xff0c;用爱欲来填充&#xff1b; 第三次&#xff0c;在困难和容易之…

Flink SQL -- 概述

1、Flink SQL中的动态表和连续查询 1、动态表&#xff1a; 因为Flink是可以做实时的&#xff0c;数据是在不断的变化的&#xff0c;所以动态表指的是Flink中一张实时变换的表&#xff0c;表中会不断的有新的数据。但是这张表并不是真正的物理表。 2、连续查询&#xff1a; 连续…

王道考研--》顺序表课后习题C语言代码实现(冲刺)

考研是许多计算机科学专业学生追求高学历、寻求更好就业前景的途径。在考研过程中&#xff0c;数据结构是一个非常重要的科目&#xff0c;而代码实现题更是其中的难点之一。在这篇文章中&#xff0c;我们将探讨如何通过实现数据结构代码问题来提升考研成绩。无论您是否有编程经…

C++ 常用方法,刷oj必备(持续更新!!!)

输出结果保留小数点后n位(4位) #include<iostream> #include <iomanip> using namespace std;int main(){double s ;cin >> s ;cout<<fixed << setprecision(4) << s ;return 0; } 类型转换 string 转 int #include <iostream> …

ClickHouse主键索引最佳实践

在本文中&#xff0c;我们将深入研究ClickHouse索引。我们将对此进行详细说明和讨论&#xff1a; ClickHouse的索引与传统的关系数据库有何不同ClickHouse是怎样构建和使用主键稀疏索引的ClickHouse索引的最佳实践 您可以选择在自己的机器上执行本文给出的所有Clickhouse SQL…

RabbitMQ集群

RabbitMQ概述 1.RabbiMQ简介 RabbiMQ是⽤Erang开发的&#xff0c;集群⾮常⽅便&#xff0c;因为Erlang天⽣就是⼀⻔分布式语⾔&#xff0c;但其本身并不⽀持负载均衡。支持高并发&#xff0c;支持可扩展。支持AJAX&#xff0c;持久化&#xff0c;用于在分布式系统中存储转发消…

【CocoaPods安装环境和流程以及各种情况】

CocoaPods 环境HomebrewRubyrbenvRubyGems 和 Bundler安装Ruby管理Ruby更新Ruby替换Ruby镜像方式1方式2 CocoaPods安装CocoaPodsCocoaPods使用安装的一些问题单元测试引用问题 参考的链接 环境 Homebrew $ brew --config *可以发现打印有下面一行&#xff1a; Homebrew Ruby: …