2014-04-23 12 views
5

Ich schreibe einfaches screen scraping-Programm in C#, für die ich alle Eingabe innerhalb eines einzigen Formulars namens "aspnetForm" (es gibt 2 Formulare auf der Seite) ausgewählt werden muss (und ich möchte keine Eingaben von einem anderen), und alle Eingaben in dieser Form werden in verschiedenen Tabellen, Divs oder nur auf der ersten untergeordneten Ebene dieser Form platziert.XPath Auswahl in HTMLAgilityPack funktioniert nicht wie erwartet

So geschrieben ich wirklich einfache XPath-Abfrage:

//form[@id='aspnetForm']//input 

Es Arbeiten wie in allen Browsern zu erwarten, dass ich getestet (Chrome, IE, Firefox) - es gibt, was ich will.

Aber in HTMLAgilityPack funktioniert es überhaupt nicht - SelectNodes geben immer nur NULL zurück.

Diese Abfragen, die ich für Tests geschrieben habe, funktioniert gut, aber gibt nicht zurück, was ich will. Wählen Sie zuerst alle Eingaben ist das erste Childs für meine Form sind, und die zweite gerade Rückkehr Form:

//form[@id='aspnetForm']/input 
//form[@id='aspnetForm'] 

Ja, ich weiß, dass ich nur Knoten von den letzten Abfrage aufzuzählen über, oder einen anderen Select machen auf ihr Ergebnis, aber Ich möchte das nicht wirklich machen. Ich möchte dieselbe Abfrage wie in Browsern verwenden.

Ist XPath derzeit in HTMLAgilityPack beschädigt? Gibt es alternative XPath-Implementierungen für C#?

UPDATE: Testcode:

using HtmlAgilityPack; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 

namespace HtmlAGPTests 
{ 
    [TestClass] 
    public class XPathTests 
    { 
     private const string html = 
       "<form id=\"aspnetForm\">" + 
       "<input name=\"first\" value=\"first\" />" + 
       "<div>" + 
        "<input name=\"second\" value=\"second\" />" + 
       "</div>" + 
       "</form>"; 

     private static HtmlNode GetHtmlDocumentNode() 
     { 
      var document = new HtmlDocument(); 
      document.LoadHtml(html); 
      return document.DocumentNode; 
     } 

     [TestMethod] 
     public void TwoLevelXpathTest()  // fail - nodes is NULL actually. 
     { 
      var query = "//form[@id='aspnetForm']//input"; // what i want 
      var documentNode = GetHtmlDocumentNode(); 

      var inputNodes = documentNode.SelectNodes(query); 

      Assert.IsTrue(inputNodes.Count == 2); 
     } 

     [TestMethod] 
     public void TwoSingleLevelXpathsTest()  // works 
     { 
      var formQuery = "//form[@id='aspnetForm']"; 
      var inputQuery = "//input"; 
      var documentNode = GetHtmlDocumentNode(); 

      var formNode = documentNode.SelectSingleNode(formQuery); 
      var inputNodes = formNode.SelectNodes(inputQuery); 

      Assert.IsTrue(inputNodes.Count == 2); 
     } 

     [TestMethod] 
     public void SingleLevelXpathTest()  // works 
     { 
      var query = "//form[@id='aspnetForm']"; 
      var documentNode = GetHtmlDocumentNode(); 

      var formNode = documentNode.SelectSingleNode(query); 

      Assert.IsNotNull(formNode); 
     } 

    } 
} 
+0

.NET verfügt über eine integrierte Implementierung von XPath, die von HtmlAgilityPack verwendet wird (HAP implementiert keine eigene XPath-Engine). Und tatsächlich funktionierte HAPs XPath für mich gut, also würde ich vorschlagen, zuerst etwas anderes zu vermuten. – har07

+0

Versuchen Sie, das 'HtmlDocument' zu speichern, und prüfen Sie, ob die gespeicherte Datei das erwartete HTML-Format enthält. – har07

+1

@ har07, es enthält - ich habe das vor dem Stellen der Frage getestet. Auch "dreckige Workaround" -Methoden funktionieren, also ist es absolut kein Problem mit der Eingabe. Testcode zu Frage hinzugefügt, so dass Sie es selbst testen können - es funktioniert NICHT wie erwartet. – rufanov

Antwort

4

Das unerwartete Verhalten im Test auftreten, da die HTML <form> Element enthält. Hier bezieht sich die Diskussion:

Ariman. „Ich habe festgestellt, dass nach jeder Knoten Parsen keine untergeordneten Knoten haben alle Knoten, die in der Form sein sollte (, usw.) erstellt werden, wie es ist . Geschwister eher dann Kinder

VikciaR: "In HTML-Tag-Spezifikation Form überlappen können, so Htmlagilitypack ein wenig anders diesen Knoten behandeln ..."

[CodePlex discussion : No child nodes for FORM objects]

Und wie durch VikciaR dort vorgeschlagen, versuchen Sie Ihren Testcode Initialisierung wie folgt zu ändern:

private static HtmlNode GetHtmlDocumentNode() 
{ 
    var document = new HtmlDocument(); 
    document.LoadHtml(html); 

    //execute this line once 
    HtmlNode.ElementsFlags.Remove("form"); 

    return document.DocumentNode; 
} 

Randbemerkung:inputQuery Wert in Testmethode sollte TwoSingleLevelXpathsTest().//input sein. Beachten Sie den Punkt (.) am Anfang, um anzugeben, dass diese Abfrage relativ zum aktuellen Knoten ist. Sonst wird es von der Wurzel aus suchen, die ehemalige formQuery ignorierend (ohne den Punkt, Sie können formQuery zu irgendetwas ändern, solange es Null nicht zurückgibt, das inputQuery wird immer das gleiche Ergebnis zurückgeben).

+2

Little seltsames Verhalten standardmäßig. Aber trotzdem, danke! Es klappt! – rufanov

Verwandte Themen