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

Lach!

Ich habe das gerade nocheinmal installiert.
Diesmal hat es geklappt :shock:

Danke!
[b][i]ein kleines game für die die lust haben http://konaminut.mybrute.com[/i][/b]
;-)
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Ich bin etwas spät:
Das ist weder magisch, noch schlechter Stil.
raise verhält sich aktuell wie eine Funktion, die explizit auf einen Typ prüft und dann anders handelt als sonst, also Ducktyping ignoriert.

Code: Alles auswählen

def f(thing):
  if isinstance(thing, type) and issubclass(thing, Exception):
      thing = thing()
  
  # ...
Würde man auf solche Spezialfälle in normalen Python Funktionen durchgängig implementieren, wäre das schlechter Stil. Diese Funktion täte etwas, was von ihrer Aufgabe her nicht direkt ersichtlich ist, und tut es nur wenn man einen ganz bestimmten Typ als Argument übergäbe. Und zugleich hat man eine gewisse unnötige Inkonsequenz, denn ansonsten instanziiert man die Klasse ja sowieso. Warum also bei diesem speziellem Fall nicht auch? Warum kracht es nicht einfach, warum versucht raise auf diese Art und Weise das "falsche" Argument zurechtzubiegen?
Klar, mit solchen Exceptions macht man das nicht. Aber da geht auch ``raise MyError()`` nicht, so what?
Sowas ist ja auch ähnlich einzuordnen, wie wenn du deine Liste "list" nennst.
Ich erwarte (bei einer Funktion) normalerweise, das diese Funktion genau beschreibt, wie es mit seinen Argumenten umgeht und welche Verhaltensweisen diese unterstützen müssen. raise als Statement erwartet ein Exemplar einer Subklasse von Exception oder von Exception selbst. Übergebe ich einfach nur einen Namen, erwarte ich (bei Betrachten des Tracebacks zb), das dieser Name diese Verhaltensweisen unterstützt. Insofern wäre der einzige Hinweis darauf, das ich hier tatsächlich eine Klasse übergebe, die übliche Python Namenskonvention. Ohne diese (und das wissen, was raise genau tut) hätte ich nicht den geringsten Anhaltspunkt, was an der Stelle schief gelaufen wäre, weil eben durch einen Typcheck und einer aus der Reihe fallenden Behandlung versucht wurde, den "Fehler" bei Übergabe des Arguments zu korrigieren.
Ich würde es (im Falle Python 3) einfach ausprobieren...
Ich sagte ja nur, das ich mich selbst nicht darauf verlasse, das es so bleibt bzw es einfach klarer finde, auch hinter ein einzelnes "raise ValueError" noch ein Klammernpaar hinterzusetzen ;).


Das alles ist natürlich nicht so tragisch, wenn man einmal das Verhalten von raise kennt. Dennoch finde ich es allein schon wegem dem sonst üblichem Ducktyping besser, genau das zu übergeben, was auch erwartet wird.
Antworten