Steuerzeichen und HTML aus einer Textdatei eintfernen

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.
Antworten
Jess
User
Beiträge: 7
Registriert: Freitag 21. September 2007, 17:15

Hallo,

für einen Kurs soll ich einen (einfachen) Spam Filter schreiben (benotet wird die Idee, nicht der Filter/Quellcode an sich). Als Ratschlag bekamen wir entweder mit Python und dem nltk oder mit Weka einen Filter zu erstellen.
Ich habe mich für Python entschieden, obwohl ich diese Sprache nochnicht konnte.
Nun habe ich ein Problem, bei dem mir leider weder google noch die Suche hier richtig helfen konnten.
Mein Testcorpus beinhaltet u.a. Spam Mails (als Textdateien), diese möchte ich parsen und alle irrelevanten Sachen (html code, steuerzeichen usw) rausschmeißen und die Wörter mit stemming in ihre "Grundform" bringen.
Mein Problem ist nun, daß ich es nicht schaffe, einen Regulären Ausdruck zu schreiben, der HTML und Steuerzeichen entfernt.
Es gibt hier im Forum zwar reguläre Ausdrücke für das Entfernen von HTML Tags, aber wenn ich diese in meinen Code einbaue, ist in meiner Ausgabedatei fast nichts mehr drin.
Auch nicht die Testsätze, die ich extra reingeschrieben habe, um zu schauen, ob sie noch da sind.

Mit HTML Tags könnte ich ja noch leben, aber die Steuerzeichen sind ein richtiges Problem für mich.
Wie kann ich diese denn entfernen?
In Perl schafft es wohl der Ausdruck [:ascii:], gibt es da in Python was Ähnliches?

Ich hoffe, ich habe jetzt nicht zuviel und zu verwirrend geschrieben, aber ich habe mittlerweile einen Knoten im Hirn, da ich schon seit Stunden versuche das Problem zu lösen.

Vielleicht hat hier ja jemand eine Idee.

Liebe Grüße,
Jessica
BlackJack

Ich würde als erstes mal mit dem was das `email`-Package aus der Standardbibliothek so bietet, die Textteile aus einer E-Mail auslesen.

Dann ist das entfernen von HTML-Markup mittels regulärem Ausdruck eigentlich noch recht leicht zu machen. Verwende mal ein Programm wie Kodos um Deinen Ausdruck mit Deinen Texten zu testen.

Die Texte selbst müsste man dann wohl in Unicode umwandeln wenn man mit Buchstaben statt Bytes operieren will und dort lässt sich vielleicht die Kategorie der Zeichen benutzen um unerwünschtes aus zu filtern → Modul `unicodedata`.
Jess
User
Beiträge: 7
Registriert: Freitag 21. September 2007, 17:15

Danke für den Tipp.
Leider hilft mir das "email" Package nicht weiter ;(
Ich umgehe das Problem mittlerweile indem ich die Datei für die Spammails mit einem Perl Script "säubere".
Mein nächstes Problem ist nun, daß ich Wortwahrschenlichkeiten in einem Dictionary zähle, aber da das Dictionary wohl im cache gehalten wird kann ich leider nicht gleichzeitig 2 Dicitionarys halten, was ich für meine Grundidee eigentlich brauchte...
Wenn ich versuche 2 Dictionary zu erzeugen bekomme ich den Fehler "MemoryError".
Verstehe ich das nun richtig, daß der Platz im Speicher nicht ausreicht?

Liebe Grüße,
Jessica
merlin_emrys
User
Beiträge: 110
Registriert: Freitag 3. März 2006, 09:47

Jess hat geschrieben: Wenn ich versuche 2 Dictionary zu erzeugen bekomme ich den Fehler "MemoryError".
Verstehe ich das nun richtig, daß der Platz im Speicher nicht ausreicht?
Das kann ich mir nicht so recht vorstellen. Ich bearbeite teilweise (für mein Gefühl) ziemlich große Textmengen mit Python, und es hat noch keine Speicherplatzprobleme gegeben.

Kannst Du mal Deinen Quelltext und eine Kopie der Fehlermeldung posten? Vielleicht ergibt sich daraus, wo das Problem ist.
Jess
User
Beiträge: 7
Registriert: Freitag 21. September 2007, 17:15

Ich benutze ja das Dictionary im darin Wörter und ihre Vorkommenshäufigkeit in einer Datei zu speichern.
In meiner ersten Datei stehen 7.590.856 Wörter, wenn man nur die Wörter zählt, ohne ihre mehrfachvorkommen mitzuzählen sind das in diesem fall 137.425 (soviele Einträge sind das dann ja auch im Dictionary, wobei ich jetzt auchschon die Wörter die weniger als 10 mal vorkommen rausgefiltert habe).
In der zweiten Datei stehen 4.734.680 Wörter und 112.288 verschiedene (wieder die Anzahl der Wörter im Dictionary).

Der Code der mir ein Dictionary erstellt, sieht so aus:

Code: Alles auswählen

import re
import pickle
from nltk.stem.porter import PorterStemmer

f_out= file('spam_count2.txt','w')
f_in = 'spam_full.txt'

word_list = re.split('\s+', file(f_in).read().lower()) 		#zerhackt den string und macht alles kleingeschrieben

zeichen = re.compile(r'[(<.*?>), (0-9), (:*), (--*)/*"*=]')  #regulaerer ausdruck der sagt, was entfernt werden soll
zeichen2 = re.compile(r'[\W)]')
print 'Words in text:', len(word_list) 			#gibt die anzahl der woerter aus

freq_dic = {} 				#erzeugt ein leeres dictionary
#n= 0
for word in word_list:
	#n = n+1
	#if n > 1000:
	#	break	
	word = zeichen.sub('', word)	# solange es woerter in der liste gibt, entferne die gefundenen zeichen
	word = zeichen2.sub('', word)
	PorterStemmer().stem_word(word)
	if len(word) < 20:
		try:
			freq_dic[word] += 1    # schreibt die woerter ins dictionary, wenn schon vorhanden
		except:
			freq_dic[word] = 1	# schreibt woerter die nochnicht vorhanden
	

	
print 'Unique words:', len(freq_dic)

decorated=[]
for key, value in freq_dic.items():
    decorated.append((value, key))
decorated.sort()
decorated.reverse()

for value, key in decorated:
    if value<10:
             del(value, key)
    	#print value, key
    else:
    	s = '%4.4i:\t%s\n' %(value, key)
    	f_out.write(s)
f_out.close()
hmmm...
wie kann man den code eingerückt darstellen?

Um nun 2 Dictionarys zu erstellen habe ich praktisch den code nochmal hintendran kopiert.
Die Fehlermeldung war nur "MemoryError".

Jess

Edit by gerold: BBCode aktiviert

Danke Dir gerold!
merlin_emrys
User
Beiträge: 110
Registriert: Freitag 3. März 2006, 09:47

Jess hat geschrieben:
In meiner ersten Datei stehen 7.590.856 Wörter, ...
Okay, das ist geringfügig länger als die Dateien, die ich normalerweise handhabe... :-o :-o :-o

Hast Du mal versucht, was passiert, wenn Du wieder nur die je ersten 1000 Einträge einliest?
Jess
User
Beiträge: 7
Registriert: Freitag 21. September 2007, 17:15

Dann bekomme ich noch die Wortanzahl im 1. Dokument und die Anzahl voneinander unterschiedlicher Wörter und dann kommt "Killed"

Jess
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Jetzt gebe ich den Tipp für den glaube ich gerold normalerweise zuständig ist: versuch es doch mal mit Shelve, das muss nicht komplett im Speicher gehalten werden.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

@Jess: Du wirst da letztendlich viel zu viel und zu oft im Speicher halten.

Zeile 9 liest die komplette Datei in den Speicher und braucht dann gleich nochmal soviel Platz um eine Kopie bloss mit Kleinbuchstaben zu halten. Die Grossbuchtaben-Version wird dann wieder verworfen. Dann wird eine Liste mit den ganzen Worten angelegt. Die bleibt dann für den Rest des Programms belegt.

Dann werden die bereinigten und "ge-stem-mten" Wörter die kürzer als 20 Zeichen sind erzeugt und in das Dictionary gespeichert.

Ab Zeile 34 wird eine Liste mit den Einträgen erzeugt, die auch nochmal Speicher verbraucht. Das ist übrigens etwas umständlicher als es sein müsste. Du könntest gleich ``decorated = freq_dic.items()`` schreiben und dann bei der `sort()`-Methode eine entsprechende `key`-Funktion angeben und das `reverse`-Argument.

Zeile 42 macht sicher nicht das was Du denkst was sie tut. Dort werden die *Namen* `value` und `key` aus dem Namensraum gelöscht und nicht etwa die Objekte die daran gebunden sind. Da die unter anderem in der Liste stecken, die an `decorated` gebunden ist, bestehen die natürlich auch noch weiterhin.

Wenn Du einfach per copy'n'paste den Quelltext runterkopierst und die Namen ein wenig veränderst, dann wird auch für die zweite Datei nochmal der ganze Speicher belegt.

Zwei Verbesserungsvorschläge:

1. Den Code in Funktionen stecken, dann bleiben keine "Datenleichen" auf Modulebene weil lokale Namen nach Ablauf der Funktion verschwinden.

2. Nicht die ganzen Dateien auf einmal in den Speicher laden, sondern zeilenweise verarbeiten.
Jess
User
Beiträge: 7
Registriert: Freitag 21. September 2007, 17:15

Danke für die vielen Tipps :O)

Darüber ein shelve zu nutzen habe ich auch schon nachgedacht, es dann aber verworfen.
Jetzt wollte ich es nochmal probieren, aber schon beim "Erstellen" des shelves bekomme ich eine Fehlermeldung :(
d = shelve.open("dateiname.slv")
AttributeError: 'module' object has no attribute 'open'

Was ist denn da falsch?
import shelve habe ich auch in meinem Code...
Und in jedem Beispiel, das ich im Netz gefunden habe, steht das so drin...
Wenn ich das shelve jemals erstellen kann, kann ich es dann genauso füllen wie mein Dictionary? So habe ich das bisher in allen Beschreibungen gelesen...

@Blackjack: ich versuche Deine Ratschläge umzusetzen, danke :)
BlackJack

Nur mal so geraten: Du hast ein `shelve.py` in dem Verzeichnis? Dann wird *das* importiert, anstelle des `shelve`-Moduls aus der Standardbibliothek.
Jess
User
Beiträge: 7
Registriert: Freitag 21. September 2007, 17:15

Gut geraten :oops:
Danke
Jess
User
Beiträge: 7
Registriert: Freitag 21. September 2007, 17:15

So, nochmals vielen Dank an alle hier.
Das Projekt ist in soweit beendet, daß der Spamfilter fertig ist und nun ausgewertet wird.

Jess
Antworten