17

私はこの問題に関連するすべてのWebページを読んだと思いますが、それでも解決策を見つけることができないので、ここにいます。

自分の管理下にないHTMLWebページがあり、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を使用していますが、 èhtmlエンティティが見つかるまでうまくいきます。「ByeBye 」に対してfoundCharacters:を呼び出し、次に「egrave」のentityNameを使用してresolveExternalEntityName:systemID::を呼び出します。このメソッドでは、NSDataで変換された文字「è」を返すだけです。foundCharactersが再度呼び出され、前の文字列「Bye Bye」に文字列「è」が追加され、パーサーがNSXMLParserUndeclaredEntityErrorエラーを発生させます。

DTDがなく、解析しているhtmlファイルを変更できません。この問題について何か考えはありますか?

更新(2010年12月3日)。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を使用してこれを行う方法があるはずだと思います。その方法を知っている場合は、ここに自由に書き込んでください。

4

6 に答える 6

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ドキュメントの前にENTITY宣言を付けてエンティティを宣言しようとすると成功しますが、展開されたエンティティはに戻され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 "è">
]>

別の実験では、内部DTDを使用して完全に有効なxmlドキュメントを作成しました

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

LibXMLのSAXインターフェイスの使用に関するチュートリアルへのリンクを見つけました。xmlSAXHandlerによって使用されるはNSXMLParsergetEntityコールバックを定義できるようにします。を呼び出した後getEntity、エンティティの展開がコールバックに渡されcharactersます。

NSXMLParserここに機能がありません。NSXMLParserまたはそのエンティティ定義をdelegate格納し、それらをxmlSAXHandler getEntityコールバックに提供する必要があります。これは明らかに起こっていません。バグレポートを提出します。

それまでの間、ドキュメントが小さい場合は、文字列の置換を実行するという以前の回答は完全に受け入れられます。上記のSAXチュートリアルとAppleのXMLPerformanceサンプルアプリをチェックして、libxmlパーサーを自分で実装する価値があるかどうかを確認してください。

これは楽しかったです。

于 2010-03-14T22:57:03.673 に答える
2

おそらくハッキーではない解決策は、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 /外部エンティティをダウンロードする必要なしに、エンティティを適切に処理する必要があります。

于 2013-04-17T07:04:24.710 に答える
0

NSXMLParserが探しているのは有効なXMLではないため、この例では別の問題が発生すると思います。

上記の正確な問題は、タグMETA、LI、HTML、およびBODYが閉じられていないため、パーサーがドキュメントの残りの部分で終了タグを探している間、ずっと見えてしまうことです。

HTMLを変更するためのアクセス権がない場合に私が知っている唯一の方法は、終了タグを挿入してHTMLをミラーリングすることです。

于 2010-03-03T14:31:46.007 に答える
0

NSXMLParserでデータを解析する前に、データ内で文字列の置換を行うことができます。NSXMLParserは、私が知る限り、UTF-8です。

于 2010-03-03T13:11:27.697 に答える
0

libxml2のような別のパーサーを使用してみます-理論的には、貧弱なHTMLを処理できるはずだと思います。

于 2010-03-03T21:07:38.723 に答える
0

iOSの開発を始めたばかりなので、同じことを検索して、関連するメーリングリストのエントリを見つけました:http://www.mail-archive.com/cocoa-dev@lists.apple.com/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。しかし、その後も解析を続けます。もちろん、問題は実際のエラーを区別するのが難しいことです;-)

于 2012-05-24T04:39:23.620 に答える