html parser

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.
INFACT
User
Beiträge: 385
Registriert: Freitag 5. Dezember 2008, 16:08

Sehr geehrtes Python Forum,

Code: Alles auswählen

import sys
text = """<b>buffer</b>
<form>
lalala
lululu
</form>
buffer
<form>
buffer
</form>
alla"""

istrue = False

liste = []

form1 = ""
for el in xrange(len(text)):
    if istrue:
        if text[el] == "<":
            if text[el + 1] == "/" and text[el + 2] == "f" and text[el + 3] == "o" and text[el + 4] == "r" \
                        and text[el + 5] == "m" and text[el + 6] == ">":
                            form1 = form1 + "</form>"
                            istrue = False
                            liste.append(form1)
                            form1 = ""
                            continue
                            
        form1 = form1 + text[el]
    if text[el] == "<":
        if text[el + 1] == "f" and text[el + 2] == "o" and text[el + 3] == "r" and text[el + 4] == "m":
            istrue = True
            form1 = form1 + "<"
Das ist der hässlichste Code den ich je geschrieben habe - ich weiß.
Aber er funktioniert. :roll:

Da dieser Code so schwer lesbar ist schreibe ich mal die Ausgabe hin:

Code: Alles auswählen

>>> liste
['<form>\nlalala\nlululu\n</form>', '<form>\nbuffer\n</form>']
Ich brauche etwas, wie oben, dass den Ihnalt einzelner htmltags auslesen kann, ich habe es ja schon geschafft, aber da der Code so ekelhaft ist frage ich euch mal ob das irgentwie schöner ( Mal vom Pep8 abgesehen ) machen kann.

Geht das?

MfG INFACT

EDIT: Code aufgebessert...
Zuletzt geändert von INFACT am Samstag 2. Mai 2009, 16:08, insgesamt 3-mal geändert.
[b][i]ein kleines game für die die lust haben http://konaminut.mybrute.com[/i][/b]
;-)
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Hmm .. Phraser ... nette Wortschöpfung ;) Was du suchst ist ein HTML - PARSER.

Beautiful Soup, lxml.html, ... da gibts viele mit denen du glücklicher wirst.
BlackJack

Argh! Ja das geht schöner -- in dem man einen HTML-Parser nimmt. So heisst das übrigens auch: Parser und nicht Phraser. ;-)

Ansonsten gibt's auch so schöne Methode wie `str.find()`, die man sich nicht mit aufwändigen Schleifen selber basteln muss.
vario
User
Beiträge: 4
Registriert: Freitag 24. Oktober 2008, 15:43

Wie wäre es mit regular expressions? Damit könntest Du nach

<deintag> text text text ... </deintag>

direkt suchen, dir die einzelne Teile ausgeben lassen, etc. Schau mal hier:

http://www.amk.ca/python/howto/regex/ und hier http://docs.python.org/library/re.html

Ansonsten gibt es für HTML bestimmt auch schon etwas.
INFACT
User
Beiträge: 385
Registriert: Freitag 5. Dezember 2008, 16:08

Etwas in der Art?

Code: Alles auswählen

>>> l = re.compile("<form>*</form>")
>>> l.findall(text)
[]
Das Sternchen würde, dann alles bedeuten können...

CAUTION: der Code klappt nicht
[b][i]ein kleines game für die die lust haben http://konaminut.mybrute.com[/i][/b]
;-)
INFACT
User
Beiträge: 385
Registriert: Freitag 5. Dezember 2008, 16:08

Code: Alles auswählen

>>> l = re.compile("<form>[\d\s\w]</form>")
>>> l.findall(text)
Der Code würde klappen wenn zwischen <form> und </form> nur 1 char wäre...
[b][i]ein kleines game für die die lust haben http://konaminut.mybrute.com[/i][/b]
;-)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

HTML mit Regular Expressions zu parsen ist auch sinnlos, was du suchst ist ein Parser. Wie schon die Vorposter gemeint haben.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

INFACT hat geschrieben:

Code: Alles auswählen

if text[el + 1] == "/" and text[el + 2] == "f" and text[el + 3] == "o" and text[el + 4] == "r" \
     and text[el + 5] == "m" and text[el + 6] == ">":
Btw die `text[:6]`-Notation kennst du?

Code: Alles auswählen

if text[:6] == '<form>':
    ....
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Oder das:

Code: Alles auswählen

text.startswith('<form>')
Hast du eigentlich jemals in Pythontutorial durchgemacht?
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Ein einfacher re zum Parsen von HTML:

Code: Alles auswählen

In [16]: a = re.compile(r"(<(?P<tag>[^/]+?)>(?P<inhalt>.*)</(?P=tag)>)+", re.MULTILINE)

In [17]: c = a.search(text)

In [18]: c
Out[18]: <_sre.SRE_Match object at 0x9f97660>

In [19]: c.groups()
Out[19]: ('<b>buffer</b>', 'b', 'buffer')
Strings kann man wie Listen behandeln, siehe Slice Notation (zb in "A Byte of Python" nachschlagen, du verstehst ja kein Englisch). Du könntest auch den Text mit Methoden wie startswith(), find usw in immer kleinere in (logische) Einheiten zerteilen.

Übringens: Du bist ziemlich hartnäckig. Dein Code wird sieht auch deutlich besser aus als noch vor zwei Monaten, auch wenn du bestimmte (doch sehr grundlegende Dinge) wie zb eben dieses "slicen" nicht kennst und dir die Erfahrung im Herangang an Probleme fehlt. Wenn du jetzt noch dein Englisch verbesserst, damit du zb die offizielle Dokumentation lesen kannst, kommt das schon mit der Zeit.

EDIT: Hab MULTILINE mit DOTALL verwechselt:

Code: Alles auswählen

In [31]: a = re.compile(r"(<(?P<tag>[^/]+?)>(?P<inhalt>.*?)</(?P=tag)>)+", re.DOTALL)

In [32]: for m in a.finditer(text):
    print m.groups()
   ....:     
   ....:     
('<b>buffer</b>', 'b', 'buffer')
('<form>\nlalala\nlululu\n</form>', 'form', '\nlalala\nlululu\n')
('<form>\nbuffer\n</form>', 'form', '\nbuffer\n')
lunar

Code: Alles auswählen

from __future__ import print_function, division

import lxml.html

text = """\
<b>buffer</b>
<form>
lalala
lululu
</form>
buffer
<form>
buffer
</form>
alla"""

tree = lxml.html.fromstring(text)
for form in tree.findall('form'):
    print(form.text_content().strip())
Man parst HTML nicht mir regulären Ausdrücken!
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Man parst HTML nicht mir regulären Ausdrücken!
In richtigem Code natürlich nicht. Mir scheint aber das INFACT nicht ernsthaft HTML parsen will, sondern sich nur ein wenig an dererlei Dingen versuchen will. Einen (sehr kleinen und rudimentären) HTML Parser zu erstellen, der sehr ausdrucksschwach und fehleranfällig ist, ist relativ leicht und mag eine nette Übung sein. Sein ursprünglichen Code zeigt imho doch schon, das er nicht wirklich Seiten damit auslesen will.
lunar

Ich denke, du urteilst da etwas vorschnell, was sagt dir, dass das nicht einfach nur Beispielcode ist?
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Nichts, aber es wurde ja schon auf richtige Parser hingewiesen, und darauf, das man RE's dafür nicht nehmen sollte - würde er bei dem ernsthaftem Parsen von HTML trotz Hinweisen auf entsprechende Parser zwanghaft etwas mit regulären Ausdrücken zusammenbauen wollen, wäre er schlicht selbst schuld, denn man erkennt schnell, wie einfach einem die Parser das Parsen machen, Suchfunktion hier im Forum reicht ja schon. Hätte er dabei Hilfe gebraucht, hätte er dann nochmal fragen können. Aber gut, ich hätte "Bei ernsthaften Angelegenheiten: Kein HTML mit RE parsen" nochmal mit Nachdruck unter mein Posting setzen können.
INFACT
User
Beiträge: 385
Registriert: Freitag 5. Dezember 2008, 16:08

Okay, ich kann lesen... :o
Ich habe jetzt 500 mal versucht lxml zu installieren...
... diesmal hat es geklappt, aber wenn ich das auführe tritt jedes mal ein Fehler auf, in io.py, http://paste.pocoo.org/show/115446/

Code: Alles auswählen

>>> import lxml.html

Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
    import lxml.html
  File "C:\Python\Lib\site-packages\lxml\html\__init__.py", line 12, in <module>
    from lxml import etree
  File "lxml.etree.pyx", line 34, in lxml.etree (src/lxml/lxml.etree.c:144634)
  File "C:\Python\lib\io.py", line 502
    n = (readahead.find(b"\n") + 1) or len(readahead)
                            ^
SyntaxError: invalid syntax
Ich habe keine Ahnung was da stattdessen stehen soll...
weiß jemand was ich machen muss, damit das klappt
[b][i]ein kleines game für die die lust haben http://konaminut.mybrute.com[/i][/b]
;-)
OverNord
User
Beiträge: 72
Registriert: Donnerstag 24. Januar 2008, 11:59
Kontaktdaten:

Der Traceback sieht so aus, als ob du lxml für Python > 2.5 installiert hast, und Python 2.5 verwendest, b"" funktioniert erst ab Python 2.6, darunter erzeugt es einen SyntaxError.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Die Frage ist, warum überhaupt io.py mitspielt. Das ist in 2.6 zwar vorhanden, aber wird nicht für normales File-I/O verwendet.

@OP: Hast du vielleicht Python 2 und 3 vermischt?
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Wenn man will, kann man natürlich auch XHTML mit regulären Ausdrücken parsen, denn so schwer ist das nicht. Hier habe ich einen Parser für ein Subset von XHTML in ~25 Zeilen Python (Funktion `Html`). Was noch fehlt, ist das Auswerten von Entities. Das war mir zu mühsam. Hier ist ein Vorschlag:

Code: Alles auswählen

ENT = re.compile(r'&(?:(\w+)|#([0-9]+)|#x([a-fA-F0-9]+));')
PREDEF = {'quot': '"', 'amp': '&', 'apos': "'", 'lt': '<', 'gt': '>'}

def parse_entities(text):
    def parse(m):
        if m.group(1): return PREDEF[m.group(1)]
        if m.group(2): return unichr(int(m.group(2), 10))
        if m.group(3): return unichr(int(m.group(3), 16))
    return ENT.sub(parse, text)
Stefan
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

@Stefan: Wenn du eine Ausnahme auslöst ohne ihr Argumente zu übergeben, solltest du trotzdem erst ein Exemplar ;) erstellen. In deinem Beispiel wird die Klasse "ausgelöst". Das funktioniert zwar trotzdem (es wird automatisch ein Exemplar erstellt), aber das ist dann doch magisches Verhalten. Problematisch wird das spätestens hier:

Code: Alles auswählen

>>> class MyError(Exception):
...  def __init__(self, message):
...   self.message = message
...  def __str__(self):
...   return self.message
... 
>>> raise MyError
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() takes exactly 2 arguments (1 given)
Ohne Einblick in den genauen Quelltext weiß man zb nicht, ob der Fehler direkt von MyError ausgeht oder ob MyError ein Name für ein anderes Exemplar einer Exception ist. Zum Beispiel TypeError:

Code: Alles auswählen

>>> try:
...  raise MyError
... except TypeError, MyError:
...  raise MyError
... 
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
TypeError: __init__() takes exactly 2 arguments (1 given)
>>> MyError.__class__
<type 'exceptions.TypeError'>
Ich würde mich nicht darauf verlassen, das dieses Verhalten in Python 3 (das ist Python 2.5.4) oder späteren Python Versionen so bleibt.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

str1442 hat geschrieben:@Stefan: Wenn du eine Ausnahme auslöst ohne ihr Argumente zu übergeben, solltest du trotzdem erst ein Exemplar ;) erstellen. In deinem Beispiel wird die Klasse "ausgelöst". Das funktioniert zwar trotzdem (es wird automatisch ein Exemplar erstellt), aber das ist dann doch magisches Verhalten.
Das ist weder magisch, noch schlechter Stil.
Problematisch wird das spätestens hier:

Code: Alles auswählen

>>> class MyError(Exception):
...  def __init__(self, message):
...   self.message = message
...  def __str__(self):
...   return self.message
... 
>>> raise MyError
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() takes exactly 2 arguments (1 given)
Klar, mit solchen Exceptions macht man das nicht. Aber da geht auch ``raise MyError()`` nicht, so what?
Ohne Einblick in den genauen Quelltext weiß man zb nicht, ob der Fehler direkt von MyError ausgeht oder ob MyError ein Name für ein anderes Exemplar einer Exception ist. Zum Beispiel TypeError:

Code: Alles auswählen

>>> try:
...  raise MyError
... except TypeError, MyError:
...  raise MyError
... 
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
TypeError: __init__() takes exactly 2 arguments (1 given)
>>> MyError.__class__
<type 'exceptions.TypeError'>
Sowas ist ja auch ähnlich einzuordnen, wie wenn du deine Liste "list" nennst.
Ich würde mich nicht darauf verlassen, das dieses Verhalten in Python 3 (das ist Python 2.5.4) oder späteren Python Versionen so bleibt.
Ich würde es (im Falle Python 3) einfach ausprobieren...
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Antworten