2014-04-23 21 views
5

Sto scrivendo un semplice programma di screen scraping in C#, per cui ho bisogno di selezionare tutti gli input inseriti all'interno di un singolo modulo chiamato "aspnetForm" (nella pagina c'è 2 moduli e non voglio input da un altro) e tutti gli input in questo modulo sono posizionati all'interno di tabelle, div o solo al primo livello figlio di questo modulo.XPath select in HTMLAgilityPack non funziona come previsto

Così ho scritto molto semplice query XPath:

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

opere è tutto come previsto in tutti i browser che ho provato (Chrome, IE, Firefox) - restituisce quello che voglio.

Ma in HTMLAgilityPack non funziona affatto - SelectNodes restituisce sempre solo NULL.

Questa query che ho scritto per i test funziona bene, ma non restituisce quello che voglio. Per prima cosa selezionare tutti gli input di che sono di prima bambino per la mia forma, e forma la seconda appena di ritorno:

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

sì, lo so che io posso solo enumerare sopra i nodi da ultima query, o fare un altro SelectNodes su di essa la conseguenza, ma non voglio davvero farlo Voglio utilizzare la stessa query dei browser.

XPath è attualmente danneggiato in HTMLAgilityPack? Esistono implementazioni XPath alternative per C#?

UPDATE: Codice di prova:

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 ha implementato l'implementazione di XPath che viene utilizzata da HtmlAgilityPack (HAP non implementa il proprio motore XPath). E in effetti l'XPath di HAP funzionava bene per me, quindi suggerirei di sospettare qualcos'altro prima. – har07

+0

Provare a salvare il 'HtmlDocument', quindi controllare se il file salvato contiene il formato HTML previsto. – har07

+1

@ har07, contiene - l'ho provato prima di inviare una domanda. Anche i metodi "dirty workaround" funzionano, quindi non è assolutamente un problema con l'input. Aggiunto il codice di test alla domanda, in modo che tu possa testarlo da solo - NON funziona come previsto. – rufanov

risposta

4

Il comportamento imprevisto nel test verificarsi perché il codice HTML contiene <form> elemento. Qui la discussione è legato:

Ariman:. "Ho scoperto che dopo l'analisi qualsiasi nodo non ha nodi figli vengono creati tutti i nodi che dovrebbero essere all'interno del modulo (,, etc.) in quanto è . fratelli piuttosto che i bambini

VikciaR: "In Html tag form specifica può sovrapporsi, in modo da gestire HtmlAgilityPack questo nodo un po 'diverso ..."

[CodePlex discussion : No child nodes for FORM objects]

E come suggerito da VikciaR lì, cercare di modificare il codice di inizializzazione test come questo: nota

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

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

    return document.DocumentNode; 
} 

laterale:inputQuery valore nel metodo di prova dovrebbe essere TwoSingleLevelXpathsTest().//input. Notare il punto (.) all'inizio per indicare che questa query è relativa al nodo corrente. Altrimenti cercherà dalla radice, ignorando l'ex formQuery (senza il punto, è possibile modificare formQuery fino a quando non restituisce null, lo inputQuery restituirà sempre lo stesso risultato).

+2

Poco strano comportamento di default .. Ma comunque, grazie! Funziona! – rufanov