Regular Expressions scheitern bei Linefeed in Datei

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
tt-web
User
Beiträge: 23
Registriert: Dienstag 7. Juli 2015, 12:36

Guten Morgen,
ich versuche gerade einen einzelnen (veränderlichen) Wert aus einer ansonsten statischen Webseite auszulesen. Hierzu möchte ich regular expressions verwenden. Im Detail geht es um die Statusseite unseres Solarinverters von der ich die aktuellen Produktionszahlen auszulesen um sie weiter zu verwenden. Mein Problem ist, dass ich verschiedene Varianten an regulären Ausdrücken bereits auf https://regex101.com/ für Python bereits getestet habe und sie funktioniert haben.
Wenn ich das script aber bei mir ausprobiere kommt es zu keinem Match. Nachdem ich erstmal überprüft habe, dass die Webseite sauber eingelesen wird und das Matching mit "einfachen" pattern auch tatsächlich funktioniert, konnte ich einen "Schuldigen" ausmachen:

In der eingelesenen Datei kommt am Ende jeder Zeile ein Linefeed vor:

Code: Alles auswählen

<td width="100">
  Gesamtenergie</td>
<td width="70" align="right" bgcolor="#FFFFFF">
  37818</td>
Mein Pythonprogramm sieht so aus:

Code: Alles auswählen

import sys
import urllib
import re
                                                                                                                                                                                              
Webseite = "http://XXX:XXX@1?2.1??.1??.???"
pattern = r"Ge(samt)energie</td>"
                                                                                                                                                                                              
temp = urllib.urlopen(Webseite)                                                                                                                                                               
lines = temp.read()                                                                                                                                                                           
                                                                                                                                                                                              
result = re.search(pattern,lines)                                                                                                                                                             
                                                                                                                                                                                              
print result.group()
print result.group(1)
das Pattern r"Ge(samt)energie</td> ist erfolgreich. Sobald ich aber ein "\n" oder "\s" anhänge um mich an die Zahl "37818" vorzuarbeiten bekomme ich kein match mehr. Was mache ich falsch?

vielen Dank
thomas
BlackJack

@tt-web: HTML verarbeitet man besser nicht mit regulären Ausdrücken (alleine) sondern verwendet einen entsprechenden Parser. Gerade weil einem sonst kleine Änderungen am Quelltext die nicht einmal Auswirkungen auf die Anzeige haben müssen, sonst den Spass verderben.

`lxml.html` oder diese Bibliothek zusammen mit `BeautifulSoup` sind die üblichen Bibliotheken die man in Python für so etwas verwendet. Da würde ich die Zelle mit 'Gesamtenergie' suchen und von da aus dann die nächste Zelle und deren Inhalt.

Code: Alles auswählen

In [15]: import bs4

In [16]: import re

In [17]: source = '''\
    ...: <td width="100">
    ...:   Gesamtenergie</td>
    ...: <td width="70" align="right" bgcolor="#FFFFFF">
    ...:   37818</td>
    ...: '''

In [18]: soup = bs4.BeautifulSoup(source)

In [19]: soup.find('td', text=re.compile(r'\s*Gesamtenergie'))
Out[19]: 
<td width="100">
  Gesamtenergie</td>

In [20]: x = soup.find('td', text=re.compile(r'\s*Gesamtenergie'))

In [21]: x.find_next_sibling('td')
Out[21]: 
<td align="right" bgcolor="#FFFFFF" width="70">
  37818</td>

In [22]: x.find_next_sibling('td').text
Out[22]: u'\n  37818'

In [23]: int(x.find_next_sibling('td').text)
Out[23]: 37818
tt-web
User
Beiträge: 23
Registriert: Dienstag 7. Juli 2015, 12:36

Hallo,
danke für die antwort. leider ist beautiful soup so gar nicht mein ding, bzw. "neu" für mich.

Da es mir ja auch um den Lerneffekt geht werde ich mich da aber sofort einarbeiten.

allerdings... mein Problem besteht ja weiterhin. Ich arbeite seit langem mit regulären Ausdrücken und ich verstehe einfach nicht warum es nicht funktioniert.. das macht mir etwas Sorgen. an dem Quelltext wird sich nichts ändern, der ist fest im gerät verankert und nicht so leicht austauschbar. also wenn du noch nen tipp hast, was ich mit dem output anstellen kann, dass mein regex funktioniert dann nur her damit.

Ach ja... meine Shifttaste hängt wirklich :D

lg THomas
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dein Fehler ist nicht wirklich nachvollziehbar. Sowohl theoretisch (vom bloßen Anschauen des Patterns mit und ohne angehängtem "\n") als auch praktisch (Ausprobieren mit deinem o.g. Beispieltext) funktioniert es. Entweder du wendest ein anderes Pattern an oder du hast einen anderen Text.

Du hättest diese Probleme wahrscheinlich nicht in dieser Form, wenn du den Rat von BlackJack befolgen und BeautifulSoup einbinden würdest.
tt-web
User
Beiträge: 23
Registriert: Dienstag 7. Juli 2015, 12:36

ja... genau das ist es ja auch was mich so stutzig macht. Das Pattern ist exakt so, in allen regex-testern funktioniert es einwandfrei (hierzu habe ich die Webseite gespeichert, den inhalt kopiert und das Pattern darauf angewendet). Aber auf meinem Raspberry Pi mit aktuellem raspian (debian-derivat) funktioniert es nicht. Wie gesagt, durch entsprechende Printausgaben habe ich sicherstellen können, dass die Webseite tatsächlich eingelesen wird. Zum erweiterten Testen habe ich dann diesen einen Teil der mich interessiert mit "etwas drumrum" rauskopiert und eine Test.txt angelegt. Auch hier funktioniert die Ausgabe nicht wie gewünscht. Da ich immer mit copy-paste gearbeitet habe, kam ich auf die Idee mir mal die Datei genauer anzusehen und in Notepad++ habe ich mir mal die nicht-druckbaren Zeichen angeschaut : jede Zeile wird mit einem "lf" abgeschlossen.

Ich glaube, dass irgendetwas grundlegendes verkehrt ist....
Sirius3
User
Beiträge: 17738
Registriert: Sonntag 21. Oktober 2012, 17:20

@tt-web: genau den wichtigen Teil Deiner Frage hast Du weggelassen, nämlich den, wo es nicht mehr funktioniert. Wo hast Du ein '\n' oder '\s' angehängt, und wie willst Du Dich an die Zahl "37818" vorarbeiten?
tt-web
User
Beiträge: 23
Registriert: Dienstag 7. Juli 2015, 12:36

OHA! Das stimmt.. und ich habe geglaubt ich wäre schon ausführlich gewesen ... Asche über mein Haupt.

Also: das pattern

Code: Alles auswählen

pattern = r"Ge(samt)energie</td>"
liefert mir als Ergebnis in group(1) erwartungsgemäß "samt"

Da ich aber nach dem </td> ein linefeed habe, dann formatierungsanweisungen und dann erst der gewünschte Wert habe ich folgendes Pattern geschrieben:

Code: Alles auswählen

pattern = r"Gesamtenergie<td>\n.*\n\s(\d+)</td>
Dies hätte mir nur die Zahlen ausgeben sollen. das tut es auch wenn ich einen regextester drauf ansetze.. nur eben im "Live-Betrieb" nicht. Python liefert mir stur ein "none" bzw. nichts gefunden mit entsprechender Fehlermeldung zurück. Der "Fehler" tritt auf, sobald ich an an das Pattern ein \n anhänge. Also auch das nächste Beispiel funktioniert schon nicht mehr:

Code: Alles auswählen

pattern = r"Ge(samt)energie</td>\n"
(hier würde ich wieder "samt" als Ergebnis erwarten)

Ich habe auch schon \v bzw. \r\n ausprobiert. Ich finde es sehr sehr merkwürdig.

LG Thomas
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Was ergibt ``print("\r\n" in html)`` (wenn `html` der heruntergeladene Seitenquelltext ist)?
tt-web
User
Beiträge: 23
Registriert: Dienstag 7. Juli 2015, 12:36

Das gibt mir "True" aus.
Sirius3
User
Beiträge: 17738
Registriert: Sonntag 21. Oktober 2012, 17:20

@ tt-web: dein Problem ist, dass '.' keine Zeilenumbrüche matcht.

Code: Alles auswählen

re.search(r"Gesamtenergie</td>\n.*\s(\d+)</td>", www, re.DOTALL)
Dass da aber überhaupt der richtige Wert rauskommt, ist mehr oder weniger Zufall. Das ist nicht robust und fehlertolerant. Nimm einen HTML-Parser.
tt-web
User
Beiträge: 23
Registriert: Dienstag 7. Juli 2015, 12:36

Code: Alles auswählen

import sys
import urllib
import re
import bs4
                                                                                                                                                                                              
Webseite = "http://xxx.xxx.xxx.xxx"

temp = urllib.urlopen(Webseite)                                                                                                                                                               
temp2 = temp.read()                                                                                                                                                                           
                                                                                                                                                                                              
result = re.search(r"Gesamtenergie</td>\n.*\s(\d+)</td>",temp2 , re.DOTALL)
print result.group()
liefert mir:

Code: Alles auswählen

Traceback (most recent call last):                                                                                                                                                            
  File "Gesamtenergie.py", line 12, in <module>                                                                                                                                               
    print result.group()                                                                                                                                                                      
AttributeError: 'NoneType' object has no attribute 'group'  
...es findet also trotz DOTALL kein Match statt...
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Da ja offenbar auch "\r\n" und nicht "\n" das Zeilenende darstellt. Daher musst du dein Pattern anpassen.

Oder:
Sirius3 hat geschrieben:Nimm einen HTML-Parser.
BlackJack hat geschrieben:HTML verarbeitet man besser nicht mit regulären Ausdrücken (alleine) sondern verwendet einen entsprechenden Parser.
Und auch nochmal von mir: Wenn du reguläre Ausdrücke lernen willst, dann nimm besser nichts, wo reguläre Ausdrücke eher ungeeignet sind. Wir sprechen hier über Probleme, die du mit einem geeigneten Parser überhaupt nicht hättest... :K
tt-web
User
Beiträge: 23
Registriert: Dienstag 7. Juli 2015, 12:36

...Okay.. ich glaube ihr habt recht. normalerweise habe ich mit regulären Ausdrücken nur gute Erfahrungen gemacht. dann muss ich mich jetzt eben mal in die diversen Parser einarbeiten und rausfinden was die überhaupt machen. Trotzdem vielen herzlichen dank, ich habe so einiges gelernt, auch wenn ich nicht an mein Ziel gekommen bin.
:P
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Wenn du das API des Parsers deiner Wahl erlernt hast, dann hilft dir das ja auch bei ähnlich Aufgaben. Es ist eben nicht hilfreich, bei Textverarbeitung immer alles mit regulären Ausdrücken erschlagen zu wollen. :wink:
Antworten