Samouczek XPath

W tym samouczku otrzymasz delikatne wprowadzenie do XPath, języka zapytań, którego można użyć do wybrania dowolnych części HTML dokumenty w calibre. XPath jest powszechnie używanym standardem, a wyszukiwanie go w Google przyniesie mnóstwo informacji. Ten samouczek koncentruje się jednak na używaniu XPath do zadań związanych z e-bookami, takich jak znajdowanie nagłówków rozdziałów w nieustrukturyzowanym dokumencie HTML.

Wybieranie według nazwy tagu

Najprostszą formą wyboru jest wyszukanie znacznika wg nazwy. Na przykład przypuśćmy, że chcesz odnaleźć wszystkie znaczniki <h2> w dokumencie. Zapytanie jest proste:

//h:h2        (Selects all <h2> tags)

Przedrostek // oznacza wyszukaj na każdym poziomie dokumentu. A teraz przypuśćmy, że chcesz wybrać tylko te znaczniki <span>, które zawierają znacznik <a>. Można to zrobić tak:

//h:a/h:span    (Selects <span> tags inside <a> tags)

Jeśli chcesz wyszukać znaczniki na określonym poziomie dokumentu, zmień przedrostek:

/h:body/h:div/h:p (Selects <p> tags that are children of <div> tags that are
             children of the <body> tag)

This will match only <p>A very short e-book to demonstrate the use of XPath.</p> in the Przykładowy e-book but not any of the other <p> tags. The h: prefix in the above examples is needed to match XHTML tags. This is because internally, calibre represents all content as XHTML. In XHTML tags have a namespace, and h: is the namespace prefix for HTML tags.

Teraz przypuśćmy, że chcesz wybrać znaczniki <h1> i <h2>. Do tego potrzebna jest konstrukcja XPath nazywana predykatem. predicate jest po prostu testem, używanym do wybierania znaczników. Test te mają ogromne możliwości i w dalszej części samouczka zobaczysz co można dzięki nim uzyskać. Predykat jest tworzony poprzez umieszczenie wyrażenia testowego w nawiasach kwadratowych:

//*[name()='h1' or name()='h2']

Jest kilka nowych elementów w tym wyrażeniu XPath. Pierwszym jest użycie symbolu *. Oznacza on dopasuj dowolny znacznik. Teraz popatrzmy na wyrażenie name()='h1' or name()='h2'. name() jest przykładem wbudowanej funkcji. Zwraca ona po prostu nazwę znacznika. Używając jej możemy więc wybrać znaczniki, których nazwa to h1 lub h2. Funkcja name() nie uwzględnia przestrzeni nazw, nie ma więc konieczności użycia przedrostka h:. XPath ma wbudowanych wiele przydatnych funkcji. Kilka z nich poznasz w tym samouczku.

Wybieranie wg atrybutów

Aby wybrać znaczniki wg ich atrybutów wymagane jest użycie predykatów:

//*[@style]              (Select all tags that have a style attribute)
//*[@class="chapter"]    (Select all tags that have class="chapter")
//h:h1[@class="bookTitle"] (Select all h1 tags that have class="bookTitle")

W tym przypadku operator @ odnosi się do atrybutów znacznika. Można ponadto użyć niektórych funkcji wbudowanych XPath, aby dokładniej dopasować wartości atrybutów.

Wybieranie po zawartości znacznika

Przy użyciu XPath możesz nawet wybrać znaczniki, bazując na tekście, który zawierają. Najlepszym na to sposobem jest użycie wyrażeń regularnych poprzez wbudowaną funkcję „term:re:test():

//h:h2[re:test(., 'chapter|section', 'i')] (Selects <h2> tags that contain the words chapter or
                                          section)

W tym przypadku operator . odnosi się do zawartości znacznika, tak jak operator @ odnosił się do atrybutów.

Przykładowy e-book

<html>
    <head>
        <title>A very short e-book</title>
        <meta name="charset" value="utf-8" />
    </head>
    <body>
        <h1 class="bookTitle">A very short e-book</h1>
        <p style="text-align:right">Written by Kovid Goyal</p>
        <div class="introduction">
            <p>A very short e-book to demonstrate the use of XPath.</p>
        </div>

        <h2 class="chapter">Chapter One</h2>
        <p>This is a truly fascinating chapter.</p>

        <h2 class="chapter">Chapter Two</h2>
        <p>A worthy continuation of a fine tradition.</p>
    </body>
</html>

Wbudowane funkcje XPath

name()

Nazwa aktualnie wybranego znacznika

contains()

contains(s1, s2) zwraca true jeśli s1 zawiera s2.

re:test()

re:test(src, pattern, flags) returns true if the string src matches the regular expression pattern. A particularly useful flag is i, it makes matching case insensitive. A good primer on the syntax for regular expressions can be found at regexp syntax