Mein RSS-Feed-Lese-Thread-Thread

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
__marcus__
User
Beiträge: 92
Registriert: Mittwoch 10. September 2008, 22:10
Wohnort: Hamburg

Mein RSS-Feed-Lese-Thread-Thread

Beitragvon __marcus__ » Montag 29. September 2008, 11:14

Leider hab ich auf meinem Rechner nicht MySQLdb installiert, deswegen teste ich das auf dem Server und weiß von daher z.B. nicht, ob das bei mir vielleicht laufen würde. Aber bei dem Skript hier bekomme mal die eine mal die andere Fehlermeldung aus dem Titel. Allerdings auch keine Stelle, wo das passiert, was ich noch nie gehabt habe und nicht wirklich einordnen kann.

Ist mein Code denn soweit OK? (Und kann mir bei der Gelegenheit vielleicht auch jemand sagen, wie ich die letzte auskommentierte Zeile einbauen müsste, damit die nicht geschlossen werden, während die Threads noch laufen?)

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import MySQLdb, feedparser, sys
from threading import Thread

class getNews(Thread):
   def __init__ (self,url):
      Thread.__init__(self)
      self.url = url
   def run(self):
      feed = feedparser.parse(self.url)
      for entry in feed.entries:
         try:
            cursor.execute('INSERT INTO nachrichten(titel, url) VALUES("%s", "%s")', (entry.title.replace('\n', ' '), entry.link))
         except:
            pass

try:
   db = MySQLdb.connect(host = 'rdbms.strato.de', user = 'HANNOVER96', passwd = 'muttisliebling', db = 'KGB567125')
   cursor = db.cursor()
except:
   sys.exit('Could not connect to database server')

cursor.execute('SELECT url FROM feedquellen')

for response in cursor:
   t = getNews(response[0])
   t.start()
   
# cursor.close(); db.close();
Zuletzt geändert von __marcus__ am Donnerstag 2. Oktober 2008, 17:11, insgesamt 1-mal geändert.
BlackJack

Beitragvon BlackJack » Montag 29. September 2008, 11:42

PEP 249 ─ Python Database API Specification v2.0 sagt zum Thema "threadsafety":

Code: Alles auswählen

    threadsafety

            Integer constant stating the level of thread safety the
            interface supports. Possible values are:

                0     Threads may not share the module.
                1     Threads may share the module, but not connections.
                2     Threads may share the module and connections.
                3     Threads may share the module, connections and
                      cursors.


Und `MySQLdb`:

Code: Alles auswählen

In [18]: MySQLdb.threadsafety
Out[18]: 1


Du kannst also weder Verbindungen noch Cursor von mehreren Threads aus verwenden.

Falls das ginge, um die zweite Frage zu beantworten, müsste vor das Schliessen eine Schleife, die auf alle Threads wartet. Siehe `Thread.join()`.

PS: Eventuell ist eine Lösung mit `executemany()` schneller und ich sehe nirgends ein `commit()`!?
__marcus__
User
Beiträge: 92
Registriert: Mittwoch 10. September 2008, 22:10
Wohnort: Hamburg

Beitragvon __marcus__ » Dienstag 30. September 2008, 23:39

Danke Dir.
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

Beitragvon Sr4l » Mittwoch 1. Oktober 2008, 13:00

BlackJack hat geschrieben:PS: Eventuell ist eine Lösung mit `executemany()` schneller und ich sehe nirgends ein `commit()`!?


http://dev.mysql.com/doc/refman/5.1/de/commit.html hat geschrieben:
Standardmäßig läuft MySQL im Autocommit-Modus. Das bedeutet, dass, sobald eine Anweisung ausgeführt wird, die eine Tabelle aktualisiert (also ändert), MySQL diese Änderung auf Festplatte speichert.

Verwenden Sie eine transaktionssichere Speicher-Engine (wie InnoDB, BDB oder NDB Cluster), dann können Sie den Autocommit-Modus mit der folgenden Anweisung deaktivieren:

SET AUTOCOMMIT=0;

Nach der Deaktivierung des Autocommit-Modus durch Setzen der AUTOCOMMIT-Variablen auf Null müssen Sie Ihre Änderungen mit COMMIT auf Festplatte speichern oder mit ROLLBACK rückgängig machen, wenn Sie die seit Beginn der Transaktion vorgenommenen Änderungen nicht übernehmen wollen.


Man braucht also bei MySQL in manchen Fällen kein commit.
Hat mich (vor einiger Zeit) auch gewundert.
BlackJack

Beitragvon BlackJack » Mittwoch 1. Oktober 2008, 13:31

Man braucht es nicht, aber es schadet auch nicht und die Python DB-API 2.0 schreibt vor, dass Transaktionen aktiviert sein sollten wenn die Datenbank das kann, und das `Connection.commit()` als Leeroperation implementiert werden sollte falls die Datenbank keine Transaktionen kann.
__marcus__
User
Beiträge: 92
Registriert: Mittwoch 10. September 2008, 22:10
Wohnort: Hamburg

Beitragvon __marcus__ » Donnerstag 2. Oktober 2008, 17:09

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import MySQLdb, feedparser, sys, threading
news = []      # Hier schreiben die Threads die RSS-Titel samt Link rein
threads = []   # Liste um mit .join() auf alle Threads warten zu können

class getNews(threading.Thread):   
   newslock = threading.Lock()
   def __init__ (self,url):
      threading.Thread.__init__(self)
      self.url = url
   def run(self):
      try:
         feed = feedparser.parse(self.url)
         for entry in feed.entries:
            getNews.newslock.acquire()
            news.append((entry.title, entry.link))
            getNews.newslock.release()
      except:
         pass

if __name__ == "__main__":         
   try:
      db = MySQLdb.connect(host = 'rdbms.strato.de', user = '', passwd = '', db = '')
      cursor = db.cursor()
   except:
      sys.exit('Could not connect to database server')
   cursor.execute('SELECT url FROM feedquellen')
   for response in cursor:
      t = getNews(response[0])      
      threads.append(t)
      t.start()
   for t in threads:
      t.join(60)
   for n in news:
      try:
         cursor.execute('INSERT INTO nachrichten(titel, url) VALUES("%s", "%s")', (n[0], n[1]))
      except:
         pass
   cursor.close(); db.close();


Das ist jetzt im großen und ganzen aus meinem RSS-Feed-Lese-Thread geworden, aber es stellen sich mir leider neue Fragen:

- Hab ich das mit newslock.acquire() richtig gemacht und ist das auch erforderlich hier?
- Die url-Spalte in meiner DB ist "unique" und executemany steigt beim ersten doppelten Eintrag aus. Kann ich das verhindern oder muss ich das dann mit der Schleife machen?

Edit:
Kann mir bitte vielleicht noch jemand die zwei verbliebenen Fragen beantworten?

Wer ist online?

Mitglieder in diesem Forum: Google [Bot]