<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
</head>
<body>
这是不带标签的文本
<h1>Hello World</h1>
<div>
<h1>这是大标题</h1>
<p>这是一段段落</p>
<p>这是一段段落</p>
<div>
<h1>这是小标题</h1>
<p>这是一段段落</p>
<p>这是一段段落</p>
</div>
</div>
<div>
<h1>这是大标题</h1>
<p>这是一段段落</p>
<p>这是一段段落</p>
</div>
</body>
</html>
此为简单使用方式测试结果!更复杂的使用方式,见最下方的测试参考!
html
├── head
└── body
├── span
├── h1
├── div
│ ├── h1
│ ├── p
│ ├── p
│ └── div
│ ├── h1
│ ├── p
│ └── p
└── div
├── h1
├── p
└── p
span
标签html 中没有任何标签的纯本本我们统一给它们加上了 span
标签!
暂不详,如发现问题,会及时修改此文档!
<!--jsoup-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.2</version>
</dependency>
package com.zibo.zibo2022.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;
public class StringUtility {
private StringUtility() {
}
/**
* 连接字符串
* @param strings 字符串列表
* @return 连接后的字符串
*/
public static String concat(String... strings) {
StringBuilder builder = new StringBuilder(strings.length << 3);
for (String s : strings) {
builder.append(s);
}
return builder.toString();
}
/**
* 连接字符串
* @param objects 字符串对象列表
* @return 连接后的字符串
*/
public static String concat(Object... objects) {
StringBuilder builder = new StringBuilder(objects.length << 3);
for (Object o : objects) {
builder.append(o);
}
return builder.toString();
}
/**
* 如果为 null 或者空字符串或空白返回 false,否则返回true
* @param string 字符串
* @return 判断结果
*/
public static boolean isNotNullOrEmptyOrBlank(String string) {
return string != null && !string.isEmpty() && !string.trim().isEmpty();
}
}
package com.zibo.zibo2022.top.utils;
import com.zibo.zibo2022.utils.StringUtility;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PrettyHtmlUtil {
private PrettyHtmlUtil() {
}
/**
* 将 html 格式化 + 加标签
* @param html html 字符串
* @param clazz 格式化后的类型,仅支持 Document.class String.class
* @return 格式化后的 html 字符串
*/
public static <T> T prettyHtml(String html, Class<T> clazz, boolean onlyBody) {
if (!StringUtility.isNotNullOrEmptyOrBlank(html)) {
return null;
}
// 清空 html 中所有注释信息
html = html.replaceAll("<!--.*?-->", "");
// 解析 html
Document doc = Jsoup.parse(html);
// 设置输出格式
doc.outputSettings(new Document.OutputSettings().outline(true));
// 根据换行符分割 html 标签
String[] split = onlyBody ? doc.body().html().split("\n") : doc.html().split("\n");
// 去掉 split 里面的空内容
List<String> list = new ArrayList<>();
for (String s : split) {
if (s!=null && !"".equals(s.trim())) {
list.add(s);
}
}
// 再次拼接 html 字符串
StringBuilder finalHtml = new StringBuilder();
// html 标签的正则表达式
Pattern pattern = Pattern.compile("<.*?>");
for (String s : list) {
// 匹配
Matcher matcher = pattern.matcher(s);
// 如果没有匹配到
if (!matcher.find()) {
// 去掉前后空格 + 加上标签
s = StringUtility.concat("<span>", s.trim(), "</span>");
}
finalHtml.append(s);
}
// 将再次拼接的 html 重新解析,美化代码格式
Document parse = Jsoup.parse(finalHtml.toString());
// 设置输出格式
parse.outputSettings(new Document.OutputSettings().outline(true));
// 返回对应格式的数据
if (clazz.equals(String.class)) {
return (T)parse.html();
} else if (clazz.equals(Document.class)) {
return (T)parse;
} else {
throw new RuntimeException("不支持的类型");
}
}
}
测试 html 即前言中的 html !可以看到“这是不带标签的文本”加上了
span
标签!
<html>
<head></head>
<body>
<span>这是不带标签的文本</span>
<h1>Hello World</h1>
<div>
<h1>这是大标题</h1>
<p>这是一段段落</p>
<p>这是一段段落</p>
<div>
<h1>这是小标题</h1>
<p>这是一段段落</p>
<p>这是一段段落</p>
</div>
</div>
<div>
<h1>这是大标题</h1>
<p>这是一段段落</p>
<p>这是一段段落</p>
</div>
</body>
</html>
package com.zibo.zibo2022.top.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 标签类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Tag {
/**
* 标签名
*/
private String name;
/**
* 标签的内容或者什么
*/
private String text;
/**
* 当前标签节点的 html 内容
*/
private String html;
/**
* 当前标签节点的属性列表
*/
private List<Attr> attrList;
/**
* 当前标签节点的子节点列表
*/
private List<Tag> children;
}
package com.zibo.zibo2022.top.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 属性类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Attr {
/**
* 属性名
*/
private String name;
/**
* 属性值
*/
private String value;
}
package com.zibo.zibo2022.utils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Pair<K, V> {
private K key;
private V value;
}
package com.zibo.zibo2022.top.utils;
import com.zibo.zibo2022.top.entity.Attr;
import com.zibo.zibo2022.top.entity.Tag;
import com.zibo.zibo2022.utils.Pair;
import com.zibo.zibo2022.utils.StringUtility;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Node;
import org.jsoup.select.Elements;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TagUtil {
private TagUtil() {
}
/**
* html 转成树形结构实体类
*
* @param file html 文件
* @param removeScript 是否移除 script 标签
* @param selector 标签选择器
* @param map 属性取值映 map (将标签的属性值或者文本取到 text 里面)
* @return 树形结构实体类
*/
public static Tag buildTag(File file, boolean onlyBody, boolean removeScript, String selector, Map<String, Pair<String, String>> map)
throws Exception {
// 读取 html 文件,转换成 Document 对象
Document document = Jsoup.parse(file, "UTF-8");
return buildTag(document.html(), onlyBody, removeScript, selector, map);
}
/**
* html 转成树形结构实体类
*
* @param html html 字符串
* @param onlyBody 是否只取 body 内容
* @param removeScript 是否移除 script 标签
* @param selector 标签选择器
* @param map 属性取值映 map (将标签的属性值或者文本取到 text 里面)
* @return 树形结构实体类
*/
public static Tag buildTag(String html, boolean onlyBody, boolean removeScript, String selector,
Map<String, Pair<String, String>> map) {
// 适配选择器
Document doc;
if (StringUtility.isNotNullOrEmptyOrBlank(selector)) {
doc = PrettyHtmlUtil.prettyHtml(Jsoup.parse(html).select(selector).html(), Document.class, onlyBody);
} else {
doc = PrettyHtmlUtil.prettyHtml(html, Document.class, onlyBody);
}
assert doc != null;
// 拿到 html Node 对象
Node node = doc.childNodes().get(0);
return buildTag(node, removeScript, map);
}
/**
* html 转成树形结构实体类 (递归)
*
* @param node html Node 对象
* @param removeScript 是否移除 script 标签
* @param map 属性取值映 map (将标签的属性值或者文本取到 text 里面)
* @return 树形结构实体类
*/
private static Tag buildTag(Node node, boolean removeScript, Map<String, Pair<String, String>> map) {
// 创建一个标签实体类
Tag tag = new Tag();
// 如果是 script 标签且需要移除,则移除
if (removeScript && "script".equals(node.nodeName())) {
return null;
}
// 创建属性列表
List<Attr> attrList = new ArrayList<>();
// 如果当前节点有属性,则添加到属性列表中
if (!node.attributes().isEmpty()) {
node.attributes().forEach(key -> attrList.add(new Attr(key.getKey(), key.getValue())));
}
// 设置标签名称
tag.setName(node.nodeName());
// 设置节点 html 内容
tag.setHtml(node.outerHtml());
// 设置属性列表
tag.setAttrList(attrList);
// 创建子标签列表
List<Tag> children = new ArrayList<>();
// 如果当前节点有子节点,则添加到子标签列表中
if (!node.childNodes().isEmpty()) {
for (Node child : node.childNodes()) {
// 跳过纯文本等内容
if (!child.nodeName().equals("#text") && !child.nodeName().equals("#comment") && !child.nodeName().equals("#document")
&& !child.nodeName().equals("#document-fragment") && !child.nodeName().equals("#document-type") &&
!child.nodeName().equals("#document-type-fragment")) {
// 往下一层递归
Tag buildTag = buildTag(child, removeScript, map);
// 如果不为空,则添加到子标签列表中
if (buildTag != null) {
children.add(buildTag);
}
}
}
}
// 如果里面是纯文本,则将文本设置到 text 属性中
if (node.childNodes().size() == 1 && "#text".equals(node.childNodes().get(0).nodeName())) {
tag.setText(node.childNodes().get(0).toString());
}
// 不同的标签根据 map 取值,并设置到 text 属性中
switch (node.nodeName().trim()) {
case "a" -> {
String a = tag.getAttrList().stream().filter(attr -> "href".equals(attr.getName())).findFirst().orElse(new Attr("href", ""))
.getValue();
if (map != null && map.containsKey("a") && !a.startsWith("http")) {
Pair<String, String> pair = map.get("a");
tag.setText(StringUtility.concat(pair.getKey(), a, pair.getValue()));
} else {
tag.setText(a);
}
if (node.childNodes().size() == 1) {
if ("#text".equals(node.childNodes().get(0).nodeName())) {
tag.setText(StringUtility.concat(tag.getText(), " ", node.childNodes().get(0).toString()));
}
}
}
case "img" -> {
String src = tag.getAttrList().stream().filter(attr -> "src".equals(attr.getName())).findFirst().orElse(new Attr("src", ""))
.getValue();
if (map != null && map.containsKey("img") && !src.startsWith("http")) {
Pair<String, String> pair = map.get("img");
tag.setText(StringUtility.concat(pair.getKey(), src, pair.getValue()));
} else {
tag.setText(src);
}
}
case "div" -> {
String replace =
tag.getAttrList().stream().filter(attr -> "class".equals(attr.getName())).findFirst().orElse(new Attr("class", ""))
.getValue().replace(" ", ".");
if (StringUtility.isNotNullOrEmptyOrBlank(replace)) {
tag.setText("." + replace);
}
}
default -> {
}
}
// 设置子标签列表
tag.setChildren(children);
return tag;
}
// 这个复杂测试用
public static Map<String, String> getTextByPath(Tag tag, Map<String, Pair<String, String>> pathMap) {
Map<String, String> newMap = new HashMap<>();
Document parse = Jsoup.parse(tag.getHtml());
for (Map.Entry<String, Pair<String, String>> entry : pathMap.entrySet()) {
Elements elements = parse.select(entry.getValue().getKey());
if (elements.size() == 1) {
if (StringUtility.isNotNullOrEmptyOrBlank(entry.getValue().getValue())) {
newMap.put(entry.getKey(), elements.get(0).attr(entry.getValue().getValue()));
} else {
newMap.put(entry.getKey(), elements.get(0).text());
}
} else {
System.out.println(elements);
throw new RuntimeException("找到的元素数量大于 1 !");
}
}
return newMap;
}
}
测试 html 即前言中的 html !
Tag(name=html, text=null, html=<html>
<head></head>
<body>
<span>这是不带标签的文本</span>
<h1>Hello World</h1>
<div>
<h1>这是大标题</h1>
<p>这是一段段落</p>
<p>这是一段段落</p>
<div>
<h1>这是小标题</h1>
<p>这是一段段落</p>
<p>这是一段段落</p>
</div>
</div>
<div>
<h1>这是大标题</h1>
<p>这是一段段落</p>
<p>这是一段段落</p>
</div>
</body>
</html>, attrList=[], children=[Tag(name=head, text=null, html=<head></head>, attrList=[], children=[]), Tag(name=body, text=null, html=<body>
<span>这是不带标签的文本</span>
<h1>Hello World</h1>
<div>
<h1>这是大标题</h1>
<p>这是一段段落</p>
<p>这是一段段落</p>
<div>
<h1>这是小标题</h1>
<p>这是一段段落</p>
<p>这是一段段落</p>
</div>
</div>
<div>
<h1>这是大标题</h1>
<p>这是一段段落</p>
<p>这是一段段落</p>
</div>
</body>, attrList=[], children=[Tag(name=span, text=这是不带标签的文本, html=<span>这是不带标签的文本</span>, attrList=[], children=[]), Tag(name=h1, text=Hello World, html=<h1>Hello World</h1>, attrList=[], children=[]), Tag(name=div, text=null, html=<div>
<h1>这是大标题</h1>
<p>这是一段段落</p>
<p>这是一段段落</p>
<div>
<h1>这是小标题</h1>
<p>这是一段段落</p>
<p>这是一段段落</p>
</div>
</div>, attrList=[], children=[Tag(name=h1, text=这是大标题, html=<h1>这是大标题</h1>, attrList=[], children=[]), Tag(name=p, text=这是一段段落, html=<p>这是一段段落</p>, attrList=[], children=[]), Tag(name=p, text=这是一段段落, html=<p>这是一段段落</p>, attrList=[], children=[]), Tag(name=div, text=null, html=<div>
<h1>这是小标题</h1>
<p>这是一段段落</p>
<p>这是一段段落</p>
</div>, attrList=[], children=[Tag(name=h1, text=这是小标题, html=<h1>这是小标题</h1>, attrList=[], children=[]), Tag(name=p, text=这是一段段落, html=<p>这是一段段落</p>, attrList=[], children=[]), Tag(name=p, text=这是一段段落, html=<p>这是一段段落</p>, attrList=[], children=[])])]), Tag(name=div, text=null, html=<div>
<h1>这是大标题</h1>
<p>这是一段段落</p>
<p>这是一段段落</p>
</div>, attrList=[], children=[Tag(name=h1, text=这是大标题, html=<h1>这是大标题</h1>, attrList=[], children=[]), Tag(name=p, text=这是一段段落, html=<p>这是一段段落</p>, attrList=[], children=[]), Tag(name=p, text=这是一段段落, html=<p>这是一段段落</p>, attrList=[], children=[])])])])
<!--引入依赖commons-beanutils-->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>
package com.zibo.zibo2022.top.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Iterator;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TreeNode {
private String name;
private List<TreeNode> children;
public String toString() {
StringBuilder buffer = new StringBuilder(50);
print(buffer, "", "");
return buffer.toString();
}
private void print(StringBuilder buffer, String prefix, String childrenPrefix) {
buffer.append(prefix);
buffer.append(name);
buffer.append('\n');
for (Iterator<TreeNode> it = children.iterator(); it.hasNext();) {
TreeNode next = it.next();
if (it.hasNext()) {
next.print(buffer, childrenPrefix + "├── ", childrenPrefix + "│ ");
} else {
next.print(buffer, childrenPrefix + "└── ", childrenPrefix + " ");
}
}
}
}
package com.zibo.zibo2022.top.utils;
import com.zibo.zibo2022.top.entity.TreeNode;
import com.zibo.zibo2022.utils.StringUtility;
import org.apache.commons.beanutils.PropertyUtils;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
public class TreeNodeUtil {
private TreeNodeUtil() {
}
/**
* 根据树结构对象生成树结构
* @param treeObject 树结构对象
* @param printPropName 打印属性名称
* @param childrenPropName 子节点属性名称
* @return 树结构对象
*/
public static TreeNode buildTreeNode(Object treeObject, String printPropName, String childrenPropName) {
// 创建树结构对象
TreeNode treeNode = new TreeNode();
// 要打印的内容
String printValue = "";
// 子节点列表
List<?> childrenValue = new ArrayList<>();
// 子树节点列表
List<TreeNode> children = new ArrayList<>();
try {
// 要打印的值
printValue = (String) PropertyUtils.getProperty(treeObject, printPropName);
childrenValue = (List<?>) PropertyUtils.getProperty(treeObject, childrenPropName);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
// 设置要打印的内容
treeNode.setName(printValue);
if (!childrenValue.isEmpty()) {
for (Object child : childrenValue) {
// 编辑每一个子节点,递归构建树节点
TreeNode childTreeNode = buildTreeNode(child, printPropName, childrenPropName);
// 添加树节点
children.add(childTreeNode);
}
}
// 设置树节点的子内容
treeNode.setChildren(children);
return treeNode;
}
/**
* 根据树结构对象生成树结构
* @param treeObject 树结构对象
* @param printPropNameList 打印属性名称列表
* @param childrenPropName 子节点属性名称
* @return 树结构对象
*/
public static TreeNode buildTreeNode(Object treeObject, List<String> printPropNameList, String childrenPropName) {
TreeNode treeNode = new TreeNode();
// 获取printPropName属性的值
String printValue = "";
List<?> childrenValue = new ArrayList<>();
List<TreeNode> children = new ArrayList<>();
StringBuilder sb = new StringBuilder();
try {
for (String printPropName : printPropNameList) {
String name = (String) PropertyUtils.getProperty(treeObject, printPropName);
sb.append(StringUtility.isNotNullOrEmptyOrBlank(name) ? name : "").append(" ");
}
printValue = sb.toString().trim();
childrenValue = (List<?>) PropertyUtils.getProperty(treeObject, childrenPropName);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
treeNode.setName(printValue);
if (! childrenValue.isEmpty()) {
for (Object child : childrenValue) {
TreeNode childTreeNode = buildTreeNode(child, printPropNameList, childrenPropName);
children.add(childTreeNode);
}
}
treeNode.setChildren(children);
return treeNode;
}
}
html
├── head
└── body
├── span
├── h1
├── div
│ ├── h1
│ ├── p
│ ├── p
│ └── div
│ ├── h1
│ ├── p
│ └── p
└── div
├── h1
├── p
└── p
仅打印标签!测试结果见前言!
package com.zibo.zibo2022.main;
import com.zibo.zibo2022.top.entity.Tag;
import com.zibo.zibo2022.top.entity.TreeNode;
import com.zibo.zibo2022.top.utils.TagUtil;
import com.zibo.zibo2022.top.utils.TreeNodeUtil;
import java.io.File;
import java.util.Arrays;
public class Main2 {
public static void main(String[] args) throws Exception {
Tag tag = TagUtil.buildTag(new File("C:\\Users\\Administrator\\Desktop\\test.html"), true, true,
null,
null
);
System.out.println(tag);
TreeNode treeNode = TreeNodeUtil.buildTreeNode(tag, "name", "children");
System.out.println(treeNode);
}
}
仅用于参考,并不能运行起来!时间不多了,后续我补充完整!可参考方法做适配!
测试的 html 代码:http://guozhivip.com/rank/
package com.zibo.zibo2022.main;
import com.zibo.zibo2022.top.entity.Tag;
import com.zibo.zibo2022.top.entity.TreeNode;
import com.zibo.zibo2022.top.utils.TagUtil;
import com.zibo.zibo2022.top.utils.TreeNodeUtil;
import com.zibo.zibo2022.utils.Pair;
import com.zibo.zibo2022.utils.StringUtility;
import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class Main {
public static void main(String[] args) throws Exception {
// 前后缀
Map<String, Pair<String, String>> concatMap = new HashMap<>();
concatMap.put("img", new Pair<>("http://www.baidu.com/rank/", ""));
Map<String, Pair<String, String>> pathMap = new HashMap<>();
pathMap.put("a", new Pair<>("a", "href"));
pathMap.put("span", new Pair<>("a > span", ""));
pathMap.put("img", new Pair<>("a > div > img", "src"));
Tag tag = TagUtil.buildTag(new File("C:\\Users\\Administrator\\Desktop\\test.html"), true, true,
null,
null
);
Optional<Tag> bodyOptional = tag.getChildren().stream().filter(t -> "body".equals(t.getName())).findFirst();
if (bodyOptional.isPresent()) {
Tag body = bodyOptional.get();
for (Tag child : body.getChildren()) {
Map<String, String> hashMap = TagUtil.getTextByPath(child, pathMap);
for (Map.Entry<String, String> entry : hashMap.entrySet()) {
if (concatMap.get(entry.getKey()) != null) {
entry.setValue(StringUtility.concat(concatMap.get(entry.getKey()).getKey(), entry.getValue(), concatMap.get(entry.getKey()).getValue()));
}
}
System.out.println(hashMap);
}
}
TreeNode treeNode = TreeNodeUtil.buildTreeNode(tag, Arrays.asList("name", "text"), "children");
System.out.println(treeNode);
}
}
太长了,贴一部分出来!
html
├── head
└── body
├── div .col.pc
│ └── a https://weibo.com/newlogin?tabtype=search&url=
│ ├── span 微博热搜
│ └── div .slogo
│ └── img http://www.baidu.com/rank/images/wb.png
├── div .col.pc
│ └── a https://top.baidu.com/board
│ ├── span 百度风云榜
│ └── div .slogo
│ └── img http://www.baidu.com/rank/images/bd.png
├── div .col.pc
│ └── a https://www.sogou.com/web?query=%E4%BB%8A%E6%97%A5%E7%83%AD%E6%90%9C
│ ├── span 搜狗热搜榜
│ └── div .slogo
│ └── img http://www.baidu.com/rank/images/sg.png
用于取数据!也有点长,贴一部分出来!
{a=https://weibo.com/newlogin?tabtype=search&url=, img=http://www.baidu.com/rank/images/wb.png, span=微博热搜}
{a=https://top.baidu.com/board, img=http://www.baidu.com/rank/images/bd.png, span=百度风云榜}
{a=https://www.sogou.com/web?query=%E4%BB%8A%E6%97%A5%E7%83%AD%E6%90%9C, img=http://www.baidu.com/rank/images/sg.png, span=搜狗热搜榜}
{a=https://trends.so.com/hot, img=http://www.baidu.com/rank/images/360.png, span=360实时热点}
{a=https://weixin.sogou.com/, img=http://www.baidu.com/rank/images/wx.png, span=微信热门}
{a=https://weibo.com/newlogin?tabtype=topic&url=, img=http://www.baidu.com/rank/images/wb.png, span=微博话题榜}
{a=https://m.weibo.cn/p/106003type=25&t=3&disable_hot=1&filter_type=realtimehot, img=http://www.baidu.com/rank/images/wb.png, span=微博热搜}
{a=https://top.baidu.com/board?tab=realtime, img=http://www.baidu.com/rank/images/bd.png, span=百度热搜}
{a=https://ib.snssdk.com/rogue/aladdin_landingpage/template/aladdin_landingpage/hot_words.html?isBrowser=true&traffic_source=, img=http://www.baidu.com/rank/images/tt.png, span=头条热点}
{a=https://m.sogou.com/web/searchList.jsp?keyword=%E4%BB%8A%E6%97%A5%E7%83%AD%E7%82%B9, img=http://www.baidu.com/rank/images/sg.png, span=搜狗热搜}
{a=https://m.so.com/s?q=%E4%BB%8A%E6%97%A5%E7%83%AD%E7%82%B9, img=http://www.baidu.com/rank/images/360.png, span=360热搜}
{a=https://weixin.sogou.com/, img=http://www.baidu.com/rank/images/wx.png, span=微信热门}
{a=https://www.zhihu.com/billboard, img=http://www.baidu.com/rank/images/zh.png, span=知乎热榜}
最终使用方式还在优化,基本的路已经走通了!