【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[0])->v.text.text);

 /* 递归该节点下的所有子节点 */
 for(i=0;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[1],"rb"))) return -1;
 stat(argv[1],&buf);
 data=malloc(sizeof(char)*(buf.st_size+1));
 fread(data,sizeof(char),buf.st_size,fp);
 fclose(fp);
 data[buf.st_size]=0;

 /* 解析HTML文本文件 */
 output=gumbo_parse(data);
 /* 获取TITLE */
 get_title(output->root);

 /* 销毁,释放内存 */
 gumbo_destroy_output(&kGumboDefaultOptions,output);
 free(data);

 return 0;
}
注释已经写的很清楚了,首先我们的节奏是这个样子的:
第一步加载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[0];

 /* 根据class属性的值打印结果 */
 if(strcmp(attr->value,"ipstart") == 0)
 {
  if(ip->type == GUMBO_NODE_TEXT)
   printf("开始IP:%s ",ip->v.text.text);
 }
 else if(strcmp(attr->value,"ipend") == 0)
 {
  if(ip->type == GUMBO_NODE_TEXT)
   printf("结束IP:%s ",ip->v.text.text);
 }
 else if(strcmp(attr->value,"address") == 0)
 {
  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=0;i < children->length;++i)
   get_dns(children->data[i],GUMBO_TAG_DD);

 /* 查找所有<dt>标签 */
 for(i=0;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-1;
 /* 解析 */
 output=gumbo_parse(buf);
 if(!output)
 {
  free(buf);
  return -1;
 }
 /* 获取我们想要的内容 <dt>*/
 get_dns(output->root,GUMBO_TAG_DT);

 /* 释放资源 */
 gumbo_destroy_output(&kGumboDefaultOptions,output);
 free(buf);

 return 0;
}
由于使用了oauth所以使用gcc编译时需要加上-loauth参数
时间: 2024-11-03 20:52:54

【c语言】使用gumbo解析HTML的相关文章

swift语言-隐式解析可选

隐式解析可选 隐式解析可选和可选类型的区别 声明的时候需要在变量类型后面加叹号(!). #import Foundation var optValue: Int! = nil if optValue != nil{ println("not nil") }else{ println("nil") } 强制解析时候可以不加叹号. #import Foundation var optValue: Int! = nil println(optValue)

C语言中申明解析方法的总结

转载自:http://soft.chinabyte.com/database/248/12193748.shtml <C专家编程>第3章学习 C语言中的声明是一个比较令人头痛的问题,尤其一些复杂的声明看起来甚至会令人感到恐怖,如果您是一位初学者,您一定会对下面这几个式子感到畏惧.因为对于刚接触这种形式的人来说,这简直太复杂了,当然前提是假定您未掌握分析这方面的专业知识. 第一个声明: void(*signal(int sig, void (*func)(int)))(int);第二个声明:co

Go语言的参数解析(flag)

flag参数解析,我原以为会了,结果今天发现,原来比我印象中还强大些. 索性记一下. 第一个例子是常见用法: //Flag例1 //author:Xiong Chuan Liang //date:2015-4-10 package main import ( "flag" "fmt" "os" ) var ( levelFlag = flag.Int("level", 0, "级别") bnFlag int

c语言相关知识点解析

本文介绍如何使用c语言 运行环境 vs2017,ctrl + F5 运行程序 目录 初识 常量和变量 标识符 数据类型 运算符 输入输出语句 流程控制语句 变量和函数的属性 指针 结构体 结构体在数据结构中的使用 共用体类型 typedef关键字的使用 文件操作 初识 c程序基本结构 #include "stdafx.h" // 指定头文件,想使用函数库中的函数必须指明 int main() // 程序入口 { int a = 111, b = 222, sum; sum = a +

&lt;JavaScript语言精粹&gt;JSON解析器源码阅读

1 // 这是一个用JavaScript编写JSON解析器的实现方案: 2 var jsonParser = (function() { 3 // 这是一个能把JSON文本解析成JavaScript数据结构的函数. 4 // 它是一个简单的递归降序解析器. 5 // 我们在另一个函数中定义此函数,以避免创建全局变量. 6 7 var at, // 当前字符索引 8 ch, // 当前字符 9 escapee = { 10 '"': '"', 11 "\\": &qu

在QQ群里有人提问有没有C语言的XML解析,偶然想到了这个问题:C++调用C库,简单试验:

我的电脑Mac,系统MaveRicks. 写一个C代码: #include<stdio.h> void hello(){ printf("Hello, this is from C Language ~\n"); } 编译一个库文件: franklinMacmini:~ git$ gcc --shared -o libhello.so hello.c franklinMacmini:~ git$ ll -tr drwx------+ 3 git staff 102 Sep

领域特定语言 笔记,解析器组合子

类图,继承和聚合关系,详图和简图:composite模式. 这本书[http://book.douban.com/subject/21964984/],第210页-216页. grammar用 parser comibinator in java描述 composite pattern composite pattern应用于 parser comibinator,简图 继承和聚合关系,展开的详图

JS 语言的Function 解析

1.最基本的作为一个本本分分的函数声明使用. 复制代码代码如下: function func(){} 或 var func=function(){}; 2.作为一个类构造器使用: 复制代码代码如下: function class(){} class.prototype={}; var item=new class(); 3.作为闭包使用: 复制代码代码如下: (function(){ //独立作用域 })(); 4.可以作为选择器使用: 复制代码代码如下: var addEvent=new fu

c语言数组问题解析

#import <Foundation/Foundation.h> #define COUNT 10 int main(int argc, const char * argv[]) {     //1.随机产生20个[10 , 50]的正整数存到数组中,并求数组中的所有元素最大值.最小值.平均值以及各元素之和.第二大值. //    int max = 0, min = 0, sum = 0, secondMax = 0; //    float ave = 0.0; //    int a[