MemoryError bei Beautiful Soup (dringend)

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.
Benutzeravatar
Michael Schneider
User
Beiträge: 567
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Bremen
Kontaktdaten:

Freitag 14. September 2007, 13:33

BlackJack hat geschrieben:Vielleicht ist es einfacher das Programm so umzuschreiben, das pro Seite, oder für alle hundert Seiten, ein eigener Prozess gestartet wird. Dann könnte man auch gleich mehrere Prozesse parallel starten und damit Multicore-Prozessoren besser ausnutzen.
Wow, die Idee ist gut! Ich dachte gerade darüber nach, wirklich ein paar hundert Seiten auf einmal über einen popen2-Aufruf in einer eigenen Programminstanz zu analysieren (wie kann man eigentlich über eine Pipe in ein Skript kommende Daten dort auslesen?).
Aber wenn es stimmt, dass im Zweifelsfall nur der eine Prozess gekillt wird und nicht der mit meinem Hauptscript, dann ist das zweifellos besser. Gerade weil der Import der MySQL Schnittstelle immer etwas dauert.

Danke für die Anregung, mal sehen wie ich das einbauen kann!

Grüße,
Michael
Diese Nachricht zersört sich in 5 Sekunden selbst ...
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Freitag 14. September 2007, 14:46

birkenfeld hat geschrieben:Und zwar nicht mit "unicode()", denn __unicode__ liefert einfach self zurück.
Tatsache. Und jetzt ist mir auch wieder klar warum TextPress "child + ''" macht. :wink:
TUFKAB – the user formerly known as blackbird
BlackJack

Freitag 14. September 2007, 15:42

Slicing hat bei einem kurzen Test auch funktioniert. :-)
Benutzeravatar
Michael Schneider
User
Beiträge: 567
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Bremen
Kontaktdaten:

Freitag 14. September 2007, 21:13

Könnt ihr mir nochmal erklären, was es mit dem "child +", dem Unicode und dem Slicing auf sich hat?

Michael
Diese Nachricht zersört sich in 5 Sekunden selbst ...
BlackJack

Freitag 14. September 2007, 22:07

Jo, man achte auf den Typ:

Code: Alles auswählen

In [137]: c
Out[137]: u'DATUM'

In [138]: type(c)
Out[138]: <class BeautifulSoup.NavigableString>

In [139]: c + ''
Out[139]: u'DATUM'

In [140]: type(c + '')
Out[140]: <type 'unicode'>

In [141]: type(c[:])
Out[141]: <type 'unicode'>
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Freitag 14. September 2007, 22:14

Und der Knackpunkt ist eben:

Code: Alles auswählen

In : type(unicode(c))
Out: <class BeautifulSoup.NavigableString>
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Benutzeravatar
Michael Schneider
User
Beiträge: 567
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Bremen
Kontaktdaten:

Samstag 15. September 2007, 09:17

Hallo,

gut, danke. Soweit habe ich das verstanden. Und das wende ich jetzt auf alle NavigableStrings an? Oder wie kann ich das effektiv nutzen, um das Speicherproblem zu bekämpfen? Ich meine, ich behalte ja eigentlich keine Referenzen.
Ich habe zwar zwischenzeitlich ein Dictionary mit NavigableStrings, aber ich dachte, dass die Referenzen vom Dict auf die BF-Objekte nur einseitig (vom Dict) wären und damit beim Abbau des Dicts gelöst werden.

Gibt es keine Methode in dem ganzen BF Paket, das dieses Gesamtkonstrukt wieder trennt???

Grüße,
Michael
Diese Nachricht zersört sich in 5 Sekunden selbst ...
BlackJack

Samstag 15. September 2007, 11:23

Na irgendwo muss es ja noch Referenzen geben. Was machst Du denn mit den Daten? An Funktionen/Methoden der Datenbankanbindung übergeben? Vielleicht hält die ja noch Referenzen aus irgendwelchen Gründen und `BeautifulSoup` ist "unschuldig". Kannst ja mal die entsprechenden Funktionen/Objekte durch "Dummies" ersetzen und schauen ob das Programm dann durchläuft.

Oder schauen ob Du mit dem `gc`-Modul nach dem Verarbeiten von ein paar Seiten noch Objekte findest, die Deiner Meinung nach gar nicht mehr vorhanden sein dürften.
Benutzeravatar
Michael Schneider
User
Beiträge: 567
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Bremen
Kontaktdaten:

Dienstag 18. September 2007, 09:43

Hallo BlackJack,

die Idee mit dem gc-Modul war wirklich gut. Ich habe folgende Passage eingefuegt, um nach jedem Schleifendurchlauf den Zwischenstand zu bekommen:

Code: Alles auswählen

	gc.set_debug(gc.DEBUG_LEAK)
gc.collect()
print "gc-check => checked %i, garbage: %i" % (len(gc.get_objects()), len(gc.garbage))
for o in gc.garbage:
    print type(o), o
    del gc.garbage[:]
fileErrLog.flush()
Wenn ich das ausfuehre, bekomme ich diese Bildschirmausgabe:
<type 'instance'>
<type 'dict'> {'originalEncoding': 'ascii', 'parseOnlyThese': None, 'fromEncoding': None, 'verbose': 0, 'parent': None, 'parserClass': <class BeautifulSoup.BeautifulSoup at 0x402adf90>, 'quoteStack': [], 'nextSibling': None, '_SGMLParser__starttag_text': u'<A HREF="http://...">', 'currentData': [], 'attrs': [], 'offset': 0, 'instanceSelfClosingTags': {None: None}, 'containsSubstitutions': 0, 'convertEntities': None, 'stack': [], 'isSelfClosing': 0, 'previous': u'\n', 'tagStack': [], 'smartQuotesTo': 'html', 'contents': [], 'markupMassage': [(<_sre.SRE_Pattern object at 0x40058b40>, <function <lambda> at 0x402b5578>), (<_sre.SRE_Pattern object at 0x4006c120>, <function <lambda> at 0x402b55c0>)], 'name': u'[document]', 'markup': None, 'nomoretags': 0, 'next': None, 'literal': 0, 'rawdata': u'', 'lasttag': u'a', 'hidden': 1, 'currentTag': , 'previousSibling': None, 'lineno': 1}
<type 'list'> []
<type 'list'> []
<type 'dict'> {None: None}
<type 'list'> []
<type 'list'> []
<type 'list'> []
<type 'list'> []
<class 'BeautifulSoup.NavigableString'>

<type 'dict'> {'nextSibling': None, 'next': None, 'previousSibling': None, 'parent': None, 'previous': None}
Und in errlog bekomme ich 11 collectable Objekte angezeigt (ich denke die da oben). Aber nur, solange die Suche erfolglos war (also heute morgen noch nichts los war). Inzwischen rast eine schier endlose Liste übers Terminal und ich musste abbrechen. Bei dem, was ich sah, handelte es sich um Tags und NaviableStrings (bzw. Listen und Dicts, die wahrscheinlich davon referenziert werden, siehe oben).

Jetzt habe ich es nochmal versucht, ohne gc.set_debug - was ja die Objekte liefern soll, die unreferenziert, aber nicht collectable sind. Jetzt bekomme ich schon einen Wert von 1201 (beim ersten Durchlauf). Beim Folgedurchlauf sind es dann schon 2412.

Fazit: es gibt in meinem Programm offenbar Lecks, die direkt mit den BeautifulSoup Objekten zutun haben.

Aber wie kann ich die Elemente der garbage-Liste manuell freigeben?
Wenn ich es richtig verstanden habe, muss ich das Basisobjekt (BF) finden, dann deren Referenzierer (gc.get_referrers) identifizieren und das Basisobjekt dann aus all diesen Listen, Dictionaries, Instanzen ... entfernen? Gibt es dafür schon einen Präzedenzfall?

[EDIT] Die BF-Klasse hat keine __del__ Methode, deshalb sollte sie in der Liste nicht abräumbarer Objekte nur auftauchen, weil sie selbst referenziert wird. Dabei ist mit dieses erste Instanzobjekt verdächtig, das ich nicht zuordnen kann. Ich versuche gerade herauszufinden, was das für eine Instanz ist...[/EDIT]
Grüße,
Michael
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Benutzeravatar
Michael Schneider
User
Beiträge: 567
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Bremen
Kontaktdaten:

Dienstag 18. September 2007, 10:41

Moin!

Ich habe nochmal den Garbage-Inhalt nach einem collect ohne set_debug-Einstellung geprueft:
gc-check => checked 19533, garbage: 1612
<type 'instance'> BeautifulSoup.Tag
<type 'instance'> BeautifulSoup.Tag
<type 'instance'> BeautifulSoup.Tag
<type 'instance'> BeautifulSoup.Tag
...
Es stehen also AUSSCHLIEßLICH BF.Tag Instanzen in der Liste nicht referenzierter, nicht löschbarer Objekte. Nur warum? Wenn ich die Doku richtig gelesen habe, sollte der GC Cycles von Objekten ohne __del__-Methode aufbrechen können. Und die BF.Tag Objekte haben keine.

Hat jemand eine Idee, was hier schief läuft und/oder wie ich diese .Tag-Instanzen loswerde?

Grüße,
Michael
Zuletzt geändert von Michael Schneider am Dienstag 18. September 2007, 10:45, insgesamt 1-mal geändert.
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Benutzeravatar
Michael Schneider
User
Beiträge: 567
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Bremen
Kontaktdaten:

Dienstag 18. September 2007, 12:56

Hallo!

Ich habe die Lösung/den Fehler endlich gefunden:

Der zuerst analysierte Bericht kann aus mehreren Seiten bestehen, die in einer Schleife analysiert werden. Allerdings hatte ich nur das am Ende bestehende Objekt mit der oben erwähnten disjoint_soup Funktion zerlegt, statt jedes Objekt innerhalb der Schleife. Seit ich den Funktionsaufruf in die Schleife verlegt habe, bleibt nichts mehr übrig.

Immernoch nicht sicher bin ich mir über den Grund, warum und ob BeautifulSoup Konstrukte nun vom Garbage Collector zerlegt werden, oder nicht. Wenn ich einfach

Code: Alles auswählen

del Soup
eingebe, bekomme ich folgende Fehlermeldung:
Traceback (most recent call last):
File "./gctest.py", line 5, in ?
from troubleinfo import quick_info, get_props, troubles_of_day
File "./troubleinfo.py", line 213
del Soup
SyntaxError: can not delete variable 'Soup' referenced in nested scope
Grüße,
Michael
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Antworten