XPath – co, proč a hlavně jak?

Potom, co jsem na nedávné Poslední Sobotě mluvilMatcheru, se zájem o tento nástroj znatelně zvýšil. Lidé začali posílat pull-requesty, bugreporty a maily s otázkami jak se dá udělat to či ono. Na všechny jsem rád odpověděl, protože dotazy byly buď triviální a tudíž jednoduché na vytřešení nebo zajímavé oříšky, které musely být vyřešeny. Jedno měly však společné: V naprosté většině řešení nemělo nic společného s Matcherem, ale ve správném použití XPath, na němž je nástroj založený. Právě z toho důvodu jsem napsal následující letmý úvod do XPath, který ukáže vlastnosti nejčastěji používané při extrakci dat z HTML.

XPath plní stejný účel jako CSS selektory: Z HTML stromu vybere uzly, které mě zajímají a se kterými bych chtěl dál pracovat – ostylovat v případě CSS nebo z nich extrahovat data v případě Matcheru. Ve srovnání se současnou verzí CSS selektorů je však XPath expresivnější a místy má odlišnou sémantiku.

Všechno bude nejlépe patrné z ukázek.

/html – Vybere element html, který je kořenovým elementem stromu.

/html/body – Odpovídá všem elementům body, které jsou přímými potomky kořenového elementu html.

//h2 – Všechny elementy h2, které se v dokumentu vyskytují, včetně vlastních podstromů. Pokud jeden h2 kdekoli v sobě obsahuje jiný element h2, výsledkem budou všechny tyto elementy.

//div/span//a – Selektory je možno libovolně řetězit a tento najde všechny a jako potomky span, které jsou přímými potomky libovolného divu.

./div nebo div – Vybere všechny elementy div které jsou přímými potomky právě aktivního elementu. Jde o relativní polohu a ne o polohu fixovanou ke kořenu dokumentu.

.//div – To samé jako předchozí výraz, ale připouští i nepřímé potomky.

div[@class="asdf"] – div, jehož atribut class je „asdf“. XPath nepodporuje CSS třídy a k obsahu atributů přistupuje jako ke stringům a proto, tento výraz není stejný jako css selektor div.asdf. XPath podmínce @class="asdf" vyhoví jen ty elementy jejichž atribut class je jen a pouze string „asdf“. Na toto je si třeba dávat pozor, protože jde o největší odchylku od chování, které by čekal člověk odkojený CSS selektory.

div[@class != "asdf"] nebo div[not(@class = "asdf")] – Odpovídá divům jejichž třída není „asdf“.

div[contains(@class, "asdf")] – divy jejichž atribut class obsahuje string „asdf“. Například: Třída „xasdfy“ také vyhovuje tomuto predikátu.

div[starts-with(@class, "asdf")] – divy jejichž atribut class začíná na „asdf“.

div[contains(concat(" ", normalize-space(@class), " "), " asdf ")] – Tento výraz má stejný význam jako CSS selektor div.asdf (tedy kromě toho, že CSS nepřihlíží v velikosti písmen a XPath ano). Naštěstí takové konstrukce nejsou skoro nikdy potřeba, protože většinou stačí jednoduché @class="asdf" nebo funkce contains().

div[@class] – divy, které mají nějaký atribut class

div[span] – divy, které mají jako přímého potomka span

div[.//span] – divy, které obsahují span

div[span[@class]] – divy, které mají přímého potomka span, který má nastavenou libovolnou třídu

div[@class and span] – divy, které mají nastaven atribut class a zároveň obsahují span jako potomka. Stejně jako and se dá použít i logické or nebo funkce not().

div[1] – První div v pořadí v jakém se vyskytuje v dokumentu.

div[last()] – Poslední div.

div[position() mod 2 = 0] – Každý sudý div.

Na pořadí predikátů v hranatých závorkách záleží:

div[@class="asdf"][1] – Vybere všechny divy s třídou „asdf“ a z nich pak vybere ten první. Pokud existují nějaké divy s touto třídou, výsledkem bude vždycky první z nich.

div[1][@class="asdf"] – Vybere první div a ten ve výsledné množině ponechá jen pokud má třídu „asdf“. Když existují divy s touto třídou, ale první ji nemá, výsledkem je prázdná množina.

div/text()[1] – Z divu vybere první kus textu, který není obsažen v žádném jiném elementu.

preceding-sibling::h2 – Vybere elementy h2, které předcházejí aktivnímu elementu a zároveň jsou jeho sourozenci (tj. jsou přímými potomky stejného elementu jako aktivní element).

a[@class="active"]/following-sibling::a[1] – Vybere první odkaz, který následuje (a je sourozenec) po odkazu s třídou „active“.


Dále k tématu:

PHP DOM, SimpleXML a Matcher

Flattr this!

This entry was posted in PHP. Bookmark the permalink.

5 Responses to XPath – co, proč a hlavně jak?

  1. paranoiq says:

    na XPath používám preprocesor, který některé složité konstrukce zjednodušuje a zavádí spoustu zkratek. i z toho jde pochopit, jak některé dotazy napsat – viz: https://github.com/…ryEngine.php#L27

  2. mrtnzlml says:

    V chrome develop režimu lze po kliknutí na uzel v HTML pravým myšítkem získat přímo XPath na daný prvek. Může se hodit, když člověk neví… (-:

  3. Milo says:

    Nejlepší česky psaný zdroj o XPath je kniha „PHP a XML“ od Jiřího Koska. Perfektně vysvětlené identifikátory os a co vlastně zkrácené zápisy bez osy znamenají. Po přečtění se pak výrazy XPath sekají jedna radost :)

  4. peter tutor says:

    na starsom projekte sme potrebovali nieco rozsirit a konkretne nejake veci boli urobene cez XPath.
    Tak som to posunul jednemu novsiemu clenovi v teame, ze nech sa to nauci, ze to bude potrebne. Na dalsi den mi to prisiel vratit, ze on blbosti robit nebude a ani sa to nebude ucit. Ze na to musime mat nejaku sci-fi kniznicu a hned mi aj X rieseni z rukava dal, co stacil na webe nastudovat za par minut.
    Nedali mi a pozrel som si jeho CV a mal to tam! Ze ovlada super XPath! Na pohovore vyzeral akcny, ze ma rad vyzvy a ze sa chce ucit nove veci.
    Nutit som ho nechcel do toho, to by iba robil nieco proti srsti a tak som ho musel z teamu vylucit. Nechapal. Mam pocit, ze v inom teame tiez nepochodil a musel odist.

  5. Jakub Vrána says:

    Vždycky, když jsem s XPath něco dělal, tak jsem si otevřel http://www.kosek.cz/…/vyrazy.html

Leave a Reply

Your email address will not be published. Required fields are marked *