HTML解析库Gumbo简单使用记录
Gumbo简介#
Gumbo是谷歌开源的一个纯C编写的HTML解析库,性能很好,就是用起来比较麻烦。
github地址https://github.com/google/gumbo-parser
还有一个C++封装的版本https://github.com/lazytiger/gumbo-query.git
关于HTML的参考,可见https://developer.mozilla.org/zh-CN/docs/Web/HTML
最近准备写一个爬虫,用于爬取epsg.io上的数据,所以找了这个库用于HTML的解析。其实我这个简单的爬取固定位置的内容,用这个实在是有点杀鸡用牛刀了,直接做字符串搜索会更方便。
使用记录#
关于这个的使用,网上找不到太多的资料。
这里有一个https://blog.csdn.net/fjb2080/article/details/78992851
这个图片上实际已经把相关的关系描述清楚了,我这里只做简单的补充。
1、GumboNode的类型#
对于一个GumboNode
结构体对象,需要通过它的GumboNodeType type
字段判断其类型后,可根据类型对成员v
进行操作。
v
是一个union
对象,它可以是GumboDocument
、GumboElement
、GumboText
三种类型之一。
1、GUMBO_NODE_DOCUMENT 文档节点#
文档节点表示的是一个完整的html文档,就是从<html>
到</html>
之间的全部信息。对于v
可取GumboDocument
类型的成员document
。
对于文档节点,其内部包含的元素节点都在GumboVector children
中。
2、GUMBO_NODE_ELEMENT 元素节点#
只要是含有标签tag
的部分,都是元素节点。这个可以简单的理解为,只要是<标签名>
到</标签名>
之间的就是一个元素节点的内容(有的元素是单标签的),元素节点可以有包含嵌套关系,子节点都在GumboVector children
中。
/** * 用于表示所有HTML元素的结构。 它包含有关标记,属性和子节点的信息。 */ typedef struct { /** * GumboNodes数组,包含此元素的子元素。 保存的是GumboNode的指针。 */ GumboVector /* GumboNode* */ children; /** 这个元素的GumboTag(标签,HTML的标签是定义好的)枚举值 */ GumboTag tag; /** 此元素的GumboNamespaceEnum值(表示这个是HTML、SVG还是MATHML)*/ GumboNamespaceEnum tag_namespace; /** * 指向此元素的原始标记文本的GumboStringPiece,直接指向源缓冲区。 * 如果标记是通过算法插入的(例如,<head>或<tbody>插入),则这将是一个零长度字符串。 */ GumboStringPiece original_tag; /** * 指向此元素的原始结束标记文本的GumboStringPiece。 * 如果以算法方式插入结束标记(例如,关闭自闭标记),则这将是一个零长度字符串。 */ GumboStringPiece original_end_tag; /** 记录元素开始标签在来源字符串中的起始位置。 */ GumboSourcePosition start_pos; /** 记录元素结束标签在来源字符串中的起始位置。 */ GumboSourcePosition end_pos; /** * GumboAttributes数组,按照解析顺序包含此元素标签的属性 * 数组保存的是GumboAttribute的指针 */ GumboVector /* GumboAttribute* */ attributes; } GumboElement;
3、GUMBO_NODE_TEXT 文本节点#
文本节点,对于v
可取GumboText
类型的成员text
。
Gumbo
在解析的时候,对于\r\n
这种都会解析为一个独立的文件节点。
4、GUMBO_NODE_CDATA#
CDATA
节点是一个比较特殊的节点,这个节点用于传输需要浏览器不做解析,原封不动的当做文本的内容。所以对于v
也是取GumboText
类型的成员text
。
5、GUMBO_NODE_COMMENT#
注释节点,这个用于保存html中的注释,对于这个节点,对于v
也是取GumboText
类型的成员text
。取出来的GumboText
对象中的text
成员不包含注释分隔符。
6、GUMBO_NODE_WHITESPACE#
这是一个文本节点的特例,文本的内容都是空白字符(空格、TAB、回车)。v
也是取GumboText
。
7、GUMBO_NODE_TEMPLATE#
模板节点。就是标签<template>
包含的部分。
这与GUMBO_NODE_ELEMENT是分开的,因为许多客户端库都希望忽略模板节点的内容,如规范所示。 在GUMBO_NODE_ELEMENT上递归会在这里做正确的事情,而想要包含模板内容的客户端也应该检查GUMBO_NODE_TEMPLATE。 v将是一个GumboElement。
2、简单的使用#
为了方便使用,我简单的封装了两个函数,对于我的使用已经足够了。如果需要更方便的使用,可以考虑https://github.com/lazytiger/gumbo-query.git
1、用于方便一点的查找子节点的#
std::vector<GumboNode*> find_sub_node(const GumboNode* parentNode, GumboNodeType type, GumboTag tag, int attrCount, ...) { std::vector<GumboNode*> subNode; const GumboVector* vec = &(parentNode->v.element.children); for (int i = 0; i < vec->length; ++i) { GumboNode* node = (GumboNode*)vec->data[i]; if (node->type != type || (type == GUMBO_NODE_ELEMENT && node->v.element.tag != tag)) { continue; } int pattend = 0; va_list vl; va_start(vl, attrCount); for (int ai = 0; ai < attrCount; ++ai) { const char* name = va_arg(vl, char*); const char* value = va_arg(vl, char*); GumboAttribute* attr = gumbo_get_attribute(&(node->v.element.attributes), name); if (attr == NULL || strcmp(value, attr->value) != 0) { continue; } pattend += 1; } va_end(vl); if (pattend == attrCount) { subNode.push_back(node); } } return subNode; }
2、用于方便的查找文本子节点的#
std::vector<std::string> find_sub_text(const GumboNode* parentNode) { std::vector<std::string> subText; const GumboVector* vec = &(parentNode->v.element.children); for (int i = 0; i < vec->length; ++i) { GumboNode* node = (GumboNode*)vec->data[i]; if (GUMBO_NODE_TEXT == node->type) { subText.push_back(node->v.text.text); continue; } } return subText; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
2017-09-18 各个版本VS编译好的GDAL库下载
2016-09-18 C++11的简单线程池代码阅读