在iPhone上使用NSXMLParser解析html实体
我想我读了每一个关于这个问题的网页,但我仍然找不到解决办法,所以我来了 我有一个HTML网页,它不在我的控制之下,我需要从我的iPhone应用程序中解析它。以下是我正在谈论的网页示例:在iPhone上使用NSXMLParser解析html实体,iphone,parsing,nsxmlparser,html-entities,Iphone,Parsing,Nsxmlparser,Html Entities,我想我读了每一个关于这个问题的网页,但我仍然找不到解决办法,所以我来了 我有一个HTML网页,它不在我的控制之下,我需要从我的iPhone应用程序中解析它。以下是我正在谈论的网页示例: <HTML> <HEAD> <META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> </HEAD> <BODY
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
</HEAD>
<BODY>
<LI class="bye bye" rel="hello 1">
<H5 class="onlytext">
<A name="morning_part">morning</A>
</H5>
<DIV class="mydiv">
<SPAN class="myclass">something about you</SPAN>
<SPAN class="anotherclass">
<A href="http://www.google.it">Bye Bye è un saluto</A>
</SPAN>
</DIV>
</LI>
</BODY>
</HTML>
其中replaceThMLentities:(NSData*)是这样的:
data = [self replaceHtmlEntities:data];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
[parser setDelegate:self];
[parser parse];
- (NSData *)replaceHtmlEntities:(NSData *)data {
NSString *htmlCode = [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding];
NSMutableString *temp = [NSMutableString stringWithString:htmlCode];
[temp replaceOccurrencesOfString:@"&" withString:@"&" options:NSLiteralSearch range:NSMakeRange(0, [temp length])];
[temp replaceOccurrencesOfString:@" " withString:@" " options:NSLiteralSearch range:NSMakeRange(0, [temp length])];
...
[temp replaceOccurrencesOfString:@"À" withString:@"À" options:NSLiteralSearch range:NSMakeRange(0, [temp length])];
NSData *finalData = [temp dataUsingEncoding:NSISOLatin1StringEncoding];
return finalData;
}
但我仍然在寻找解决这个问题的最佳方法。在接下来的几天里,我将尝试使用TouchXml,但我仍然认为应该有一种使用NSXMLParser API来实现这一点的方法,因此,如果您知道如何实现,请随意在这里编写。在使用NSXMLParser解析数据之前,您可以在数据中进行字符串替换。据我所知,NSXMLParser是UTF-8。我认为您将在这个示例中遇到另一个问题,因为它不是NSXMLParser所寻找的有效XML 上面提到的确切问题是,标记META、LI、HTML和BODY没有关闭,因此解析器会在文档的其余部分查找关闭标记
如果您没有权限更改HTML,我所知道的唯一解决方法就是使用插入的结束标记对其进行镜像。我会尝试使用不同的解析器,如libxml2-理论上我认为应该能够处理糟糕的HTML。在探索了几种替代方法之后,NSXMLParser似乎不支持标准实体
,&apos;以外的实体;,“和&;
下面的代码失败,导致出现NSXMLParserUndeclaredEntityError
// Create a dictionary to hold the entities and NSString equivalents
// A complete list of entities and unicode values is described in the HTML DTD
// which is available for download http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent
NSDictionary *entityMap = [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithFormat:@"%C", 0x00E8], @"egrave",
[NSString stringWithFormat:@"%C", 0x00E0], @"agrave",
...
,nil];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
[parser setDelegate:self];
[parser setShouldResolveExternalEntities:YES];
[parser parse];
// NSXMLParser delegate method
- (NSData *)parser:(NSXMLParser *)parser resolveExternalEntityName:(NSString *)entityName systemID:(NSString *)systemID {
return [[entityMap objectForKey:entityName] dataUsingEncoding: NSUTF8StringEncoding];
}
通过在HTML文档前面添加实体声明来声明实体的尝试将通过,但是扩展的实体不会传递回parser:foundCharacters
,并且会删除è和è字符
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
[
<!ENTITY agrave "à">
<!ENTITY egrave "è">
]>
我找到了关于的教程的链接。NSXMLParser
使用的xmlSAXHandler
允许定义getEntity
回调。调用getEntity
后,实体的扩展将传递给字符
回调
NSXMLParser
在这里缺少功能。应该发生的是NSXMLParser
或其委托
存储实体定义并将其提供给xmlSAXHandler
getEntity回调。这显然没有发生。我将提交错误报告
同时,如果您的文档很小,那么执行字符串替换的早期答案是完全可以接受的。请查看上面提到的SAX教程以及Apple的XMLPerformance示例应用程序,看看自己实现libxml
解析器是否值得
这很有趣。因为我刚开始做iOS开发,所以我一直在搜索同样的东西,并找到了一个相关的邮件列表条目:
这与您最初的解决方案非常相似,也会导致解析器错误NSXMLParserErrorDomain error 26;但之后它会继续解析。当然,问题是更难区分真正的错误;-)一个可能的更少的黑客解决方案是用本地修改的DTD替换DTD,并使用ll外部实体声明替换为本地实体声明 我就是这样做的: 首先,查找文档DTD声明并将其替换为本地文件。例如,替换为:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html><body><a href='a.html'>hi!</a><br><p>Hello</p></body></html>
打开DTD文件,查找任何外部实体引用:
<!ENTITY % HTMLlat1 PUBLIC
"-//W3C//ENTITIES Latin 1 for XHTML//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent">
%HTMLlat1;
%HTMLAT1;
将其替换为实体文件的内容(在上述情况下)
替换所有外部引用后,NSXMLParser应该正确处理实体,而无需在每次解析XML文件时下载每个远程DTD/外部实体。Ps。我知道NSXMLParser是XML解析器,而不是HTML解析器,但我了解到libxml2也存在同样的问题。NSXMLParser似乎更容易处理比libxml2强,所以我首先尝试了这个,希望它能工作。如果没有解决方案,那么我将不得不切换到libxml2…正如下面Griffo所建议的,我用适当的字符替换了文本中的每个html实体,然后用NSXMLParser对其进行解析。现在它可以工作了,但我真的想知道哪一个更好呃,这是解决这类问题的一种方法。我注意到,对于符号和字符“&”的&;实体,至少对于多个“foundcharacter”“电话,处理起来很痛苦。是的,我只是在想这件事,但我不能真的认为这是一个真正的解决办法。。。因为有一个方法resolveExternalEntityName:systemID,文档中说:“委托可以解析外部实体(例如,定位和读取外部声明的DTD),并将结果作为NSData对象提供给解析器对象。”因此,它应该存在一种使用它来解析实体并将其转换为解析器的方法。。。可能我在NSXMLParser的逻辑中遗漏了一些东西……但我读到NSXMLDocument不可用于iphone开发,是真的吗?NSXMLDocument在TouchXML中可用。看这里:谢谢,我一定会试试的。但我不能停止思考,什么是正确的方式来处理这个案件只使用sdk代码…对不起。。。示例中的html代码只是文件的第一部分。那是我的错。该文件的每个标记都已正确关闭。我了解到libxml2有一个HTMLparser,但我找不到关于这个的教程、文档或示例,这就是我第一次尝试NSXMLParser的原因。:(这不起作用。它继续引发NSXMLParserUndeclaredEntityError=26.)(我使用了您自己的代码。它输入了resolveExternalEntityName方法,然后引发异常…您可以包含url吗?我还有另一个理论要测试。仍在寻找解决方案。找到了一个可能的答案,但它无法帮助我们。)
- (NSData *)parser:(NSXMLParser *)parser resolveExternalEntityName: (NSString *)entityName systemID:(NSString *)systemID {
NSAttributedString *entityString = [[[NSAttributedString alloc] initWithHTML:[[NSString stringWithFormat:@"&%@;", entityName] dataUsingEncoding:NSUTF8StringEncoding] documentAttributes:NULL] autorelease];
NSLog(@"resolved entity name: %@", [entityString string]);
return [[entityString string] dataUsingEncoding:NSUTF8StringEncoding];
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html><body><a href='a.html'>hi!</a><br><p>Hello</p></body></html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "file://localhost/Users/siuying/Library/Application%20Support/iPhone%20Simulator/6.1/Applications/17065C0F-6754-4AD0-A1EA-9373F6476F8F/App.app/xhtml1-transitional.dtd">
<html><body><a href='a.html'>hi!</a><br><p>Hello</p></body></html>
NSBundle* bundle = [NSBundle bundleForClass:[self class]];
NSString* path = [[bundle URLForResource:@"xhtml1-transitional" withExtension:@"dtd"] absoluteString];
<!ENTITY % HTMLlat1 PUBLIC
"-//W3C//ENTITIES Latin 1 for XHTML//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent">
%HTMLlat1;