2010-03-03 45 views
15

我想我会阅读与此问题有关的每一个网页,但我仍然无法找到解决方案,所以我就是这样。在iPhone上使用NSXMLParser解析html实体

我有一个HTML网页,它不在我的控制之下,我需要从我的iPhone应用程序解析它。这是我讲的网页样本:

<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 &egrave; un saluto</A> 
     </SPAN> 
     </DIV> 
    </LI> 
    </BODY> 
</HTML> 

我使用的NSXMLParser,它是顺利,直到它找到&egrave; html实体。它调用foundCharacters:for“Bye Bye”,然后调用resolveExternalEntityName:systemID ::,其中entityName为“egrave”。 在这种方法中,我只是返回在NSData中转换的字符“è”,再次调用foundCharacters,将字符串“è”添加到前一个“Bye Bye”,然后解析器提出NSXMLParserUndeclaredEntityError错误。

我没有DTD,我无法更改我解析的html文件。你对这个问题有什么想法吗?在此先感谢你们所有人, Rob。

更新(12/03/2010)。 GRIFFO的建议后,我结束了这样的事情:

data = [self replaceHtmlEntities:data]; 
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data]; 
[parser setDelegate:self]; 
[parser parse]; 

其中replaceHtmlEntities:(NSData的*)是这样的:

- (NSData *)replaceHtmlEntities:(NSData *)data { 

    NSString *htmlCode = [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding]; 
    NSMutableString *temp = [NSMutableString stringWithString:htmlCode]; 

    [temp replaceOccurrencesOfString:@"&amp;" withString:@"&" options:NSLiteralSearch range:NSMakeRange(0, [temp length])]; 
    [temp replaceOccurrencesOfString:@"&nbsp;" withString:@" " options:NSLiteralSearch range:NSMakeRange(0, [temp length])]; 
    ... 
    [temp replaceOccurrencesOfString:@"&Agrave;" withString:@"À" options:NSLiteralSearch range:NSMakeRange(0, [temp length])]; 

    NSData *finalData = [temp dataUsingEncoding:NSISOLatin1StringEncoding]; 
    return finalData; 

} 

,但我仍然在寻找解决的最佳方式问题。我会在接下来的几天尝试TouchXml,但我仍然认为应该有一种方法可以使用NSXMLParser API来做到这一点,所以如果你知道如何,随时写在这里:

+0

Ps。我知道NSXMLParser是一个XML解析器,而不是一个HTML解析器,但我读到libxml2存在同样的问题。 NSXMLParser似乎比libxml2更容易学习,所以我第一次尝试这个希望它工作。如果没有解决方案,那么我将不得不切换到libxml2 ... – Roberto 2010-03-03 11:50:39

+0

正如下面的Griffo所建议的那样,我用合适的字符替换了文本中的每个html实体,然后使用NSXMLParser对其进行解析。现在它正在工作,但我真的很想知道哪个是解决这类问题的更好方法。 – Roberto 2010-03-04 07:37:49

+0

我与符号字符“&”,至少对于多个​​“foundCharacters”调用,这是痛苦的应对&实体注意到这一点。 – 2018-03-07 21:09:45

回答

9

探索多种选择后,看来的NSXMLParser不会支持比标准的实体&lt;, &gt;, &apos;, &quot; and &amp;

下面的代码失败,从而导致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和E和A的字符被丢弃。

<?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 "è"> 
]> 

在另一个实验中,我创建了一个完全有效的XML文档具有内部DTD

<?xml version="1.0" standalone="yes" ?> 
<!DOCTYPE author [ 
    <!ELEMENT author (#PCDATA)> 
    <!ENTITY js "Jo Smith"> 
]> 
<author>&lt; &js; &gt;</author> 

我实现了parser:foundInternalEntityDeclarationWithName:value:;委托方法,它是清楚的是,解析器然而获取实体数据,仅针对预定义的实体调用parser:foundCharacters

2010-03-20 12:53:59.871 xmlParsing[1012:207] Parser Did Start Document 
2010-03-20 12:53:59.873 xmlParsing[1012:207] Parser foundElementDeclarationWithName: author model: 
2010-03-20 12:53:59.873 xmlParsing[1012:207] Parser foundInternalEntityDeclarationWithName: js value: Jo Smith 
2010-03-20 12:53:59.874 xmlParsing[1012:207] didStartElement: author type: (null) 
2010-03-20 12:53:59.875 xmlParsing[1012:207] parser foundCharacters Before: 
2010-03-20 12:53:59.875 xmlParsing[1012:207] parser foundCharacters After: < 
2010-03-20 12:53:59.876 xmlParsing[1012:207] parser foundCharacters Before: < 
2010-03-20 12:53:59.876 xmlParsing[1012:207] parser foundCharacters After: < 
2010-03-20 12:53:59.877 xmlParsing[1012:207] parser foundCharacters Before: < 
2010-03-20 12:53:59.878 xmlParsing[1012:207] parser foundCharacters After: < 
2010-03-20 12:53:59.879 xmlParsing[1012:207] parser foundCharacters Before: < 
2010-03-20 12:53:59.879 xmlParsing[1012:207] parser foundCharacters After: < > 
2010-03-20 12:53:59.880 xmlParsing[1012:207] didEndElement: author with content: < > 
2010-03-20 12:53:59.880 xmlParsing[1012:207] Parser Did End Document 

我在Using the SAX Interface of LibXML找到了一个教程链接。由NSXMLParser使用的xmlSAXHandler允许定义getEntity回调。在调用getEntity后,实体的扩展被传递给characters回调。

NSXMLParser此处缺少功能。应该发生的是,NSXMLParser或其delegate存储实体定义并将它们提供给xmlSAXHandlergetEntity回调。这显然没有发生。我将提交一份错误报告。

与此同时,如果您的文档很小,执行字符串替换的早期答案是完全可以接受的。查看上面提到的SAX教程以及Apple提供的XMLPerformance示例应用程序,看看是否值得自己实施分析器。

这一直很有趣。

+0

一旦我回家,我会试试这个!谢谢! – Roberto 2010-03-15 07:42:37

+0

:(这没有奏效。它继续养NSXMLParserUndeclaredEntityError = 26 :( 我用你自己的代码。它进入方法resolveExternalEntityName,然后引发异常...... – Roberto 2010-03-15 19:33:48

+0

你能包含的网址?我有另一种理论我想测试 – falconcreek 2010-03-15 20:08:44

0

你可以做一个字符串替换在使用NSXMLParser解析它之前的数据。就我所知,NSXMLParser只有UTF-8。

+0

是的,我只是在想这一点,但我真的不能认为这是一个真正的解决办法 - 因为该方法resolveExternalEntityName:systemID与用于该文件说: “代理人可以解析外部实体(例如,查找和读取外部声明的DTD),并将结果作为NSData对象提供给解析器对象。“ 所以应该存在一种方法,用它来解决实体和解析器翻译吧... 可能是我想的东西在的NSXMLParser的逻辑... – Roberto 2010-03-03 14:08:52

+0

你可以尝试NSXMLDocument – conorgriffin 2010-03-03 14:45:07

+0

但我是读到NSXMLDocument不适用于iphone开发,这是真的吗? – Roberto 2010-03-03 15:16:42

0

我想你会遇到这个例子的另一个问题,因为它不是vaild XML,它是NSXMLParser正在寻找的。

上述问题的确切问题是META,LI,HTML和BODY标签没有关闭,所以解析器看起来一路通过文档的其余部分寻找其结束标签。

解决这个问题的唯一方法是,我知道如果您无权访问更改HTML,请将其与插入的结束标记进行镜像。

+0

对不起......本例中的HTML代码只是文件的第一部分来处理这种情况的正确方法。这是我的错。该文件已正确关闭所有标记。 – Roberto 2010-03-03 14:42:24

0

我会尝试使用不同的解析器,如libxml2 - 理论上我认为应该能够处理不好的HTML。

+1

我读到libxml2有一个HTMLparser,但我找不到关于这个的教程,文档或示例,这就是为什么我第一次尝试NSXMLParser。 – Roberto 2010-03-04 07:36:09

0

自从我刚开始做iOS开发我一直在寻找同样的东西,发现了相关的邮件列表条目:http://www.mail-archive.com/[email protected]/msg17706.html

- (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]; 
} 

这是非常相似的原始解决方案,也使解析器错误NSXMLParserErrorDomain error 26;但在此之后它会继续解析。问题是,当然,这是很难说真正的错误除了;-)

1

,可能为哈克解决方案在与本地修改一个与当地的一个替代所有外部实体的声明取代DTD。

这是我要做的事:

首先,找到并用本地文件替换文档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> 

与此:

<!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> 

```

来自W3C的URL下载DTD,并把它添加到您的应用程序包。您可以找到该文件的路径与下面的代码:

NSBundle* bundle = [NSBundle bundleForClass:[self class]]; 
NSString* path = [[bundle URLForResource:@"xhtml1-transitional" withExtension:@"dtd"] absoluteString]; 

打开DTD文件,发现任何外部实体引用:

<!ENTITY % HTMLlat1 PUBLIC 
    "-//W3C//ENTITIES Latin 1 for XHTML//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent"> 
%HTMLlat1;  

与实体文件的内容替换http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent在上述情况下)

替换所有外部引用后,NSXMLParser应正确处理实体在每次解析XML文件时都不需要下载每个远程DTD /外部实体。