[.NET/XPath] Hulp gevraagd met simple XPath-expressie

Pagina: 1
Acties:

  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
Ik ben in .NET aan het proberen om een webpagina te screen-scrapen, d.w.z. nuttige informatie uit HTML te vissen. Omdat deze HTML een beetje brak is, laat ik deze HTML door een library (HtmlAgilityPack) omzetten naar XML, en vervolgens wil ik dan middels XPath-expressies de juiste data eruit vissen.

Na de conversie ziet de XML er als volgt uit (de echte pagina is veel groter, maar dit stuk is relevant):
XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html>
   <body>
      <i>--Knip--</i>
      <p align="center">
         <b>&lt;&lt;&amp;nbsp;Prev</b>
         &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
         <a href="overzicht.php?page=1">
            <b>Next&amp;nbsp;&gt;&gt;</b>
         </a>
         <br />
         <i>--Knip--</i>
      </p>
      <i>--Knip--</i>
      <table style="border-width:1px;">
         <tr>
           <td><i>Tabeldata</i></td>
         </tr>
      </table>
   </body>
</html>

Nu is het mijn bedoeling om het <table>-element te vinden waar de tabeldata in staat, en die tabel is te herkennen aan het feit dat deze wordt voorafgegaan door "<< prev" en "next >>" (in de volledige XML zijn er veel meer <table>-elementen).

Mijn eerste doel is nu om middels XPath het <p>-element te vinden met de "<< prev" en "next >>" tekst. Wat me wel lukt is om de <b>-elementen te vinden met deze teksten erin; dit doe ik met de volgende XPath-expressie:
code:
1
2
3
//*[text()="<<&nbsp;Prev"]
en
//*[text()="Next&nbsp;>>"]


Deze expressies geven netjes de betreffende <b>-elementen terug. Maar ik krijg het niet voor elkaar om het <p>-element te vinden dat deze <b>-elementen bevat. Mijn eerste gedachte was
code:
1
//p[//*[text()="<<&nbsp;Prev"]]

maar deze geeft alle <p>-elementen terug, niet per se degenen die ergens "<< Prev" bevatten (in het hele document zijn ook meerdere <p>-elementen).

Wie kan mij vertellen wat ik hier verkeerd doe, en hoe het wel zou moeten?

Verwijderd

Heeft er misschien niks mee te maken, maar je zoekt op '&nbsp;' (correcte notatie voor non breaking space), maar in je XML is 't consequent 'nbsp;'...

  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
Ik heb de hele dag (nou ja...) lopen prutsen, maar ik ben eruit gekomen dankzij het tooltje "XPath Expression Tester".
Verwijderd schreef op zondag 21 januari 2007 @ 19:32:
Heeft er misschien niks mee te maken, maar je zoekt op '&nbsp;' (correcte notatie voor non breaking space), maar in je XML is 't consequent 'nbsp;'...
Lol. Als je goed kijkt is het "& amp;nbsp", en dat komt door de conversie van HTML naar XML. Want in HTML is "&nbsp" een geldige entity, maar in XML niet. En dus zal hij de eerste "&" vrolijk converteren naar "& amp;". Maar het is wel verwarrend, dat ben ik met je eens 8)7

Overigens ben ik tegen het tooltje XPath Expression Tester aangelopen, waarmee je via een GUI direct feedback krijgt op je XPath-expressies. Ik had al een vergelijkbaar tooltje, maar die was geschreven in Java en maakte dus geen gebruik van de XPath-implementatie zoals .NET die heeft. En alhoewel dat in theorie niets uit zou mogen maken... :P

Deze XPath Expression Tester heeft nog wel een bugje (als je attributen wil selecteren krijg je een prachtige assertion error), maar voor de rest werkt het heel goed om te zien waar je nu eigenlijk mee bezig bent.

  • tech-no-logical
  • Registratie: December 2000
  • Laatst online: 01-12 14:04
code:
1
//p[//*[text()="<<&nbsp;Prev"]]

maar deze geeft alle <p>-elementen terug, niet per se degenen die ergens "<< Prev" bevatten (in het hele document zijn ook meerdere <p>-elementen).
je bent dichtbij. dit zou moeten werken :
code:
1
//p[./*[text()="<<&nbsp;Prev"]]


de search tussen [] is door gebruik van ./ ipv // relatief aan de geldende context. in woorden is deze xpath dus 'geef me alle p-tags die als kind een willeukeurige tag hebben met als inhoud "<< Prev"'

jouw query is meer iets als 'geef me alle p-tags onder voorwaarde dat er in het hele document ergens tags met als inhoud "<< Prev" zijn'.

overigens is dit nog steeds niet ideaal, als je geneste p-tags hebt met op een diep niveau "<< Prev" voldoen _al_ deze p-tags aan de expressie. netter is waarschijnlijk iets als:

code:
1
//*[text()="<<&nbsp;Prev"]/ancestor::p[1]


in woorden : geef me van elke tag met inhoud "<< Prev" de eerste p-tag die een voorouder is. garantie tot de deur, alle expressies zijn ongetest....

voor meer info : de references/tutorials op http://www.zvon.org zijn altijd mijn eerste stop.

edit:
spelvoutje weggewerkt

  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
tech-no-logical, bedankt voor je reactie.

Ik had zelf ook al zo'n vermoeden dat ik iets verkeerds aan het doen was met de context node. Ik had zelf inmiddels al wel een werkende oplossing gevonden ("//p//*[text()='<< Prev']/parent::p"), maar deze werkt alleen omdat het <p>-element een parent van <b> is - jouw constructie is beter. Enne, ik heb 'm getest en hij werkt hoor ;)

Ook bedankt voor de link btw, die gaat in m'n bookmarks.