2017-08-31 4 views
0

Ich habe versucht, this pipeline in meinem Spider zu implementieren. Nach der Installation der notwendigen Abhängigkeiten kann ich die Spinne ohne Fehler ausführen, aber aus irgendeinem Grund schreibt es nicht in meine Datenbank.Pipeline schreibt nicht nach MySQL, aber gibt auch keinen Fehler

Ich bin mir ziemlich sicher, dass mit der Verbindung zur Datenbank etwas nicht in Ordnung ist. Wenn ich ein falsches Passwort eingib, bekomme ich immer noch keinen Fehler.

Wenn die Spinne alle Daten abgekratzt hat, braucht sie ein paar Minuten, bevor sie mit dem Speichern der Statistiken beginnt.

2017-08-31 13:17:12 [scrapy] INFO: Closing spider (finished) 
2017-08-31 13:17:12 [scrapy] INFO: Stored csv feed (27 items) in: test.csv 
2017-08-31 13:24:46 [scrapy] INFO: Dumping Scrapy stats: 

Pipeline:

import MySQLdb.cursors 
from twisted.enterprise import adbapi 

from scrapy.xlib.pydispatch import dispatcher 
from scrapy import signals 
from scrapy.utils.project import get_project_settings 
from scrapy import log 

SETTINGS = {} 
SETTINGS['DB_HOST'] = 'mysql.domain.com' 
SETTINGS['DB_USER'] = 'username' 
SETTINGS['DB_PASSWD'] = 'password' 
SETTINGS['DB_PORT'] = 3306 
SETTINGS['DB_DB'] = 'database_name' 

class MySQLPipeline(object): 

    @classmethod 
    def from_crawler(cls, crawler): 
     return cls(crawler.stats) 

    def __init__(self, stats): 
     print "init" 
     #Instantiate DB 
     self.dbpool = adbapi.ConnectionPool ('MySQLdb', 
      host=SETTINGS['DB_HOST'], 
      user=SETTINGS['DB_USER'], 
      passwd=SETTINGS['DB_PASSWD'], 
      port=SETTINGS['DB_PORT'], 
      db=SETTINGS['DB_DB'], 
      charset='utf8', 
      use_unicode = True, 
      cursorclass=MySQLdb.cursors.DictCursor 
     ) 
     self.stats = stats 
     dispatcher.connect(self.spider_closed, signals.spider_closed) 

    def spider_closed(self, spider): 
     print "close" 
     """ Cleanup function, called after crawing has finished to close open 
      objects. 
      Close ConnectionPool. """ 
     self.dbpool.close() 

    def process_item(self, item, spider): 
     print "process" 
     query = self.dbpool.runInteraction(self._insert_record, item) 
     query.addErrback(self._handle_error) 
     return item 

    def _insert_record(self, tx, item): 
     print "insert" 
     result = tx.execute(
     " INSERT INTO matches(type,home,away,home_score,away_score) VALUES (soccer,"+item["home"]+","+item["away"]+","+item["score"].explode("-")[0]+","+item["score"].explode("-")[1]+")" 
     ) 
     if result > 0: 
      self.stats.inc_value('database/items_added') 

    def _handle_error(self, e): 
     print "error" 
     log.err(e) 

Spider:

import scrapy 
import dateparser 
from crawling.items import KNVBItem 

class KNVBspider(scrapy.Spider): 
    name = "knvb" 
    start_urls = [ 
     'http://www.knvb.nl/competities/eredivisie/uitslagen', 
    ] 
    custom_settings = { 
     'ITEM_PIPELINES': { 
      'crawling.pipelines.MySQLPipeline': 301, 
     } 
    } 
    def parse(self, response): 
     # www.knvb.nl/competities/eredivisie/uitslagen 
     for row in response.xpath('//div[@class="table"]'): 
      for div in row.xpath('./div[@class="row"]'): 
       match = KNVBItem() 
       match['home'] = div.xpath('./div[@class="value home"]/div[@class="team"]/text()').extract_first() 
       match['away'] = div.xpath('./div[@class="value away"]/div[@class="team"]/text()').extract_first() 
       match['score'] = div.xpath('./div[@class="value center"]/text()').extract_first() 
       match['date'] = dateparser.parse(div.xpath('./preceding-sibling::div[@class="header"]/span/span/text()').extract_first(), languages=['nl']).strftime("%d-%m-%Y") 
       yield match 

Wenn es besser sind Rohrleitungen zur Verfügung zu tun, was ich versuche das auch willkommen zu erreichen sein würde. Vielen Dank!

Update: Mit dem Link in der akzeptierte Antwort vorausgesetzt ich schließlich auf diese Funktion habe, die funktioniert (und gelöst damit mein Problem):

def process_item(self, item, spider): 
    print "process" 
    query = self.dbpool.runInteraction(self._insert_record, item) 
    query.addErrback(self._handle_error) 
    query.addBoth(lambda _: item) 
    return query 
+0

Ihre Einrückung scheint falsch zu sein. '_insert_error' und' _handle_error' sind nicht Teil der Pipeline. Hast du überprüft, ob 'process_item' in deiner Pipeline aufgerufen wird (etwas ausgedruckt)? –

+0

Aktualisierte den Code mit einigen Drucken und fester Einrückung. 'process_item' wird aufgerufen, sowie alle anderen Funktionen außer' _insert_record' und '_handle_error'. Ich bin wirklich erstaunt, dass es so weit kommt, während die Verbindung in der 'init'-Funktion mit einem falschen Passwort fehlschlagen sollte, aber aus irgendeinem Grund bekomme ich keinen Fehler dafür. – Casper

+0

@Casper Haben Sie Zugriff auf MySQL-Serverprotokolle? Wenn ja, versuchen Sie es zu überprüfen, Sie könnten etwas finden. –

Antwort

0

Werfen Sie einen Blick auf at this für die Verwendung von Adbapi mit MySQL zum Speichern von Scraped-Elementen. Beachten Sie den Unterschied zwischen Ihrer und ihrer Methodenimplementierung. Während Sie den Artikel sofort zurücksenden, geben sie Deferred Objekt zurück, das das Ergebnis der Methode runInteraction ist und die den Artikel nach seiner Fertigstellung zurückgibt. Ich denke, das ist der Grund, warum Ihre _insert_record nie aufgerufen wird.

+0

Ich habe mit der Rückgabe der runInteraction getestet, aber ich bekomme einen AttributeError. Ich habe das zur Frage hinzugefügt. – Casper

+0

Ich musste 'query.addBoth (Lambda _: Element)' hinzufügen, wie sich herausstellt. Jetzt bekomme ich einen korrekten Fehler und sage mir, dass es keine Verbindung mit dem Server herstellen kann, also überprüfe ich dies mit meinem Provider. Danke bis jetzt! – Casper

+0

Das letzte Problem mit meinem Provider gelöst. Danke für die Bereitstellung dieses Codes @ Tomáš Linhart – Casper

0

Wenn Sie den Einsatz in der Ausgabe sehen können, die bereits ein Gutes Zeichen. Ich würde die Insert-Funktion auf diese Weise umschreiben:

def _insert_record(self, tx, item): 
    print "insert" 
    raw_sql = "INSERT INTO matches(type,home,away,home_score,away_score) VALUES ('%s', '%s', '%s', '%s', '%s')" 
    sql = raw_sql % ('soccer', item['home'], item['away'], item['score'].explode('-')[0], item['score'].explode('-')[1]) 
    print sql 
    result = tx.execute(sql) 
    if result > 0: 
     self.stats.inc_value('database/items_added') 

Es ermöglicht Sie, die SQL-debuggen Sie verwenden. In Ihrer Version wickeln Sie die Zeichenfolge nicht in ' um, was ein Syntaxfehler in mysql ist. Ich bin nicht sicher über Ihre letzten Werte (Score), also behandelte ich sie als Strings.

+0

Ich denke, er hat geschrieben * 'process_item' heißt, sowie alle anderen Funktionen außer' _insert_record' ... * –

+0

Das stimmt. Es scheint, als würde der '_insert_record' niemals aufgerufen. Daher glaube ich nicht, dass eine Änderung dieser Funktion einen Unterschied machen wird. – Casper

+0

Stimmt, irgendwie habe ich das nicht gesehen. –

Verwandte Themen