XPath handleiding

In deze zelfstudie krijgt u een voorzichtige inleiding tot XPath <https://en.wikipedia.org/wiki/XPath> _, een querytaal die kan worden gebruikt om willekeurige delen van `HTML <https://en.wikipedia.org/wiki/HTML>`_documenten in calibre. XPath is een veel gebruikte standaard, en googelen levert een hoop informatie op. Deze zelfstudie richt zich echter op het gebruik van XPath voor aan e-boeken gerelateerde taken, zoals het vinden van hoofdstukken in een ongestructureerd HTML-document.

Selecteren op naam van label

De eenvoudigste vorm van selectie is om labels op naam te selecteren. Bijvoorbeeld, stel dat u wilt alle <h2>-tags in een document wilt selecteren. De XPath query voor dit is:

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

Het voorvoegsel // betekent zoeken op elk niveau van het document. Stel nu dat u wilt zoeken naar <span> -tags die zich binnen de ``<a>``tags bevinden. Dat kan worden bereikt met:

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

Als u naar labels wilt zoeken in een bepaald gebied van het document, verander dan de prefix:

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

Dit komt overeen met <p>een zeer kort e-book om het gebruik van XPath.</p> in de voorbeeld_ebook aan te tonen, maar niet een van de andere <p>-tags. Het h: voorvoegsel in de bovenstaande voorbeelden is nodig om overeen te komen met XHTML-tags. Dit komt omdat intern, calibre alle inhoud als XHTML vertegenwoordigt. In XHTML-tags hebben een naamruimte, en h: is de naamruimte prefix voor HTML-tags.

Now suppose you want to select both <h1> and <h2> tags. To do that, we need a XPath construct called predicate. A predicate is simply a test that is used to select tags. Tests can be arbitrarily powerful and as this tutorial progresses, you will see more powerful examples. A predicate is created by enclosing the test expression in square brackets:

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

There are several new features in this XPath expression. The first is the use of the wildcard *. It means match any tag. Now look at the test expression name()='h1' or name()='h2'. name() is an example of a built-in function. It simply evaluates to the name of the tag. So by using it, we can select tags whose names are either h1 or h2. Note that the name() function ignores namespaces so that there is no need for the h: prefix. XPath has several useful built-in functions. A few more will be introduced in this tutorial.

Selecteren op eigenschap

To select tags based on their attributes, the use of predicates is required:

//*[@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")

Here, the @ operator refers to the attributes of the tag. You can use some of the XPath built-in functions to perform more sophisticated matching on attribute values.

Selecteren op label inhoud

Using XPath, you can even select tags based on the text they contain. The best way to do this is to use the power of regular expressions via the built-in function re:test():

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

Here the . operator refers to the contents of the tag, just as the @ operator referred to its attributes.

Sample 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>

XPath ingebouwde functies

naam()
De naam van het huidige label.
bevat()
contains(s1, s2) returns true if s1 contains 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