2016-04-28 9 views
4

Das anfängliche Problem

Ich bin ein CrawlSpider Klasse zu schreiben (die scrapy Bibliothek) und stützen sich auf eine Menge von scrapyasynchronen Magie, damit es funktioniert. Hier ist sie, abgespeckte:erstellen Unit-Test für die Methode von scrapy CrawlSpider

class MySpider(CrawlSpider): 
    rules = [Rule(LinkExtractor(allow='myregex'), callback='parse_page')] 
    # some other class attributes 

    def __init__(self, *args, **kwargs): 
     super(MySpider, self).__init__(*args, **kwargs) 
     self.response = None 
     self.loader = None 

    def parse_page_section(self): 
     soup = BeautifulSoup(self.response.body, 'lxml') 
     # Complicated scraping logic using BeautifulSoup 
     self.loader.add_value(mykey, myvalue) 

    # more methods parsing other sections of the page 
    # also using self.response and self.loader 

    def parse_page(self, response): 
     self.response = response 
     self.loader = ItemLoader(item=Item(), response=response) 
     self.parse_page_section() 
     # call other methods to collect more stuff 
     self.loader.load_item() 

Die Klasse Attribut rule meine Spinne erzählt bestimmte Links zu folgen und springen Sie zu einer Callback-Funktion sobald die Web-Seiten heruntergeladen werden. Mein Ziel ist es, die Parsing-Methode namens parse_page_section zu testen, ohne den Crawler auszuführen oder sogar echte HTTP-Anfragen zu stellen.

Was habe ich versucht,

Instinktiv Ich wandte mich an die mock Bibliothek. Ich verstehe, wie Sie eine Funktion verspotten, um zu testen, ob sie aufgerufen wurde (mit welchen Argumenten und ob es irgendwelche Nebenwirkungen gab ...), aber das ist nicht das, was ich will. Ich möchte ein gefälschtes Objekt MySpider instanziieren und gerade genug Attribute zuweisen, um parse_page_section Methode darauf aufrufen zu können.

Im obigen Beispiel, ich brauche ein response Objekt mein ItemLoader und speziell ein self.response.body Attribut zu instanziiert meine BeautifulSoup instanziiert. Im Prinzip könnte ich gefälschte Objekte wie diese machen:

from argparse import Namespace 

my_spider = MySpider(CrawlSpider) 
my_spider.response = NameSpace(body='<html>...</html>') 

, die gut auf die BeautifulSoup Klasse funktioniert, aber ich müsste mehr Attribute hinzufügen, um ein ItemLoader-Objekt zu erstellen. Für komplexere Situationen würde es hässlich und unüberschaubar werden.

Meine Frage

Ist dies der ganz richtige Ansatz? Ich kann keine ähnlichen Beispiele im Internet finden, daher denke ich, dass mein Ansatz auf einer grundlegenderen Ebene falsch ist. Jede Einsicht würde sehr geschätzt werden.

+0

@ChrisP danke für Ihre Bearbeitung. Ich habe das "scrapy" -Label nicht an erster Stelle platziert, weil ich dachte, dass die Frage im Allgemeinen mit Unit-Testing zu tun hat. – cyberbikepunk

+0

Es ist definitiv Unit-Tests im Allgemeinen, aber Leute, die viel Scraping machen, können einige einzigartige Einsichten für Unit-Test-Scraper haben. – ChrisP

+0

In diesem speziellen 'CrawlSpider'-Fall konnte ich mit dem Vortäuschen eines Antwortobjekts davonkommen. Es ist schwierig, es von Hand zu tun, aber könnte das helfen? http://requests-mock.readthedocs.io/en/latest/overview.html. Wäre das ein guter Ansatz? – cyberbikepunk

Antwort

1

Haben Sie gesehen Spiders Contracts?

Damit können Sie jeden Callback Ihres Spiders testen, ohne viel Code zu benötigen. Zum Beispiel:

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

Verwenden Sie den Befehl check die Vertragsprüfungen auszuführen.

Betrachten Sie diese answer, wenn Sie etwas noch größer wollen.

+0

Ich denke, es macht Sinn, sich für * real life * (integration) Tests anstelle von Unit Testing zu entscheiden, da sich die Website selbst ändern kann. Im Wesentlichen garantiert die Tatsache, dass Ihre Unit-Tests funktionieren, nicht, dass Ihr Scraping funktioniert. Danke für Ihren Vorschlag. – cyberbikepunk

+0

In Komponententests gibt es jedoch immer noch einen Wert, da zumindest beim Codieren Vernunftprüfungen durchgeführt werden.Die andere Antwort, die Sie zur Verfügung stellen (http://stackoverflow.com/questions/6456304/scrapy-unit-testing/12741030#12741030), zeigt, wie Sie ein Antwortobjekt auf eine bessere Weise fälschen können, indem Sie 'scrapy'' Request' und 'Antwort'-Objekte. Netter Tipp. – cyberbikepunk

Verwandte Themen