2016-06-08 18 views
0

Ich habe ein Problem mit meiner Scrapy Spider. Ipass ein Antwortobjekt von einer Parse-Funktion zu einer anderen. Der Code wird nur für Funktionen ausgeführt, die keine Yield-Anfrage haben (siehe Unterschied zwischen den beiden Snippets). Warum ist das?Python Yield verhindert Ausgabe/Ausführung in Scrapy Web Spider Crawler

# Yield in parse_page2 scope; "Parsing page 2" doesn't print 
import scrapy 


class GetInfSpider(scrapy.Spider): 
    name = "get_inf" 
    # allowed_domains = ["example.com 
    def start_requests(self): 
     yield scrapy.Request("http://www.apple.com", callback=self.parse_page1) 


    def parse_page1(self, response): 
     print"Parsepage1" 
     self.printString('This prints yet self.parse_page2(response) does not print if yield is in scope !!!') 
     self.parse_page2(response) 


    def printString(self, string): 
     print string 


    def parse_page2(self, response): 
     print "Parsing page 2" 

     yield scrapy.Request("http://www.google.com", callback=self.parse_page3) 

    def parse_page3(self, response): 
     pass 

output:

Parsiing page1 
This prints yet self.parse_page2(response) does not print if yield is in its scope !!! 

Im Gegensatz zu:

# Yield commented out; "Parsing page 2" does print 
import scrapy 


class GetInfSpider(scrapy.Spider): 
    name = "get_inf" 
    # allowed_domains = ["example.com 
    def start_requests(self): 
     yield scrapy.Request("http://www.apple.com", callback=self.parse_page1) 


    def parse_page1(self, response): 
     print"Parsiing page1" 
     self.printString('This prints yet self.parse_page2(response) does not print if yield is in its scope !!!', response) 
     self.parse_page2(response) 


    def printString(self, string, response): 
     print string 


    def parse_page2(self, response): 
     print "Parsing page 2" 

     #yield scrapy.Request("http://www.google.com", callback=self.parse_page3) 

    def parse_page3(self, response): 
     pass 

output:

Parsiing page1 
This prints yet self.parse_page2(response) does not print if yield is in its scope !!! 
Parsing page 2 
+0

I entdeckt, dass es funktioniert, wenn Sie Return statt Pass haben. – josh

+0

Was versuchen Sie mit der Ausbeute zu erreichen? Sie lassen es niemals einen Wert ergeben. Während du versuchst, den ersten Wert zu berechnen, erzielst du eine zweite Rendite. – cmd

+0

Dieser Code ist nur ein Beispiel für die Frage. Aber das gleiche Problem ist in meinem tatsächlichen Code. Ich hatte bereits yield verwendet, um eine Antwort von einer URL zu erhalten, und wollte sie an einen anderen Parser übergeben, ohne die Seite erneut aufrufen zu müssen (spart bandwitdh). Aber es würde es nicht analysieren, wenn der Ertrag in der oben gezeigten Funktion vorhanden wäre. Ich weiß nicht viel über Rendite und Rendite, aber alle Scrapy-Beispiele verwenden Rendite mit Produkten, also verwende ich sie. – josh

Antwort

0

Scrapy spider callbacks werden erwartet:

  • einen Artikel zurückgeben, eine dict oder eine Anfrage
  • oder Iterables von Gegenständen, dicts oder Anfragen,

In der Callback-Funktion sein, analysieren Sie die Antwort (Webseite) und Rückkehr entweder dicts mit extrahierten Daten, Item-Objekten, Request-Objekten oder einem iterierbaren dieser Objekte.

Das 2 häufigsten Fälle für iterable Rückrufe Ergebnisse aufweisen, sind:

  • entweder Generatoren yield Verwendung, Gegenstände, dicts Erzeugen oder Anforderungen
  • oder zurückkehr Python Liste ([]) von Elementen, dicts oder Anfragen

(andere Python-Objekte können Iterables sein, aber lassen Sie sich auf diesen 2 Fälle konzentrieren)

yield in Ihren Callback-Methoden verwenden, sind zu definieren Sie generators

Generatoren Funktionen ermöglichen es Ihnen, eine Funktion, die wie ein Iterator verhält sich zu erklären, das heißt es kann für Schleife in einem verwendet werden.

Also, in Ihrem parse_page1, wenn Sie „Ergebnisse“ von einem anderen Callback-Methode verwenden möchten, die yield verwendet, müssen Sie auf sie wiederholen, diese generierten Werte erfassen, und entweder:

  • yield jeder Wert wieder Scrapy Motor (wodurch ein zu parse_page1 Generator)
  • oder kehren alle Werte in parse_page1 als Liste

Hier ist ein Beispiel Spinne (sagen wir, spider.py) mit einem Generator Rückruf, parse, mit 3 anderen Generator Rückrufe und 1 Rückruf mit Rückkehr:

import scrapy 

class TestSpider(scrapy.Spider): 
    name = "test" 
    start_urls = ["http://www.example.com"] 

    def parse(self, response): 
     # parse_a, parse_b and parse_c are generators, 
     # so we "for loop" over them 
     for rb in self.parse_a(response): 
      yield rb 

     for rb in self.parse_b(response): 
      yield rb 

     for rb in self.parse_c(response): 
      yield rb 

     # parse_d returns a single value 
     # so we yield it's result directly 
     yield self.parse_d(response) 

    def parse_a(self, response): 
     self.logger.info("I am in 'parse_a'") 
     yield {"value": "a"} 

    def parse_b(self, response): 
     self.logger.info("I am in 'parse_b'") 
     yield {"value": "b"} 

    def parse_c(self, response): 
     self.logger.info("I am in 'parse_c'") 
     yield {"value": "c"} 

    def parse_d(self, response): 
     self.logger.info("I am in 'parse_d'") 
     return {"value": "d"} 

scrapy run spider.py dies erzeugt:

$ scrapy runspider spider.py 
2016-06-09 10:40:35 [scrapy] INFO: Scrapy 1.1.0 started (bot: scrapybot) 
(...) 
2016-06-09 10:40:35 [scrapy] INFO: Spider opened 
2016-06-09 10:40:35 [scrapy] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min) 
2016-06-09 10:40:36 [scrapy] DEBUG: Crawled (200) <GET http://www.example.com> (referer: None) 
2016-06-09 10:40:36 [test] INFO: I am in 'parse_a' 
2016-06-09 10:40:36 [scrapy] DEBUG: Scraped from <200 http://www.example.com> 
{'value': 'a'} 
2016-06-09 10:40:36 [test] INFO: I am in 'parse_b' 
2016-06-09 10:40:36 [scrapy] DEBUG: Scraped from <200 http://www.example.com> 
{'value': 'b'} 
2016-06-09 10:40:36 [test] INFO: I am in 'parse_c' 
2016-06-09 10:40:36 [scrapy] DEBUG: Scraped from <200 http://www.example.com> 
{'value': 'c'} 
2016-06-09 10:40:36 [test] INFO: I am in 'parse_d' 
2016-06-09 10:40:36 [scrapy] DEBUG: Scraped from <200 http://www.example.com> 
{'value': 'd'} 
2016-06-09 10:40:36 [scrapy] INFO: Closing spider (finished) 
2016-06-09 10:40:36 [scrapy] INFO: Dumping Scrapy stats: 
{'downloader/request_bytes': 213, 
'downloader/request_count': 1, 
'downloader/request_method_count/GET': 1, 
'downloader/response_bytes': 957, 
'downloader/response_count': 1, 
'downloader/response_status_count/200': 1, 
'finish_reason': 'finished', 
'finish_time': datetime.datetime(2016, 6, 9, 8, 40, 36, 144795), 
'item_scraped_count': 4, 
'log_count/DEBUG': 5, 
'log_count/INFO': 11, 
'response_received_count': 1, 
'scheduler/dequeued': 1, 
'scheduler/dequeued/memory': 1, 
'scheduler/enqueued': 1, 
'scheduler/enqueued/memory': 1, 
'start_time': datetime.datetime(2016, 6, 9, 8, 40, 35, 733023)} 
2016-06-09 10:40:36 [scrapy] INFO: Spider closed (finished) 

nun ein mit Callback-Methode mit Rückgabe für Werte von Generatoren und Nicht-Generator Callbacks:

Es ergibt dies:

$ scrapy runspider spider.py 
2016-06-09 10:50:41 [scrapy] INFO: Scrapy 1.1.0 started (bot: scrapybot) 
(...) 
2016-06-09 10:50:41 [scrapy] INFO: Spider opened 
2016-06-09 10:50:41 [scrapy] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min) 
2016-06-09 10:50:41 [scrapy] DEBUG: Crawled (200) <GET http://www.example.com> (referer: None) 
2016-06-09 10:50:41 [test] INFO: I am in 'parse_a' 
2016-06-09 10:50:41 [test] INFO: I am in 'parse_b' 
2016-06-09 10:50:41 [test] INFO: I am in 'parse_b' 
2016-06-09 10:50:41 [test] INFO: I am in 'parse_d' 
2016-06-09 10:50:41 [scrapy] DEBUG: Scraped from <200 http://www.example.com> 
{'value': 'a'} 
2016-06-09 10:50:41 [scrapy] DEBUG: Scraped from <200 http://www.example.com> 
{'value': 'b'} 
2016-06-09 10:50:41 [scrapy] DEBUG: Scraped from <200 http://www.example.com> 
{'value': 'b'} 
2016-06-09 10:50:41 [scrapy] DEBUG: Scraped from <200 http://www.example.com> 
{'value': 'd'} 
2016-06-09 10:50:41 [scrapy] INFO: Closing spider (finished) 
2016-06-09 10:50:41 [scrapy] INFO: Dumping Scrapy stats: 
{'downloader/request_bytes': 213, 
'downloader/request_count': 1, 
'downloader/request_method_count/GET': 1, 
'downloader/response_bytes': 957, 
'downloader/response_count': 1, 
'downloader/response_status_count/200': 1, 
'finish_reason': 'finished', 
'finish_time': datetime.datetime(2016, 6, 9, 8, 50, 41, 619777), 
'item_scraped_count': 4, 
'log_count/DEBUG': 5, 
'log_count/INFO': 11, 
'response_received_count': 1, 
'scheduler/dequeued': 1, 
'scheduler/dequeued/memory': 1, 
'scheduler/enqueued': 1, 
'scheduler/enqueued/memory': 1, 
'start_time': datetime.datetime(2016, 6, 9, 8, 50, 41, 225264)} 
2016-06-09 10:50:41 [scrapy] INFO: Spider closed (finished) 

Beachten Sie den Unterschied in der INFO-Protokolle: Im zweiten Fall werden alle INFO Linien auf einmal gedruckt, weil parse alle Generatoren verbraucht, bevor die Ergebnisse zurück zu scrapy Motor geben, und druckt dann scrapy die Gegenstände, die es bekommen hat (als Liste). Wenn im ersten Fall parse als Generator verwendet wird, iteriert scrapy über die Callback-Ergebnisse, und Sub-Callbacks werden nacheinander aufgerufen, wobei Elemente bei jeder Iteration unter yield Punkte erzeugt werden.

Eine weitere Variante ist Unter Rückrufe in eine Liste zu konsumieren (wie im zweiten Beispiel oben), und haben immer noch parse als Generator, aber das wäre weniger kohärent (wenn auch immer noch in Betrieb):

import scrapy 

class TestSpider(scrapy.Spider): 
    name = "test" 
    start_urls = ["http://www.example.com"] 

    def parse(self, response): 
     results = [] 

     # extend() will loop on the values from the generators, 
     # adding them to the list 
     results.extend(self.parse_a(response)) 
     results.extend(self.parse_b(response)) 
     results.extend(self.parse_b(response)) 

     # parse_d() returns only 1 value, so we just append the return value 
     results.append(self.parse_d(response)) 

     for rv in results: 
      yield rv 

    def parse_a(self, response): 
     self.logger.info("I am in 'parse_a'") 
     yield {"value": "a"} 

    def parse_b(self, response): 
     self.logger.info("I am in 'parse_b'") 
     yield {"value": "b"} 

    def parse_c(self, response): 
     self.logger.info("I am in 'parse_c'") 
     yield {"value": "c"} 

    def parse_d(self, response): 
     self.logger.info("I am in 'parse_d'") 
     return {"value": "d"} 
Verwandte Themen