Html Agility Pack - lettura div InnerText in tabella

c# html-agility-pack web-scraping

Domanda

Il mio problema è che non riesco a ottenere div InnerText dalla tabella. Ho estratto con successo diversi tipi di dati, ma non so come leggere div dalla tabella.

Nella seguente immagine ho evidenziato div, e ho bisogno di ottenere InnerText da esso, in questo caso - numero 3.

Clicca qui per la prima immagine

Sto cercando di farlo usando il seguente percorso:

"//div[@class='kal']//table//tr[2]/td[1]/div[@class='cipars']"

Ma sto seguendo l'errore:

Clicca qui per l'immagine del messaggio di errore

Supponendo che il resto del codice sia scritto correttamente, qualcuno potrebbe indicarmi la giusta direzione? Ho cercato di capire questo, ma non riesco a ottenere alcun risultato.

Risposta accettata

Quindi il tuo problema è che ti stai affidando a posizioni all'interno del tuo XPath. Mentre questo può essere OK in alcuni casi, non è qui, perché ti stai aspettando il primo td in un dato tr per avere un div con la classe.

Guardando la fonte in Chrome, mostra che questo non è sempre il caso. Puoi vedere questo confrontando l'elemento "1" nel calendario, a "2" e "3". Noterai che l'elemento "1" ha un numero di elementi attorno ad esso, che gli altri non hanno.

La tua query XPath originale non restituisce un elemento, questo è il motivo per cui stai ricevendo l'errore. Nel caso in cui la query XPath fornita a HtmlAgilityPack non abbia come risultato un elemento DOM, restituirà null.

Ora, poiché non hai mostrato l'intero codice, non so come viene eseguito questo codice. Tuttavia, suppongo che tu stia cercando di scorrere tutti gli elementi del calendario. Indipendentemente da ciò, hai diversi modi per farlo, ma ti mostrerò che con il selettore XPath descendant puoi semplicemente catturare tutto in una volta sola:

//div[@class='kal']//table//descendant::div[@class='cipars']

Ciò restituirà tutti gli elementi del calendario (ovvero da 1 a 30).

Tuttavia, per ottenere tutti gli elementi in una particolare riga, puoi semplicemente inserire quel tr nella query:

//div[@class='kal']//table//descendant::div[@class='cipars']

Ciò restituirebbe da 2 a 8 (la seconda riga di elementi del calendario).

Per mirare a uno specifico, beh, dovrai fare un'ipotesi sul codice sorgente del sito web. Sembra che ogni "cipars" div ha un antenato di un td con una classe datums .... quindi per ottenere il "3" valore dalla tua domanda:

//div[@class='kal']//table//descendant::div[@class='cipars']

Speriamo che questo sia sufficiente per mostrare almeno il problema.

modificare

Anche se hai un problema XPath, hai anche un altro problema.

Il sito è stato creato in modo molto strano. Il calendario è caricato in un modo strano. Quando raggiungo quell'URL, il calendario viene creato da alcuni Javascript che chiamano un servizio Web XML (scritto in PHP) che calcola quindi la table completa da utilizzare per il calendario.

A causa del fatto che questo è Javascript (codice lato client), HtmlAgilityPack non lo eseguirà. Pertanto, HtmlAgilityPack non "vede" la tabella. Quindi le domande a suo carico tornano come "non trovate" (null).

Modi intorno a questo: 1) Utilizzare uno strumento che chiamerà gli script. Con questo, intendo caricare un browser. Un ottimo strumento da usare per questo è chiamato Selenium . Questa sarà probabilmente la soluzione globale migliore perché significa che tutti gli script utilizzati dal sito verranno effettivamente chiamati. Puoi ancora utilizzare XPath con esso, quindi le tue query non cambieranno.

Il secondo modo è di inviare una richiesta allo stesso servizio web che la pagina fa. Questo è quello di ottenere fondamentalmente tornare lo stesso codice HTML che la pagina è sempre, e l'utilizzo di quello con HtmlAgilityPack. Come lo facciamo?

Bene, puoi facilmente inviare i dati POST a un servizio web usando C #. Solo per facilità d'uso ho rubato il codice da questa domanda SO . Con questo, possiamo inviare la stessa richiesta della pagina e ottenere lo stesso codice HTML.

Quindi, per inviare alcuni dati POST, generiamo un metodo come questo .....

//div[@class='kal']//table//descendant::div[@class='cipars']

Possiamo chiamarlo così:

//div[@class='kal']//table//descendant::div[@class='cipars']

Come ho avuto questo? Bene, il file php che stiamo chiamando è il servizio web della pagina e anche i dati POST. Il modo in cui ho scoperto i dati che invia al servizio consiste nel debugging di Javascript (utilizzando la console per gli sviluppatori di Chrome), ma potresti notare che è praticamente la stessa cosa presente nell'URL. Questo sembra essere intenzionale.

Il responseBody restituito è l' HTML fisico della sola table per il calendario.

Cosa ne facciamo adesso? Lo cariciamo in HtmlAgilityPack, perché è in grado di accettare puro HTML.

//div[@class='kal']//table//descendant::div[@class='cipars']

Ora, manteniamo l'XPath originale in:

//div[@class='kal']//table//descendant::div[@class='cipars']

Ora, stampiamo quello che dovrebbe essere "3":

//div[@class='kal']//table//descendant::div[@class='cipars']

Il mio output, eseguendolo localmente, è davvero: 3 .

Tuttavia, anche se questo ti farebbe superare il problema che stai avendo, suppongo che il resto del sito sia come questo. Se questo è il caso, potresti comunque riuscire a aggirarlo usando la tecnica sopra, ma strumenti come Selenium sono stati creati proprio per questo motivo.




Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché