java处理Html标签以及图片链接的逻辑代码

前言:

需要针对新闻里面的图片链接地址进行抓取替换处理,所以需要手写java工具类进行正则抓取网址链接

java工具类使用正则

具体详情代码:

 


import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.springframework.util.CollectionUtils;

import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
public class HtmlTagUtils {

    /** js标签 */
    private static final String REG_EX_SCRIPT = "<[\\s]*?script[^>]*?>[\\s\\S]*?<[\\s]*?/[\\s]*?script[\\s]*?>";
    /** css标签 */
    private static final String REG_EX_STYLE = "<[\\s]*?style[^>]*?>[\\s\\S]*?<[\\s]*?/[\\s]*?style[\\s]*?>";

    /** 特殊嵌套标签需要优先去掉 */
    private static final String REG_EX_HTML_SP_START = "<d class=[^>]+>";

    /** 特殊嵌套标签需要优先去掉 */
    private static final String REG_EX_HTML_SP_END = "</d>";

    /** html标签 */
    private static final String REG_EX_HTML = "<[^>]+>";
    /** {@code <blockquote>标签} */
    private static final String REG_EX_HTML_BLOCKQUOTE = "</?blockquote>";
    /** 其他html转义符 */
    private static final String REG_EX_SPECIAL = "&[a-zA-Z]{1,10};";
    /** 正文声明内容匹配 */
    private static final String REG_EX_STATEMENT = "(<font[^>]*?>|<span[^>]*?>|<p[^>]*?>)\\s*([(|(|【]?(免责声明|图片声明|责编|责任编辑|声明|来源链接|资料来源|文章来源|编辑|头图来源|图片来源|转载声明|风险提示|郑重声明)\\s?[::】]).*?(</font>|</span>|</p>)";
    /** 记者声明内容匹配 */
    private static final String REG_EX_REPORTER = "<p>\\s*(本报记者|本报见习记者|见习记者|每经记者).*?</p>";
    /** HTML图片标签正则匹配 */
    private static final Pattern HTML_IMG_PATTERN = Pattern.compile("<img.*?\"\\s*/?\\s*>", Pattern.CASE_INSENSITIVE);
    /** HTML图片标签路径正则匹配 */
    private static final Pattern HTML_IMG_SRC_PATTERN = Pattern.compile("\\s+src\\s*=\\s*\"?(.*?)(\"\\s*/?\\s*>|\\s+)", Pattern.CASE_INSENSITIVE);
    /** BASE64编码图片前缀 */
    private static final String HTML_IMG_BASE_64_PREFIX = "data:";

    private static final String HTML_FORMAT_STYLE = "\n<style>\n" +
            "    .paragraphFormat{\n" +
            "        text-indent: 2em;\n" +
            "        margin-top: 6px;\n" +
            "        margin-bottom: 6px;\n" +
            "    }\n" +
            "    img{\n" +
            "        display: block;\n" +
            "        margin: 0 auto;\n" +
            "    }\n" +
            "    table{\n" +
            "        border: 1px solid black;\n" +
            "        border-collapse: collapse;\n" +
            "    }\n" +
            "    table td{\n" +
            "        border: 1px solid black;\n" +
            "    }\n" +
            "</style>";

    /**
     * 删除html标签
     * @param srcStr 原资讯正文内容
     * @return 过滤后的资讯正文内容
     */
    public static String removeHtmlTag(String srcStr) {
        return StringUtils.isBlank(srcStr) ? StringUtils.EMPTY : unescape(srcStr)
                .replaceAll(REG_EX_SCRIPT, "").replaceAll(REG_EX_STYLE, "")
                .replaceAll(REG_EX_HTML, "").replaceAll(REG_EX_SPECIAL, "");
    }

    /**
     * 删除HTML标签并格式化
     * <li>去除特殊空格、全角空格、换行符、制表符、空格</li>
     * <li>去除多余空行</li>
     * <li>首行缩进,换行缩进</li>
     * <li>去除js标签、html标签</li>
     * @param srcStr 原始资讯正文内容(带HTML格式)
     * @return 删除HTML标签后的正文内容
     */
    public static String removeHtmlAndFormat(String srcStr) {
        return StringUtils.isBlank(srcStr) ? StringUtils.EMPTY : unescape(srcStr)
                // 去除特殊空格
                .replaceAll("\\u00A0", "")
                // 去除全角空格
                .replaceAll("\\u3000", "")
                // 去除换行符、制表符
                .replaceAll("[\\t\\n\\r]", "")
                // 删除首行空格
                .replaceFirst("\\s+", "")
                // 删除2个以上的空格
                .replaceAll("\\s{2,}", "")
                // 首行缩进
                .replaceFirst("", "        ")
                // 去掉图片标签,排除干扰
                .replaceAll("<(?i)img.*?\"\\s*/?\\s*>", "")
                // 去除多余空行
                .replaceAll("<(?i)p>\\s*</(?i)p>", "")
                // 换行缩进(p标签)
                .replaceAll("<(?i)p>", "<p>\r\n        ")
                // 换行缩进(p标签)
                .replaceAll("</(?i)p>", "<p>\r\n        ")
                // 换行缩进(br标签)
                .replaceAll("</?(?i)br\\s?/?>", "\r\n        ")
                // 换行缩进(tr标签)
                .replaceAll("</(?i)tr>", "\r\n        ")
                // 空格(td标签)
                .replaceAll("</(?i)td>", " ")
                // 换行缩进(section标签)
                .replaceAll("</(?i)section>", "\r\n        ")
                // 特殊嵌套标签需要优先去掉
                .replaceAll(REG_EX_HTML_SP_START, "").replaceAll(REG_EX_HTML_SP_END, "")
                // 去除js标签
                .replaceAll(REG_EX_SCRIPT, "").replaceAll(REG_EX_STYLE, "")
                // 去除html标签
                .replaceAll(REG_EX_HTML, "").replaceAll(REG_EX_SPECIAL, "")
                // 去除多余空行
                .replaceAll("\r\n\\s+\r\n", "\r\n");
    }

    /**
     * 删除正文声明和记者声明
     * @param srcStr 原资讯正文内容
     * @return 过滤后的资讯正文内容
     */
    public static String removeStatementAndReporter(String srcStr) {
        return StringUtils.isBlank(srcStr) ? StringUtils.EMPTY : unescape(srcStr)
                .replaceAll(REG_EX_HTML_BLOCKQUOTE, "")
                .replaceAll(REG_EX_STATEMENT, "")
                .replaceAll(REG_EX_REPORTER, "");
    }

    /**
     * HTML内容进行HTML标签转义
     * <li>HTML标签转义举例:{@code &lt;p&gt; -> <p>}</li>
     * @param srcStr 原始内容
     * @return 转码后的HTML内容
     */
    public static String unescape(String srcStr) {
        if (StringUtils.isBlank(srcStr)) {
            return StringUtils.EMPTY;
        }
        return StringEscapeUtils.unescapeHtml4(srcStr);
    }

    /**
     * 判断资讯正文是否包含图片
     * @param content 资讯正文
     * @return true-包含,false-不包含
     */
    public static boolean hasImg(String content) {
        if (StringUtils.isBlank(content)) {
            return false;
        }
        return extractImgPath(content).size() > 0;
    }

    /**
     * 抽取正文中的图片标签
     * @param content 资讯正文
     * @return 图片标签列表
     */
    public static List<String> extractImg(String content) {
        if (StringUtils.isBlank(content)) {
            return Collections.emptyList();
        }
        Matcher matcher = HTML_IMG_PATTERN.matcher(content);
        List<String> result = new ArrayList<>();
        while (matcher.find()) {
            result.add(matcher.group());
        }
        return result;
    }

    /**
     * 抽取正文中的图片路径
     * @param content 资讯正文
     * @return 图片路径列表
     */
    public static List<String> extractImgPath(String content) {
        List<String> imgTags = extractImg(content);
        if (CollectionUtils.isEmpty(imgTags)) {
            return Collections.emptyList();
        }
        List<String> result = new ArrayList<>();
        for (String img : imgTags) {
            Matcher matcher = HTML_IMG_SRC_PATTERN.matcher(img);
            if (matcher.find()) {
                String url = extractUrl(matcher.group());
                if (StringUtils.isNotBlank(url)
                        && !url.startsWith(HTML_IMG_BASE_64_PREFIX)) {
                    result.add(url);
                }
            }
        }
        return result.stream().distinct().collect(Collectors.toList());
    }

    /**
     * 抽取图片链接
     * @param src 图片src路径
     * @return 图片链接
     */
    public static String extractUrl(String src) {
        try {
            return unescape(URLDecoder.decode(src.substring(src.indexOf("\"") + 1, src.lastIndexOf("\"")), "UTF8"));
        } catch (Exception e) {
            log.error("图片URL解码失败,原图片链接:{}", src);
            log.error(e.getMessage(), e);
            return StringUtils.EMPTY;
        }
    }

    /**
     * 替换正文OSS配图
     * @param content 替换前正文内容
     * @param imgList 正文OSS图片列表
     * @return 替换后正文内容
     */
    public static String replaceImgPath(String content, List<UnifyImg> imgList) {
        if (hasImg(content) && !CollectionUtils.isEmpty(imgList)) {
            // 正文包含图片
            Map<String, String> imgMap = imgList.stream().collect(Collectors.toMap(UnifyImg::getSrcImgUrl, UnifyImg::getFilePath));
            Matcher matcher = HTML_IMG_PATTERN.matcher(content);
            StringBuffer buffer = new StringBuffer();
            while (matcher.find()) {
                // imgTag为完整img标签:<img src="https://n.sinaimg.cn/tech/transform/703/w630h73/20200804/3492-ixeeisa0456891.png">
                String imgTag = matcher.group();
                Matcher imgMatcher = HTML_IMG_SRC_PATTERN.matcher(imgTag);
                imgTag = imgTag.replaceFirst(" (?i)src", " data-src");
                if (imgMatcher.find()) {
                    String srcUrl = imgMatcher.group();
                    // imgUrl为图片链接:https://n.sinaimg.cn/tech/transform/703/w630h73/20200804/3492-ixeeisa0456891.png
                    String imgUrl = srcUrl.substring(srcUrl.indexOf("\"") + 1, srcUrl.lastIndexOf("\""));
                    if (imgMap.containsKey(extractUrl(srcUrl))) {
                        String imgRep = imgTag.replace(imgUrl, "/" + imgMap.getOrDefault(extractUrl(srcUrl), StringUtils.EMPTY));
                        matcher.appendReplacement(buffer, imgRep);
                    }
                } else {
                    matcher.appendReplacement(buffer, imgTag);
                }
            }
            matcher.appendTail(buffer);
            return buffer.toString();
        } else {
            return content;
        }
    }

    /**
     * 格式化资讯html格式正文
     * @param content 资讯html格式正文
     * @return 格式化后的资讯正文
     */
    public static String formatHtml(String content) {
        return content + HTML_FORMAT_STYLE;
    }

}

总结:使用正则匹配效果很好但是效率真的很低,因为全文匹配导致效率很慢,如果1万字的新闻内容,结果下来可能需要好几分钟!

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苦思冥想行则将至

穷,有钱的大爷上个两分钱吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值