Seite 1 von 1

"Kommafehler" bei html5lib?

Verfasst: Donnerstag 10. Dezember 2009, 18:40
von Snoda
Hallo, Phytonier,

ich muss mich leider wieder mal aufgrund eines Problemes an euch wenden. Ich versuche mit html5lib und lxml eine Website zu parsen. Das klappt an sich super, wenn die Seite "sauber" geschreiben wäre. Leider ist manchmal in einer Zeile ein "," zwischen den verschiedenen attributen eines Tags. In dem Fall bringt er mir nen Traceback das "," kein gültiger Wert wäre....

Kann man irgendwas einstellen dass das Komma ignoriert wird?
Hier noch mal der konkrete Kontext...

<form action="/home/index.php?r=23", method="post">

lg Snoda

Verfasst: Freitag 11. Dezember 2009, 08:52
von Darii
Eigentlich sollte html5lib auch kaputtes html akzeptieren, ich würde vorschlagen: Minimalbeispiel machen und Bug aufmachen.

Re: "Kommafehler" bei html5lib?

Verfasst: Freitag 11. Dezember 2009, 11:46
von /me
Snoda hat geschrieben:Das klappt an sich super, wenn die Seite "sauber" geschreiben wäre. Leider ist manchmal in einer Zeile ein "," zwischen den verschiedenen attributen eines Tags.
Du könntest natürlich mit BeautifulSoup erst einmal den HTML-Text aufräumen.

Verfasst: Freitag 11. Dezember 2009, 21:38
von snafu
Wo wir beim Thema sind, das habe ich kürzlich entdeckt: http://github.com/adevore/old-beautiful-soup

Parst wesentlich fehlertoleranter als die aktuelle Version von BS.

Verfasst: Freitag 11. Dezember 2009, 23:51
von jerch
@Snoda:
lxml selbst ist da ziemlich tolerant:

Code: Alles auswählen

>>> from lxml import html
>>> root=html.fromstring('<form action="/home/index.php?r=23", method="post">')
>>> html.tostring(root)
'<form action="/home/index.php?r=23" method="post"></form>'
Ich weiß allerdings nicht, wie es hier aktuell um den HTML5-Parser bestellt ist.

Verfasst: Samstag 12. Dezember 2009, 08:38
von snafu
Diesen Satz:
lxml can benefit from the parsing capabilities of html5lib through the lxml.html.html5parser module.
http://codespeak.net/lxml/html5parser.html

...lese ich so, dass `lxml` von Haus aus keine HTML5-Fähigkeiten mitbringt, was aber dank der Möglichkeit zur Einbindung des Moduls keine große Schwierigkeit darstellen sollte.

Verfasst: Samstag 12. Dezember 2009, 15:04
von start_with_python

Verfasst: Dienstag 15. Dezember 2009, 11:16
von Snoda
Danke für eure Hilfe.

Noch mal das Minimalbeispiel, aber ich denke das Prizip ist den meisten klar...

Code: Alles auswählen

from html5lib import treebuilders, HTMLParser
parser=HTMLParser(tree=treebuilders.getTreeBuilder('lxml'))
root=parser.parse("<html><body><a href='irgendwas' id='25'")
print root.xpath(".//a")
root=parser.parse("<html><body><a href='irgendwas', id='25'")
print root.xpath(".//a")

Code: Alles auswählen

[<Element a at 16e1360>]
Traceback (most recent call last):
  File "C:\Python25\test.py", line 5, in <module>
    root=parser.parse("<html><body><a href='irgendwas', id='25'")
  File "build\bdist.win32\egg\html5lib\html5parser.py", line 155, in parse
    self._parse(stream, innerHTML=False, encoding=encoding)
  File "build\bdist.win32\egg\html5lib\html5parser.py", line 130, in _parse
    method(token["name"], token["data"])
  File "build\bdist.win32\egg\html5lib\html5parser.py", line 316, in processStartTag
    self.startTagHandler[name](name, attributes)
  File "build\bdist.win32\egg\html5lib\html5parser.py", line 898, in startTagA
    self.addFormattingElement(name, attributes)
  File "build\bdist.win32\egg\html5lib\html5parser.py", line 772, in addFormattingElement
    self.tree.insertElement(name, attributes)
  File "build\bdist.win32\egg\html5lib\treebuilders\_base.py", line 246, in insertElementNormal
    element.attributes = attributes
  File "build\bdist.win32\egg\html5lib\treebuilders\etree.py", line 44, in _setAttributes
    self._element.set(key, value)
  File "lxml.etree.pyx", line 634, in lxml.etree._Element.set (src/lxml/lxml.etree.c:31548)
  File "apihelpers.pxi", line 482, in lxml.etree._setAttributeValue (src/lxml/lxml.etree.c:13849)
  File "apihelpers.pxi", line 1417, in lxml.etree._attributeValidOrRaise (src/lxml/lxml.etree.c:21673)
ValueError: Invalid attribute name u','
Die Frage ist jetzt mit welcher Methode ich Performance-optimiert weitermachen soll... html5lib komplett weglassen und nur lxml nutzen? Welche Nachteile würden daraus entstehen?

Verfasst: Dienstag 15. Dezember 2009, 16:56
von jerch
Das tolle an HTML5 ist ja, das es ein riesigen Regelsatz für das Parsen mitbringt und so versucht, den Kraut- und Rüben-Wuchs zumindest auf Parserseite einzudämmen (Dumm nur, dass so auch die Ersetzungsregeln für "falsches" HTML5 vorgeben sind und nun als Entschuldigung für das Schreiben kaputter Dokumente dienen können ;) ).

Zu der Sache mit dem Komma:
Die Spec sagt folgendes dazu - http://www.w3.org/TR/html5/syntax.html# ... name-state
Demnach ist quasi alles als Attributname erlaubt, was nicht unter Leerstellen, '/', '>' usw. fällt. Damit wäre das ein Bug, der scheint beim Treebuilder zu liegen. (Offensichtlich mag Dein lxml-Treebuilder ',' nicht als Attributnamen.)
Snoda hat geschrieben:Die Frage ist jetzt mit welcher Methode ich Performance-optimiert weitermachen soll... html5lib komplett weglassen und nur lxml nutzen? Welche Nachteile würden daraus entstehen?
Ich würde derzeit auf HTML5-Konformität verzichten. Zum einen ist HTML5 sehr neu, selbst unter Browsern nicht bis rudimentär umgesetzt (die meisten neuen Elemente besitzen noch gar keine eigene visuelle Repräsentation, z.B. input-Typen wie date und color). Dasselbe gilt auf Sprachenseite, die verfügbaren HTML5-Bibliotheken sind, sofern überhaupt schon vorhanden, kaum dem Alphastadium entwachsen. Das gilt wohl auch für Python und lxml im Besonderen, da dieses stark auf libxml aufsetzt und es dort keinen erkennbaren Fahrplan in Richtung HMTL5 gibt.
Nichtsdestotrotz hat lxml in den neueren Versionen einen html5-Parser integriert (von html5lib), falls Du auf HTML5 bestehst, kannst Du ja mal testen, ob es direkt aus lxml heraus funktioniert (vllt. ist die treebuilder-API hier angepasst worden).

Wenn Du aber tatsächlich auf Performance optimieren willst, bleibt eigentlich nur die reine lxml-Lösung, da der html5lib-Parser hier deutlich schlechter ist. Auch HTML5 betreffend wärst Du erstmal nicht schlechter gestellt, da vom W3C versichert wird, dass HTML5, was sich nur schleichend verbreiten wird (siehe oben), von heutigen "kaputten" HTML-Parsern verarbeitet werden kann.

Grüsse jerch

Verfasst: Mittwoch 16. Dezember 2009, 10:49
von sma
Man kann's auch selbst filtern, z.B. so:

Code: Alles auswählen

import re

def filter_attributes(s):
	def g(m):
		return m[0] + "=" + m[2] if m[1] else m[0]
	def f(m):
		if m.group(2):
			return m.group(1) + " ".join(g(m) for m 
				in re.findall(r"""([\w:-]+)(?:\s*(=)\s*("[^"]*"|'[^']*'|\S+))?""", 
					m.group(2))) + m.group(3)
		return m.group(0)
	return re.sub(r"(<[\S+]\s+)([^.]+)(/?\s*>)|[^<]+", f, s)
	
print filter_attributes('<a href="http://url", title=abc>so what</a>')
Besser wäre aber noch, den Verursacher des kaputten HTMLs so lange zu schlagen, bis der wenigstens wohlgeformtes (X)HTML erzeugt - es muss ja noch nicht mal valide sein.

Stefan

Verfasst: Donnerstag 24. Dezember 2009, 10:24
von Snoda
So, ich parse jetzt nur noch mit lxml. Anfangs dachte ich lxml hat keinen eigenen Parser, und hier wurde auch immer html5lib empfohlen...

... lxml ist tatsächlich wesentlich performanter und toleranter. Ich bin begeistert 8)

Danke für die guten Tips und frohes Weihnachtsfest euch allen!

LG Snoda