Blog of :/blog/weboob/Browser2_:_La_pagination.html

Browser2 : La pagination

Cet article fait partie d'une série sur le Browser2.

Beaucoup de méthodes de modules weboob retournent des listes d'éléments itérés sur des pages paginées. C'est le cas des résultats de recherches notamment.

Voici l'exemple typique de code avec l'ancien browser permettant de gérer cette pagination :

class ResultsPage(BasePage):
    def iter_results(self):
        # ...

    def get_next_url(self):
        link = self.document.xpath('//a[text()="Next »"]')

        if not link:
            return None

        return link[0].attrib["href"]

class Browser(BaseBrowser):
    # ...
    def search_pattern(self, pattern):
        self.location('/search/%s' % urllib.quote(pattern))

        while True:
            assert self.is_on_page(ResultsPage)

            for video in self.page.iter_results():
                yield video

            next_url = self.page.get_next_url()
            if next_url is None:
                return

            self.location(next_url)

Plus verbeux, on ne fait pas.

Le Browser2 introduit un mécanisme qui permet de gérer beaucoup plus simplement la pagination, dont le fonctionnement interne est simple :

  • La méthode de la page, une fois qu'elle a fini d'envoyer les éléments de la page, doit envoyer une exception NextPage avec le lien ou la requête à exécuter ;
  • Un mécanisme au dessus de la page se charge de capturer cette exception, joue la requête, et appelle de nouveau la méthode sur la nouvelle page.

Bien camouflé dans ListElement et le décorateur pagination, cela donne :

class ResultsPage(HTMLPage):
    @pagination
    @method
    class iter_results(ListElement):
        item_xpath = '//span[@id="miniatura"]'

        next_page = Link(u'//a[text()="Next »"]')

        class item(ItemElement):
            # ...

class Browser(PagesBrowser):
    # ...
    def search_pattern(self, pattern):
        self.search.go(pattern=pattern)
        assert self.search.is_here(pattern=pattern)

        return self.page.iter_results()

Définir l'attribut next_page fait que si le lien est présent, ListElement lance l'exception NextPage. Ensuite, le décorateur pagination se charge de la traiter lorsqu'elle est capturée.

Note : une autre façon de gérer la pagination existe avec la méthode PagesBrowser.pagination().