前言:
需要针对新闻里面的图片链接地址进行抓取替换处理,所以需要手写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 <p> -> <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万字的新闻内容,结果下来可能需要好几分钟!