"Kommafehler" bei html5lib?

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
Snoda
User
Beiträge: 32
Registriert: Mittwoch 1. Februar 2006, 14:34

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
Hello world!
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Eigentlich sollte html5lib auch kaputtes html akzeptieren, ich würde vorschlagen: Minimalbeispiel machen und Bug aufmachen.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

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.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

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.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@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.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

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.
start_with_python
User
Beiträge: 41
Registriert: Samstag 20. Juni 2009, 18:12

Grüße[b]
start_with_python[/b]

Lust auf [url=https://www.dropbox.com/referrals/NTE5OTQ5Mjk5]DropBox[/url]? (RefLink)
Snoda
User
Beiträge: 32
Registriert: Mittwoch 1. Februar 2006, 14:34

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?
Hello world!
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

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
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

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
Snoda
User
Beiträge: 32
Registriert: Mittwoch 1. Februar 2006, 14:34

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
Hello world!
Antworten