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
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

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: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

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

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: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

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: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

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: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

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