Text-Variable ist nach jedem Schleifenfurchgang leer?

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
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

Hallo Leute,

zuallererst: ich bin totaler Neuling in Python. Programmiere zwar gut in VB 6 (ja, ich gehöre zum 'alten Eisen'), halbwegs gut in VB.NET (Umsteiger aber in VB6 fühle ich mich 'sicher'), PHP und SQL (da bin ich gar nicht mal schlecht). Aber Python ist Neuland für mich.

So, nun das eigentliche Problem:

Ich parse/scrape eine Internetseite (mit BeautifulSoup-Bibliothek) und möchte mir einen speziellen Abschnitt (reiner Text) in meine mySQL-DB inserten. Ich möchte aber nicht den ganzen Text (es gibt viele Leerzeilen dazwischen aber auch anderes Zeug) sondern nur bestimmte Zeilen die ich in einer Schleife abfrage. Wenn die betreffenden Zeilen gefunden sind, möchte ich in eine Text-Variable (nennen wir sie 'content') hinzufügen.
Am Ende der Schleife sollte der 'content' in die DB insertet werden.

Ich sammle in die content-Variable indem ich einfach content += neueZeile benutze.
Jetzt das 'komische': die content-Variable ist bei jedem Schleifendurchgang erst einmal leer obwohl ich nur vor der Schleife ein content = "" stehen habe. In der Schleife (welche ja den geparsten/gescrapten Inhalt in mehreren Zeilen beinhaltet) wird kontinuirlich hinzugefügt.

Ich gebe die Zeile testweise auch aus und in der Ausgabe sieht es dann richtig aus. Wenn ich am Ende die content-Variable ausgebe, habe ich lediglich dieletzte Zeile drinnen.

Was mache ich falsch? Der content-Variable kann ich innerhalb des Schleifendurchganges auch neuen Text hinzufügen (habe ich probeweise gemacht und es hat funktioniert), aber sobald ein neuer Schleifendurchgang gemacht wird ist sie plötzlich leer?!?

Ich danke euch schon einmal im Voraus für eure Hilfe!
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo und willkommen im Forum!

Manchmal sagt Code mehr als tausend Worte ;-) Kürze dein Programm mal auf ein minimales lauffähiges Beispiel, welches den Fehler noch produziert und poste es hier. Wahrscheinlich findest du das Problem dabei schon selbst.
Das Leben ist wie ein Tennisball.
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

So, dies ist mein (recht leeres) Modul welches diese Aufgabe übernehmen sollte die Leerzeilen rauszufiltern und später noch diverse andere Dinge auch.
Der 1. Paramter 'temp' beinhaltet den kompletten Block an Text der extrahiert wurde und durch BeutifulSoup von HTML-Tags befreit wurde.
Der 2. Parameter 'detei' ist der Dateiname in welche der fertige Block geschrieben werden soll zu testzwecken. Wenn alles steht entfällt dieser Teil weil ich es ja in die DB jagen will.

Jeder Block hat in der letzten Zeile ein immer gleichbleibenden Text 'Blockende'. Denn frage ich ab und weiß dann wann die letzte Zeile erreicht wurde und schreibe alles in die Datei.
Natürlich könnte ich mir das sparen (sehe ich gerade) weil ich das auch ohne Abfrage nach der Schleife erledigen könnte. Ursprünglich war aber gedacht das ich in der übergebenen Variable mehrere Blöcke habe und jedes mal beim erreichen eines Blockendes dies wissen wollte. Das hat sich aber erledigt da ich jetzt die Blöche einzeln an diese Funktion übergebe. Und zu dem aktuellen Problem spielt dies ja auch keine Rolle denke ich.

Code: Alles auswählen

def analyse(temp, datei):
	if(datei!=""):
		lines = temp.splitlines()
		content = ""
		for line in lines:
			text = line.strip()
			if(text.strip()!=""):
				content += text
				if(text == "Blockende"):
					fobj_out = open(datei,"a")
					fobj_out.write(content)
					fobj_out.close()
BlackJack

@kaineanung: Das ist ein extrem komischer Programmablauf, aber ich sehe nicht wo `content` jetzt wieder an eine leere Zeichenkette gebunden wird‽ Im Gegenteil wird wahrscheinlich zu viel in die Datei geschrieben wenn 'Blockende' mehr als einmal in den Zeilen vorkommt.

``if`` ist keine Funktion. Die Klammern um die Testausdrücke gehören da nicht hin.

Eingerückt wird per Konvention vier Leerzeichen pro Ebene.

`temp` ist ein schlechter Name für ein Argument. Das ist ja schon ein schlechter Name für einen lokalen Namen bei dem einem nichts besseres eingefallen ist. `datei` ist ein schlechter Name für einen Datei*namen*.

Wiederholtes ``+=`` mit Zeichenketten in einer Schleife ist potentiell sehr ineffizient. Ich sehe auch nicht warum das nötig ist, denn man könnte die Daten auch direkt in die Datei schreiben. Oder soll nichts geschrieben werden wenn 'Blockende' nicht in den Daten vorkommt? In dem Fall würde man die Daten besser in einer Liste sammeln und die dann mit `writelines()` schreiben. Da fällt mir auf: Dir ist klar das alle Zeilenendenzeichen entfernt wurden? Und das ``text.strip()`` ziemlich überflüssig ist wenn `text` durch ``line.strip()`` entstanden ist?
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

@BlackJack

In meinem Ultraedit sind auch 4 Leerzeichen / Tab-Einrückung eingestellt. Im Editor wird das auch eingehalten. Keinen Plan warum beim c&p hier nun 8 Zeichen eingerückt werden. Ich habe es bereits abgesendet als ich es bemerkt hatte.

Ich dateche in der IF-Abfrage kann man grundsätzlich Klammern setzen, egal ob eine Funktion im Spiel ist?
Aber wenn das zum schlechten Stil gehört: lass ich dann bleiben. So war es halt einfacher.

Die Argumentbenennung ist momentan auch nur vorübergehend. Ich benutze irgendwie immer temp, datei und der Gleichen wenn ich schnell was 'zusammenschustere'. Wenn der kleine Abschnitt dann gut klappt, dann gehe ich den Code nochmals durch und vergebe passende Namen. Dies mache ich auch nur in kleinen Code-Schnipseln bei kleinen 'Hilfsfunktionen' so. Sorry, um des besseren Lesens werde ich dies auch unterbinden.

Ich summiere die Zeile doch in jedem Schleifendurchgang in den Content mit '+='. Daher verstehe ich nun die Frage nicht nach dem 'wo die Zeichenkette jedes mal gebunden wird'?
Ich habe irgendwo gelesen (wahrscheinlich ein Tutorial) das in Python Zeichenketten nicht veränderbar sind. Meinst du das vielleicht damit? Aber wenn ich content += "a" und dannch content += "b" mache, dann klappt das ja auch und im Speicher wird halt eine neue Sequenz erstellt....
Und das Blockende kommt definitiv nur einmal vor. Ist auch egal, denn der Content beinhaltet ja nur die letzte Zeile und somit liegt das Problem nicht an dieser Stelle sondern eher am 'content += text'

Was die Effizienz angeht mit dem '+=': mag sein, aber in diesem Falle (maximal 8-12 Blöcke mit jeweils 8-10 Zeilen / Block) kann man das vernachlässigen.
Ich werde es auch andersherum probieren (wie du es vorgeschlagen hast), dennoch würdem ich jetzt brennend interessieren warum mein Content jedesmal leer ist wenn ein neuer Schleifendurchgang gestartet wird....
Übrigens: ich schreibe nur in die Datei 'testweise' um das Resultat sehen zu können ob der content angesammelt hat. Der Content wird, wenn ich das Problem am Ende gelöst habe, anstatt in eine Datei eben in die DB geschrieben. Daher störe dich nun nicht daran das die datei-variable 'datei' heisst und das ich überhaupt an der Stelle in eine Datei schreibe.

Wichtig ist das der content gefüllt wird und das tut er nicht.

Text.strip habe ich mittlerweile entfernt. Ist unsinng da doppelt gemoppelt.

Was das sammeln einer 'Liste' angeht: ich bin ein wenig verwirrt was das angeht. Ich habe in VB6 (und auch anderen Sprachen) entweder ein Array oder gar nichts. In Python kommen Sets, Listen und Tupel hinzu. Neuland für mich und daher weiß ich nicht so Recht was was ist, wann was veränderbar ist und wann nicht und wozu überhaupt und der Gleichen.
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

Hier der 'schönere' Codeschnipsel:

Code: Alles auswählen

import string

def analyse(textblock, testdatei):
	if testdatei!="":
		lines = textblock.splitlines()
		content = ""
		for line in lines:
			text = line.strip()
			if text!="":
				content += text
				if text == "weiterlesen":
					fobj_out = open(testdatei,"a")
					fobj_out.write(content)
					fobj_out.close()
P.S. jetzt schon wieder: im UltraEdit SIND 4 Stellen als Tab-Einrückung eingestellt und im Editor wird es mir auch richtig angezeigt?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Es geht nicht darum, wie breit du einen Tab bei dir im Editor eingestellt hast. Das ist für das Forum vollkommen egal. Du musst deinen Editor so einstellen, dass er statt eines Tabs vier Leerzeichen einfügt.

Zum Problem mit dem Leeren content: Wie sieht der Rest deines Programms aus? In dem von dir gezeigten Code kann das Problem nicht auftreten.
Das Leben ist wie ein Tennisball.
BlackJack

@kaineanung: So wie Du die ``if``\s mit Klammern geschrieben hast sieht das aus als wäre ``if`` eine Funktion die aufgerufen wird. Das ist irreführend. Natürlich kann man um Ausdrücke soviele unnötige Klammern setzen wie man möchte, aber warum sollte man? Und was ist daran einfacher als einfach nur den Ausdruck ohne Klammern hin zu schreiben?

Erst irgendwelche Namen wählen und die dann später noch mal sinnvoll umbenennen klappt erfahrungsgemäss nicht wirklich gut. Wenn der Code dann endlich läuft bleiben die schlechten Namen dann doch oft drin weil das umbenennen scheinbar unnötige Arbeit ist.

Es wird keine Zeichenkette gebunden sondern `content` wird ja gerade *nicht* an die *leere* Zeichenkette gebunden innerhalb der Schleife, deshalb kann das von Dir beschreibene Verhalten *nicht* auftreten. Das heisst das ist entweder nicht der Quelltext den Du *tatsächlich* ausführst, oder aber der Fehler liegt nicht in dieser Funktion.

Irreführende Namen sind auch in Fällen störend bei denen da nur zu Testzwecken etwas mit gemacht wird. Ein Name weckt Erwartungen — wenn ich `datei` lese, erwarte ich dass das auch tatsächlich eine Datei ist und keine Zeichenkette. Und an *der* Stelle würde man auch nicht in die Datenbank schreiben.

Der `content` *wird* gefüllt. Kann es sein das Du erwartest das da Zeilenenden drin sind wenn das in die Datei geschrieben wird? Wie schon gesagt: Die entfernst Du alle — das ist eine mehr oder weniger lange Zeichenkette ohne Zeilenendezeichen.

Bei VB6 glaube ich die Einschränkung bei den Datentypen ja noch, aber spätestens bei VB.NET hat man die .NET-Standardbibliothek und damit auch den üblichen Zoo an Containertypen in modernen Programmiersprachen unter `System.Collections`. Listen in Python entsprechen wohl am ehesten `System.Collections.ArrayList`. In PHP muss das ”Array” für alle möglichen Containertypen herhalten — je nachdem wie man es benutzt verhält es sich entsprechend. Auf jeden Fall muss man in Python mit Listen, Wörterbüchern, Tupeln, und Iteratoren/iterierbaren Objekten umgehen können weil die einem in jedem Programm über den Weg laufen das aus mehr als ``print('Hallo')`` besteht.

Zurück zum Code: Das erste ``if`` in `analyse()` gehört da IMHO nicht hin. Wenn man keinen Dateinamen hat, dann sollte man die Funktion gar nicht erst aufrufen.

Wenn man Dateien zusammen mit der ``with``-Anweisung öffnet werden sie sicher geschlossen wenn der ``with``-Block verlassen wird.

Die Funktion würde in Python eher so aussehen:

Code: Alles auswählen

def analyse(textblock, filename):
    content = list()
    for line in textblock.splitlines():
        line = line.strip()
        if line:
            content.append(line)
            if line == 'weiterlesen':
                break
    with open(filename, 'a') as out_file:
        out_file.write(''.join(content))
Falls 'weiterlesen' im HTML ein Link war, dann war das entfernen der HTML-Tags wahrscheinlich einen Tick zu früh weil man damit Informationen wegwirft an denen man sich orientieren kann.
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

@all

Natürlich habt ihr Recht. Ich bin einfach noch zu unsicher auf den Beinen in Python.

In der aufrufenden Umgebung habe ich den Fehler gefunden. Ich habe dort eine Liste die ich befülle mit den Zeilen (alle, also ungefiltert mit Leerzeilen).
Und anstatt die Liste bzw. ein zu einem String konvertierten Text zu übergeben, übergebe ich die einzelnen Zeilen dort.
Somit ist klar das meine 'content'-Variable jedesmal leer ist und nur mit der einen Zeile befüllt wird. Am Ende ist dann auch nur diese Zeile in der Datei und zwar die letzte Zeile (ich lösche die Datei vor betreffender Stelle des Funktionsaufrufes).

Danke für eure Mühe. Ich hätte mich 'wer-weis-wie-lange' an dieser Stelle verrant ohne euch.

Entschuldigung
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Du musst dich doch für nichts entschuldigen. Ist doch wunderbar, wenn du deinen Fehler mit einem kleinen Denkanstoß selbst findest. Die Hinweise von BlackJack solltest du möglichst so umsetzen und daraus lernen und nicht als Kritik an deiner Person verstehen. Am Anfang wirst du natürlich viele Fehler machen und der Ton ist hier meist recht sachlich, aber das solltest du nicht auf dich beziehen. Das ist eher dem Medium, dem Thema und der Zeit geschuldet.
Das Leben ist wie ein Tennisball.
kaineanung
User
Beiträge: 145
Registriert: Sonntag 5. April 2015, 20:57

@EyDu

Ich wolltem ich dafür entschuldigen das es euch Zeit gekostet hat mir zu helfen obwohl ich euch auf die falsche Fährte gelockt hatte und der Fehler gar nicht in der angegebenen Funktion lag sondern schon davor. Das hätte ich selber wissen sollen bevor ich jemanden frage....

Und was Kritik angeht: immer her damit wenn mich das weiterhilft :wink:

Und ich habe schon bemerkt das BlackJack der Python-Guru ist und selbstverständlich werde ich seine Tips 1:1 umsetzen (habe ich natürlich schon gemacht) :D

Danke
Antworten