[PYTHON] Ecrire des tests Spider dans Scrapy

Quand j'ai essayé d'écrire un test unitaire pour Scrapy, c'était un peu spécial et je n'avais pas beaucoup d'informations, alors je l'ai résumé. En raison des caractéristiques du robot d'exploration selon lesquelles il est sûr de modifier le code HTML à tout moment, je pense qu'il est préférable de l'utiliser principalement pour raccourcir le temps d'analyse au moment de la mise en œuvre plutôt que pour le contrôle de validité. (* Ceci est principalement un article sur les tests unitaires Spider) (* Les tests tels que Pipeline sont hors limites car ils peuvent être écrits normalement avec unittest etc.)

TL;DR;

Utiliser les contrats Spiders

    def parse(self, response):
    """ This function parses a sample response. Some contracts are mingled
    with this docstring.

    @url http://www.amazon.com/s?field-keywords=selfish+gene
    @returns items 1 16
    @returns requests 0 0
    @scrapes Title Author Year Price
    """

Utilisation de base des contrats Spiders

Je pense qu'il est rapide de voir l'exemple de code ci-dessous. (Python3.6.2, Scrapy 1.4.0)

myblog.py


    def parse_list(self, response):
        """Processus d'analyse de l'écran de liste

        @url http://www.rhoboro.com/index2.html
        @returns item 0 0
        @returns requests 0 10
        """
        for detail in response.xpath('//div[@class="post-preview"]/a/@href').extract():
            yield Request(url=response.urljoin(detail), callback=self.parse_detail)

Faire des contrats personnalisés

Créer une sous-classe

Les contrats peuvent être prolongés en créant vos propres sous-classes. Enregistrez les Cntracts créés dans setting.py.

contracts.py


# -*- coding: utf-8 -*-

from scrapy.contracts import Contract
from scrapy.exceptions import ContractFail


class ItemValidateContract(Contract):
    """Vérifiez si l'article est comme prévu

Parce que le résultat de l'acquisition peut changer à tout moment
Je pense qu'il est préférable de tester uniquement là où vous attendez des valeurs invariantes.
Dois-je vérifier plus que des éléments manquants avec Pipeline?
    """
    name = 'item_validate' #Ce nom sera le nom dans la docstring

    def post_process(self, output):
        item = output[0]
        if 'title' not in item:
            raise ContractFail('title is invalid.')


class CookiesContract(Contract):
    """Sur demande(éraflure)Contrat pour ajouter des cookies

    @cookies key1 value1 key2 value2
    """
    name = 'cookies'

    def adjust_request_args(self, kwargs):
        # self.Convertir les arguments au format dictionnaire et mettre en cookies
        kwargs['cookies'] = {t[0]: t[1]
                             for t in zip(self.args[::2], self.args[1::2])}
        return kwargs

Code d'utilisateur

Le code sur le côté qui l'utilise ressemble à ceci.

settings.py


...
SPIDER_CONTRACTS = {
    'item_crawl.contracts.CookiesContract': 10,
    'item_crawl.contracts.ItemValidateContract': 20,
}
...

myblog.py


    def parse_detail(self, response):
        """Processus d'analyse de l'écran détaillé

        @url http://www.rhoboro.com/2017/08/05/start-onomichi.html
        @returns item 1
        @scrapes title body tags
        @item_validate
        @cookies index 2
        """
        item = BlogItem()
        item['title'] = response.xpath('//div[@class="post-heading"]//h1/text()').extract_first()
        item['body'] = response.xpath('//article').xpath('string()').extract_first()
        item['tags'] = response.xpath('//div[@class="tags"]//a/text()').extract()
        item['index'] = response.request.cookies['index']
        yield item

Lancer le test

Exécutez avec scrapy check spidername. De toute évidence, c'est plus rapide que d'essayer le nom de l'araignée de balayage scrapy, car il explore uniquement la page spécifiée.

(venv) [alpaca]~/github/scrapy/crawler/crawler % scrapy check my_blog                                                                                                [master:crawler]
.....
----------------------------------------------------------------------
Ran 5 contracts in 8.919s

OK
(venv) [alpaca]~/github/scrapy/crawler/crawler % scrapy check my_blog                                                                                                [master:crawler]
...FF
======================================================================
FAIL: [my_blog] parse_detail (@scrapes post-hook)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/rhoboro/github/scrapy/venv/lib/python3.6/site-packages/scrapy/contracts/__init__.py", line 134, in wrapper
    self.post_process(output)
  File "/Users/rhoboro/github/scrapy/venv/lib/python3.6/site-packages/scrapy/contracts/default.py", line 89, in post_process
    raise ContractFail("'%s' field is missing" % arg)
scrapy.exceptions.ContractFail: 'title' field is missing

======================================================================
FAIL: [my_blog] parse_detail (@item_validate post-hook)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/rhoboro/github/scrapy/venv/lib/python3.6/site-packages/scrapy/contracts/__init__.py", line 134, in wrapper
    self.post_process(output)
  File "/Users/rhoboro/github/scrapy/crawler/crawler/contracts.py", line 18, in post_process
    raise ContractFail('title is invalid.')
scrapy.exceptions.ContractFail: title is invalid.

----------------------------------------------------------------------
Ran 5 contracts in 8.552s

FAILED (failures=2)

Au fait, ici en cas d'erreur. (C'est à ce moment que j'ai oublié de mentionner settings.py.) Pour être honnête, il y a trop peu d'informations et c'est difficile.

(venv) [alpaca]~/github/scrapy/crawler/crawler % scrapy check my_blog                                                                                                [master:crawler]
Unhandled error in Deferred:


----------------------------------------------------------------------
Ran 0 contracts in 0.000s

OK

Recommended Posts

Ecrire des tests Spider dans Scrapy
Ecrire un test en langue GO + gin
Ecrire un test unitaire de langage C en Python
Écrire un décorateur en classe
Ecrire Python dans MySQL
Écrire des tests en Python pour profiler et vérifier la couverture
Ecrire des filtres Pandec en Python
Ecrire l'entrée standard dans le code
Écrire une distribution bêta en Python
Ecrire python dans Rstudio (réticulé)
Ecrire Spigot dans VS Code
Écrire des données au format HDF
Ecrire une dichotomie en Python
Ecrire un test piloté par table en C
Ecrire un schéma JSON avec Python DSL
Comment écrire sobrement avec des pandas
Ecrire un serveur HTTP / 2 en Python
Installer Scrapy dans l'environnement Python Anaconda
Ecrire une fonction AWS Lambda en Python
Jusqu'à ce que vous commenciez à explorer avec Scrapy
Ecrire des algorithmes A * (A-star) en Python
[Maya] Ecrire un nœud personnalisé dans Open Maya 2.0
Ecrire des contraintes de clé externe dans Django
Ecrire le code de test du sélénium en python
Ecrire un graphique à secteurs en Python
Tests de code dans le temps en Python
Comment séparer le code de traitement du pipeline par spider avec Scrapy