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"}
I entdeckt, dass es funktioniert, wenn Sie Return statt Pass haben. – josh
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
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