【c语言】使用gumbo解析HTML
之前使用过PHP的Simple HTML DOM简单地解析HTML但PHP终非我所熟悉的语言,虽然我并不对语言抱有绝对的执着= =(什么你不相信,好吧,不管你信不信,反正我是信了= =)。虽然可以简单地使用正则表达式来解析HTML但我不是希望能够找到一个合适的HTML解析库,网上搜索了下关于c语言解析HTML的库,好像不是挻多的样子,我搜索到了google的gumbo, gumbo是开源的,可以从这里得到它 https://github.com/google/gumbo-parser 我们需要下载回来手动编译安装,这里以linux debian为例 git clone https://github.com/google/gumbo-parser
cd gumbo-parser
./autogen.sh
./configure 这些一般都会非常顺利,没什么好说的,接下来就是
make
我要执行make后发现有一个错误导致无法编译通过,不知道各位是什么情况,给出的错误提示是benchmarks/benchmark.cc
文件中使用了未定义的函数clock_gettime
man了一下,该函数需要包含time.h头文件,打开benchmark.cc文件查看的确已经包含了time.h头文件,很苦恼,突然一下子就懵了,不过还好我反应还算快,看到manpages中写到
Link with -lrt (only for glibc versions before 2.17).
于是猜测没有链接库,使用vim打开Makefile文件,这个文件内容太多= =,要分析的话有些费劲,不过机智的我还是很快地通过benchmark关键字定位到了benchmark_LDADD这个变量,然后在后面加上
-lrt
注意有空格
再次make,果然成功了。。。。。。。。。。。
编译完成之后就可以使用make install进行安装了,你可能需要使用root用户权限,因为默认的安装目录在/usr/local/下 gumbo的源码提供了几个示例程序,一个c语言写的获取标题的源码和另外三个使用c++编写的代码,我全都看了(你看,我说过我不是绝对的语言执着者吧,很不幸,这些程序我都看懂了= =) 简单地说gumbo的使用很简单,使用gumbo_parse或者gumbo_parse_with_options就可以得到一个GumboOutput数据结构,我们就可以从该结构中寻找我们想要的东西了。
我们先来看一个简单的例子,就拿获取title来说吧,我决定用自己写的解析代码而不是gumbo源码提供的好个示例,因为我发现该程序无法解析出我使用的示例HTML文本文件= =,所以我就自己写个吧。。。。。。 #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
/* 包含头文件 */
#include <gumbo.h> void get_title(GumboNode *node)
{
GumboVector *children;
int i; /* 如果当前节点不是一个元素的话直接返回 */
if(node->type != GUMBO_NODE_ELEMENT) return;
/* 获取该节点的所有子元素节点 */
children=&node->v.element.children; /* 检查当前节点的标签是否为TITLE(title)
* 如果是则输出该节点下第一个节点的文本内容 */
if(node->v.element.tag == GUMBO_TAG_TITLE)
printf("%s\n",((GumboNode *)children->data[])->v.text.text); /* 递归该节点下的所有子节点 */
for(i=;i < children->length;++i)
get_title(children->data[i]);
} int main(int argc,char **argv)
{
struct stat buf;
GumboOutput *output;
FILE *fp;
char *data; /* 读取HTML文本文件 */
if(!(fp=fopen(argv[],"rb"))) return -;
stat(argv[],&buf);
data=malloc(sizeof(char)*(buf.st_size+));
fread(data,sizeof(char),buf.st_size,fp);
fclose(fp);
data[buf.st_size]=; /* 解析HTML文本文件 */
output=gumbo_parse(data);
/* 获取TITLE */
get_title(output->root); /* 销毁,释放内存 */
gumbo_destroy_output(&kGumboDefaultOptions,output);
free(data); return ;
}
注释已经写的很清楚了,首先我们的节奏是这个样子的:
第一步加载HTML文本文件,我们把它读到一个buf中,
第二步我们进行解析出GumboOutput数据结构
第三步在GumboOptout这个数据结构中找出title标签
最后我们输出内容,gumbo的步骤基本上就是这个样子的了,使用gcc编译的时候需要加上
-lgumbo
下面再说一个例子,该例子中的HTML文件内容是各国DNS的IP地址以及物理地址,大概的格式是 <dt><dd class="ipstart">开始ip地址</dd><dd class="ipend">结束ip地址</dd><dd class="address">物理地址</dd></dt>
我们的解析步骤是获取所有dt标签再获取所有dd标签,然后分别输出dd标签中class属性为ipstart、ipend、address的内 容,下面放代码,由于原HTML文本文件内容放多,我不便放上来,这里就使用在线抓取的方式获取HTML文本,所以这里给出的是HTML文本的url地 址,至目前写代码这一刻该程序还是完全能够正常工作的,日后会该网页是否会因该网页做调整等原因解析出错就不得而知了。 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <oauth.h>
/* 包含头文件 */
#include <gumbo.h> #define URL "http://ip.yqie.com/dns_usa.htm" void print_dns(GumboNode *node,GumboAttribute *attr)
{
/* 获取子节点 */
GumboNode *ip=(GumboNode *)(&node->v.element.children)->data[]; /* 根据class属性的值打印结果 */
if(strcmp(attr->value,"ipstart") == )
{
if(ip->type == GUMBO_NODE_TEXT)
printf("开始IP:%s ",ip->v.text.text);
}
else if(strcmp(attr->value,"ipend") == )
{
if(ip->type == GUMBO_NODE_TEXT)
printf("结束IP:%s ",ip->v.text.text);
}
else if(strcmp(attr->value,"address") == )
{
if(ip->type == GUMBO_NODE_TEXT)
printf("物理地址:%s\n",ip->v.text.text);
}
} void get_dns(GumboNode *node,GumboTag tag)
{
GumboVector *children;
GumboAttribute *attr;
int i; if(node->type != GUMBO_NODE_ELEMENT) return;
/* 获取当前节点class属性 */
if(attr=gumbo_get_attribute(&node->v.element.attributes,"class"))
print_dns(node,attr); /* 当前节点子节点 */
children=&node->v.element.children;
/* 如果当前节点标签为td我们就查找dd标签 */
if(node->v.element.tag == GUMBO_TAG_DT)
for(i=;i < children->length;++i)
get_dns(children->data[i],GUMBO_TAG_DD); /* 查找所有<dt>标签 */
for(i=;i < children->length;++i)
get_dns(children->data[i],GUMBO_TAG_DT);
} int main(void)
{
GumboOutput *output;
char *buf; /* 下载HTML文本文件 */
buf=oauth_http_get(URL,NULL);
if(!buf) return-;
/* 解析 */
output=gumbo_parse(buf);
if(!output)
{
free(buf);
return -;
}
/* 获取我们想要的内容 <dt>*/
get_dns(output->root,GUMBO_TAG_DT); /* 释放资源 */
gumbo_destroy_output(&kGumboDefaultOptions,output);
free(buf); return ;
}
由于使用了oauth所以使用gcc编译时需要加上-loauth参数
【c语言】使用gumbo解析HTML的更多相关文章
- C语言文件操作解析(五)之EOF解析(转载)
C语言文件操作解析(五)之EOF解析 在C语言中,有个符号大家都应该很熟悉,那就是EOF(End of File),即文件结束符.但是很多时候对这个理解并不是很清楚,导致在写代码的时候经常出错,特 ...
- 【转】C语言文件操作解析(三)
原文网址:http://www.cnblogs.com/dolphin0520/archive/2011/10/07/2200454.html C语言文件操作解析(三) 在前面已经讨论了文件打开操作, ...
- C语言创建及解析Json的使用法则
参考原文:http://blog.csdn.net/xukai871105/article/details/33013455 JSON(JavaScriptObject Notation)是一种轻量级 ...
- 在浏览器的背后(二) —— HTML语言的语法解析
当你看到这篇文章意味着我辜负了@教主的殷切期望周末木有去约会,以及苏老师@我思故我在北京鼓楼的落井下石成功了…… 本文demo powered by 已经结婚的@老赵的不再维护的wind.js 物是人 ...
- 纯C语言INI文件解析
原地址:http://blog.csdn.net/foruok/article/details/17715969 在一个跨平台( Android .Windows.Linux )项目中配置文件用 IN ...
- c语言复杂声明解析
这是个好东西,接触c语言好几年了,第一次看到这东西,惊喜万分. 先提供个分析案例,以后看方便 vector <int> * (*seq_array[]) (int )={func1,fun ...
- C语言之歌词解析
0x00 脚下的路 不知道为啥要写这个小标题,可能是年轻的心想体验一下苍老的感觉,抑或是少年的一阵迷茫.混沌的四年,终究还是入了这一行.从初时的不知,到现在的刚开始,中间的间隔竟是四年之久,想起了陈奕 ...
- Go语言 命令行解析(一)
命令行启动服务的方式,在后端使用非常广泛,如果有写过C语言的同学相信不难理解这一点!在C语言中,我们可以根据argc和argv来获取和解析命令行的参数,从而通过不同的参数调取不同的方法,同时也可以用U ...
- [JZOJ3588]【中山市选2014】J语言(表达式解析+栈)
Description J语言作为一门编程语言,诞生于20世纪90年代.............. 好学的小H今天又学到了一种新东西——J语言.显然,J语言的背景已经被小H忘得一干二净了,但是小H仍然 ...
随机推荐
- Code First 数据库迁移
当 Entity Framework Code First 的数据模型发生改变时,默认会引发一个System.InvalidOperationException 的异常.解决方法是使用DropCrea ...
- git更新到仓库
记录每次更新到仓库 现在我们手上已经有了一个真实项目的 Git 仓库,并从这个仓库中取出了所有文件的工作拷贝.接下来,对这些文件作些修改,在完成了一个阶段的目标之后,提交本次更新到仓库. 请记住,工作 ...
- 《Android源码设计模式》----面向对象六大原则
1.单一职责原则 Single Respoonsibility Principle(SRP) --封装 2.开闭原则 Open Close Principle(OCP)--对扩展开放,对修改封闭 3. ...
- javacript 实现两个数组的差集
<script type="text/javascript"> var array1 = [1,2,3,4,5,6,7,8,9]; var arra ...
- 深入理解yield
yield的英文单词意思是生产,刚接触Python的时候感到非常困惑,一直没弄明白yield的用法. 只是粗略的知道yield可以用来为一个函数返回值塞数据,比如下面的例子: 1 2 3 def ad ...
- event使用说明和DHTML参数属性
event 对象 代表事件状态,如事件发生的元素,键盘状态,鼠标位置和鼠标按钮状态. DHTML元素属性列表 属性 描述 abstract 使用 event 对象获取高级流重定向器(ASX)文件中项目 ...
- Codeforces 1085G(1086E) Beautiful Matrix $dp$+树状数组
题意 定义一个\(n*n\)的矩阵是\(beautiful\)的,需要满足以下三个条件: 1.每一行是一个排列. 2.上下相邻的两个元素的值不同. 再定义两个矩阵的字典序大的矩阵大(从左往右从上到下一 ...
- BZOJ.2286.[SDOI2011]消耗战(虚树 树形DP)
题目链接 BZOJ 洛谷P2495 树形DP,对于每棵子树要么逐个删除其中要删除的边,要么直接断连向父节点的边. 如果当前点需要删除,那么直接断不需要再管子树. 复杂度O(m*n). 对于两个要删除的 ...
- 批量将Java源代码文件的编码从GBK转为UTF-8
主要参考: http://blog.csdn.net/liu_qiqi/article/details/38706497 使用common io批量将java编码从GBK转UTF-8 http://w ...
- 改变手机浏览器(iPhone/Android)上文本输入框的默认弹出键盘
iPhone/iPad和Android提供不同的的键盘输入类型,触发合适的键盘将极大地改善用户体验. 键盘类型 默认: 默认键盘的字母模式 数字: 默认键盘的数字模式,(含小数点等) 邮件: 与默 ...