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
scrapy check spidername
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
"""
Je pense qu'il est rapide de voir l'exemple de code ci-dessous. (Python3.6.2, Scrapy 1.4.0)
@ nom du contrat arg1 arg2 arg3 ...
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)
Les contrats peuvent être prolongés en créant vos propres sous-classes. Enregistrez les Cntracts créés dans setting.py.
self.args
pre_process (self, response)
post_process (self, output)
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
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
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