xml,json都有大量的库来解析,我们如何解析html呢?

TFHpple是一个小型的封装,可以用来解析html,它是对libxml的封装,语法是xpath。
今天我看到一个直接用libxml来解析html,参看:http://www.cocoanetics.com/2011/09/taming-html-parsing-with-libxml-1/#comment-3090 那张图画得一目了然,很值得收藏。这个文章中的源码不能遍历所有的html,我做了一点修改可以将html遍历打印出来

// NSData data contains the document data 
// encoding is the NSStringEncoding of the data 
// baseURL the documents base URL, i.e. location

CFStringEncoding cfenc = CFStringConvertNSStringEncodingToEncoding(encoding); 
CFStringRef cfencstr = CFStringConvertEncodingToIANACharSetName(cfenc); 
const char *enc = CFStringGetCStringPtr(cfencstr, 0);

htmlDocPtr _htmlDocument = htmlReadDoc([data bytes], 
[[baseURL absoluteString] UTF8String], 
enc, 
XML_PARSE_NOERROR | XML_PARSE_NOWARNING); 
if (_htmlDocument) 

xmlFreeDoc(_htmlDocument); 
}

xmlNodePtr currentNode = (xmlNodePtr)_htmlDocument;

while (currentNode) 

// output node if it is an element

if (currentNode->type == XML_ELEMENT_NODE) 

NSMutableArray *attrArray = [NSMutableArray array];

for (xmlAttrPtr attrNode = currentNode->properties; attrNode; attrNode = attrNode->next) 

xmlNodePtr contents = attrNode->children;

[attrArray addObject:[NSString stringWithFormat:@"%s='%s'", attrNode->name, contents->content]]; 
}

NSString *attrString = [attrArray componentsJoinedByString:@" "];

if ([attrString length]) 

attrString = [@" " stringByAppendingString:attrString]; 
}

NSLog(@"<%s%@>", currentNode->name, attrString); 

else if (currentNode->type == XML_TEXT_NODE) 

//NSLog(@"%s", currentNode->content); 
NSLog(@"%@", [NSString stringWithCString:(const char *)currentNode->content encoding:NSUTF8StringEncoding]); 

else if (currentNode->type == XML_COMMENT_NODE) 

NSLog(@"/* %s */", currentNode->name); 
}

if (currentNode && currentNode->children) 

currentNode = currentNode->children; 

else if (currentNode && currentNode->next) 

currentNode = currentNode->next; 

else 

currentNode = currentNode->parent;

// close node 
if (currentNode && currentNode->type == XML_ELEMENT_NODE) 

NSLog(@"</%s>", currentNode->name); 
}

if (currentNode->next) 

currentNode = currentNode->next; 

else 

while(currentNode) 

currentNode = currentNode->parent; 
if (currentNode && currentNode->type == XML_ELEMENT_NODE) 

NSLog(@"</%s>", currentNode->name); 
if (strcmp((const char *)currentNode->name, "table") == 0) 

NSLog(@"over"); 

}

if (currentNode == nodes->nodeTab[0]) 

break; 
}

if (currentNode && currentNode->next) 

currentNode = currentNode->next; 
break; 



}

if (currentNode == nodes->nodeTab[0]) 

break; 

}

不过我还是喜欢用TFHpple,因为它很简单,也好用,但是它的功能不是很完完善。比如,不能获取children node,我就写了两个方法,一个是获取children node,一个是获取所有的contents. 还有node的属性content的key与node's content的key一样,都是@"nodeContent", 正确情况下属性的应是@"attributeContent",
所以我写了这个方法,同时修改node属性的content key.
NSDictionary *DictionaryForNode2(xmlNodePtr currentNode, NSMutableDictionary *parentResult) 

NSMutableDictionary *resultForNode = [NSMutableDictionary dictionary];

if (currentNode->name) 

NSString *currentNodeContent = 
[NSString stringWithCString:(const char *)currentNode->name encoding:NSUTF8StringEncoding]; 
[resultForNode setObject:currentNodeContent forKey:@"nodeName"]; 
}

if (currentNode->content) 

NSString *currentNodeContent = [NSString stringWithCString:(const char *)currentNode->content encoding:NSUTF8StringEncoding];

if (currentNode->type == XML_TEXT_NODE) 

if (currentNode->parent->type == XML_ELEMENT_NODE) 

[parentResult setObject:currentNodeContent forKey:@"nodeContent"]; 
return nil; 
}

if (currentNode->parent->type == XML_ATTRIBUTE_NODE) 

[parentResult 
setObject: 
[currentNodeContent 
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] 
forKey:@"attributeContent"]; 
return nil;



}

xmlAttr *attribute = currentNode->properties; 
if (attribute) 

NSMutableArray *attributeArray = [NSMutableArray array]; 
while (attribute) 

NSMutableDictionary *attributeDictionary = [NSMutableDictionary dictionary]; 
NSString *attributeName = 
[NSString stringWithCString:(const char *)attribute->name encoding:NSUTF8StringEncoding]; 
if (attributeName) 

[attributeDictionary setObject:attributeName forKey:@"attributeName"]; 
}

if (attribute->children) 

NSDictionary *childDictionary = DictionaryForNode2(attribute->children, attributeDictionary); 
if (childDictionary) 

[attributeDictionary setObject:childDictionary forKey:@"attributeContent"]; 

}

if ([attributeDictionary count] > 0) 

[attributeArray addObject:attributeDictionary]; 

attribute = attribute->next; 
}

if ([attributeArray count] > 0) 

[resultForNode setObject:attributeArray forKey:@"nodeAttributeArray"]; 

}

xmlNodePtr childNode = currentNode->children; 
if (childNode) 

NSMutableArray *childContentArray = [NSMutableArray array]; 
while (childNode) 

NSDictionary *childDictionary = DictionaryForNode2(childNode, resultForNode); 
if (childDictionary) 

[childContentArray addObject:childDictionary]; 

childNode = childNode->next; 

if ([childContentArray count] > 0) 

[resultForNode setObject:childContentArray forKey:@"nodeChildArray"]; 

}

return resultForNode; 
}

TFHppleElement.m里加了两个key 常量
NSString * const TFHppleNodeAttributeContentKey = @"attributeContent"; 
NSString * const TFHppleNodeChildArrayKey = @"nodeChildArray";

并修改获取属性方法为:
- (NSDictionary *) attributes 

NSMutableDictionary * translatedAttributes = [NSMutableDictionary dictionary]; 
for (NSDictionary * attributeDict in [node objectForKey:TFHppleNodeAttributeArrayKey]) { 
[translatedAttributes setObject:[attributeDict objectForKey:TFHppleNodeAttributeContentKey] 
forKey:[attributeDict objectForKey:TFHppleNodeAttributeNameKey]]; 

return translatedAttributes; 
}

并添加获取children node 方法:
- (BOOL) hasChildren 

NSArray *childs = [node objectForKey: TFHppleNodeChildArrayKey];

if (childs) 

return YES; 
}

return NO; 
}

- (NSArray *) children 

if ([self hasChildren]) 
return [node objectForKey: TFHppleNodeChildArrayKey]; 
return nil; 
}

参看:http://giles-wang.blogspot.com/2011/08/iphoneansi.html

原文:http://blog.csdn.net/favormm/article/details/6794487

HTML 解析的更多相关文章

  1. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  2. .NET Core中的认证管理解析

    .NET Core中的认证管理解析 0x00 问题来源 在新建.NET Core的Web项目时选择“使用个人用户账户”就可以创建一个带有用户和权限管理的项目,已经准备好了用户注册.登录等很多页面,也可 ...

  3. Html Agility Pack 解析Html

    Hello 好久不见 哈哈,今天给大家分享一个解析Html的类库 Html Agility Pack.这个适用于想获取某网页里面的部分内容.今天就拿我的Csdn的博客列表来举例. 打开页面  用Fir ...

  4. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  5. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  6. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  7. Asp.Net WebApi核心对象解析(下篇)

    在接着写Asp.Net WebApi核心对象解析(下篇)之前,还是一如既往的扯扯淡,元旦刚过,整个人还是处于晕的状态,一大早就来处理系统BUG,简直是坑爹(好在没让我元旦赶过来该BUG),队友挖的坑, ...

  8. 【知识必备】内存泄漏全解析,从此拒绝ANR,让OOM远离你的身边,跟内存泄漏say byebye

    一.写在前面 对于C++来说,内存泄漏就是new出来的对象没有delete,俗称野指针:而对于java来说,就是new出来的Object放在Heap上无法被GC回收:而这里就把我之前的一篇内存泄漏的总 ...

  9. SQL Server 数据加密功能解析

    SQL Server 数据加密功能解析 转载自: 腾云阁 https://www.qcloud.com/community/article/194 数据加密是数据库被破解.物理介质被盗.备份被窃取的最 ...

  10. .NetCore中的日志(1)日志组件解析

    .NetCore中的日志(1)日志组件解析 0x00 问题的产生 日志记录功能在开发中很常用,可以记录程序运行的细节,也可以记录用户的行为.在之前开发时我一般都是用自己写的小工具来记录日志,输出目标包 ...

随机推荐

  1. 【Unity笔记】关于UGUI的根节点Canvas

    创建UGUI物体时,会自动创建Canvas物体作为所有UGUI的根节点.该物体身上有个Canvas脚本,Render Mode渲染模式选项: Screen Space - Overlay:即使场景中没 ...

  2. C语言 · 2的次幂表示 · 幂方分解

    蓝桥杯练习场上有两个此类题目: 算法训练 幂方分解   时间限制:1.0s   内存限制:256.0MB        锦囊1 递归. 锦囊2 使用一个函数,递归的进行分解,每次分解的时候要将数字先转 ...

  3. buildroot 使用本地交叉编译器记录

    继上一次glibc bug 事件后,剑锋就说文件系统要用统一的交叉编译器.所以今天就试了一下,便记录一下 在官网下载干净的 buildroot 进入解压后的 buildroot 目录 步骤: make ...

  4. kill -HUP pid 更改配置后不重新启动服务,动态更新配置文件

    kill -HUP pid kill -HUP pid  pid 是进程标识.如果想要更改配置而不需停止并重新启动服务,请使用该命令.在对配置文件作必要的更改后,发出该命令以动态更新服务配置. 根据约 ...

  5. c# dataTable 合并两个列到一个新列中

    public static DataTable UniteDataTableColumns(DataTable dt, String newColumnName, string ColumnName1 ...

  6. ★ java删除代码注释

    package com.witwicky.util; import java.io.BufferedReader; import java.io.BufferedWriter; import java ...

  7. golang 数组以及slice切片

    老虞学GoLang笔记-数组和切片   数组 Arrays 数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值.在初始化后长度是固定的,无法修改其 ...

  8. pick王菊?作为“菊外人”的程序员能做点什么?

    转载:https://mp.weixin.qq.com/s/s1cb9Ij6ouTYYCZovLhXTA 最近,想必大家的朋友圈都被“王菊”占领了,打开朋友圈到处可以见到“pick王菊”.“陶渊明”. ...

  9. 配置QQ企业邮箱小结

    https://exmail.qq.com/login 1,注册管理员账号 2,添加域名 3.设置MX记录 记录类型选:MX记录 主机记录(RR):不填(非万网可以填写@) 记录值与MX优先级分别为: ...

  10. css样式DEMO

    <!-- 导入框 --> <div id="importWin" class="easyui-window" title="服务封装 ...