Seite 1 von 1
Tabelle aus HTML mit lxml.html und regex einlesen
Verfasst: Sonntag 28. September 2008, 19:21
von alexander
Hallo,
ich versuche gerade mit lxml.html Tabellenzellen einer HTML-Seite auszulesen.
Den Element Tree hole ich mit:
Code: Alles auswählen
import urllib
from lxml.html import fromstring
def get_site(url):
content = urllib.urlopen(url).read()
doctree = lxml.html.fromstring(content)
doctree.make_links_absolute(url)
jetzt will ich für alle Tabellen mit den Klassennamen "detailTable" aus den Zeilen mit den Namen "odd" oder "even" die Zellen auslesen, deren Klassenname mit "timeDetail", "odvDetail" oder "motDetail" beginnt.
Code: Alles auswählen
...
<table class="detailTable" cellspacing="0">
<tbody>
<tr class="odd">
<td class="timeDetail borderTopodd">19:17</td>
<td class="odvDetail borderTopodd">Hier steht ein Text</td>
...
<td class="motDetail borderTopodd" rowspan="2">Hier steht auch ein Text</td>
...
</tr>
...
Als regulären Ausdruck wollte ich z.B. "(odd|even)" und "timeDetai\\w*" verwenden, jedoch kommen die Funktionen .find_class() und .findall() mit regulären Ausfrücken als Argument nicht zurecht.
Code: Alles auswählen
for table in doctree.find_class('odd'):
retime = re.compile('.//td[@class="odvDetai\\w*"]')
for item in table.findall('.//td[@class="timeDetail"]'): #tr[@class="odd"]/
print type(item)
time = item.text
print time
for item in table.findall('.//td[@class="odvDetail"]'):
location = item.text
print location
for item in table.findall('.//td[@class="modDetail"]'):
description = item.text
print description
Wie kann ich reguläre Ausdrücke für die Suche nach Elementen verwenden?
Verfasst: Sonntag 28. September 2008, 21:08
von lunar
Was Wunder ... hättest du die Dokumentation gelesen, wüsstest du, dass "findall" eine Etree-spezifische Abfragesprache nutzt, die von XPath abgeleitet wurde, dass lxml.html Elemente eine .xpath() und eine .cssselect() Methode haben, um mittels XPath-Ausdrücken bzw. CSS-Selektoren zu suchen, und dass man reguläre Ausdrücke nicht für die Suche nach Elementen in lxml.html-Etrees nutzen kann.
Verfasst: Sonntag 28. September 2008, 21:41
von alexander
Ich hatte mir die Doku zu lxml durchgelesen, Reguläre Ausdrücke bzw. deren Nichtanwendbarkeit wurden leider nicht namentlich erwähnt.
Gibt es denn Alternativen zu lxml.html, die mit regulären Ausdrücken bei der Suche im Etree umgehen können?
Verfasst: Sonntag 28. September 2008, 21:50
von lunar
alexander hat geschrieben:Ich hatte mir die Doku zu lxml durchgelesen, Reguläre Ausdrücke bzw. deren Nichtanwendbarkeit wurden leider nicht namentlich erwähnt.

Wieso nur kommst du dann auf die Idee, sie zu verwenden? Wenn eine Doku etwas
nicht erwähnt, dann wohl deswegen, weil es
nicht unterstützt wird.
Gibt es denn Alternativen zu lxml.html, die mit regulären Ausdrücken bei der Suche im Etree umgehen können?
Nein. Es gibt alternative Parser wie BeautifulSoup, deren Baumobjekte Abfragen mit regulären Ausdrücken unterstützen, das ist dann allerdings kein Elementtree mehr.
Warum nutzt du nicht einfach die Mittel, die lxml.html dir zu Verfügung stellt? CSS-Selektoren und XPath sind imho um Welten besser zum Scrapen geeignet als reguläre Ausdrücke.
Verfasst: Sonntag 28. September 2008, 23:01
von Leonidas
lunar hat geschrieben:CSS-Selektoren und XPath sind imho um Welten besser zum Scrapen geeignet als reguläre Ausdrücke.
Vor allem weil man damit, ganz im Gegensatz zu regulären Ausdrücken auch nichttriviale Queries mit Relationen der Elemente zueinander ausdrücken kann.
Wenn es ein Zitat gibt, das da gut passt, dann auf jeden Fall dieses:
Some people, when confronted with a problem, think, “I know, I’ll use regular expressions.”
Now they have two problems.
Verfasst: Sonntag 28. September 2008, 23:07
von lunar
Netter Spruch

Verfasst: Montag 29. September 2008, 13:09
von alexander
Danke für den Wink mit dem Zaunpfahl
jetzt nutze ich xpath() bekomme aber nicht ganz das Ergebnis, das ich erwartet habe:
Code: Alles auswählen
is_time = re.compile("(\d\d:\d\d)")
for table in doctree.find_class('detailTable'):
print '-'*60
for item in table.xpath('.//td[contains(name(), timeDetail)]'):
try:
element = item.text.strip()
except:
pass
if is_time.match(element):
time = element
print time
else:
if element != '':
print element
element = ''
liefert nicht nur den Text für die Zelle
sondern auch den Text der folgenden Zellen
Code: Alles auswählen
<td class="odvDetail borderTopodd">Hier steht ein Text</td>
...
<td class="motDetail borderTopodd" rowspan="2">Hier steht auch ein Text</td>
Ausgabe:
Code: Alles auswählen
------------------------------------------------------------
19:17
Hier steht ein Text
Hier steht auch ein Text
Woran liegt das? Die Zellen sind doch auf der gleichen Ebene im Etree, oder nicht?
Verfasst: Montag 29. September 2008, 14:41
von snafu
Code: Alles auswählen
In [1]: s = '''<table class="detailTable" cellspacing="0">
...: <tbody>
...: <tr class="odd">
...: <td class="timeDetail borderTopodd">19:17</td>
...: <td class="odvDetail borderTopodd">Hier steht ein Text</td>
...: ...
...: <td class="motDetail borderTopodd" rowspan="2">Hier steht auch ein Text</td>
...: ...
...: </tr>
...: </tbody>
...: </table>'''
In [2]: from lxml.html import fromstring
In [3]: doc = fromstring(s)
In [4]: print doc.xpath('.//td[@class="timeDetail borderTopodd"]')[0].text
19:17
Verfasst: Montag 29. September 2008, 15:01
von alexander
@snafu: Das hatte ich als erstes probiert, das Problem ist allerdings, dass der Klassenname nicht immer "timeDetail borderTopodd" lautet, sondern auch mal nur "timeDetail" oder "timeDetail borderToppeven" usw.
Deswegen suche ich einen xpath()-Ausdruck, der mir alle entsprechenden Texte der Zellen, deren Klassenname mit "timeDetail" beginnt zurückliefert.
Verfasst: Montag 29. September 2008, 15:07
von lunar
Bei HTML-Klassen ist man imho besser mit einem CSS-Selektor bedient:
Code: Alles auswählen
tree = lxml.html.fromstring(source)
print tree.cssselect('td.timeDetail')[0].text_content()
lxml.html kompiliert CSS-Selektoren in xpath. Der dem obigen Ausdruck äquivalente XPath sieht wie folgt aus:
Code: Alles auswählen
descendant-or-self::td[contains(concat(' ', normalize-space(@class), ' '), ' timeDetail ')]
Verfasst: Montag 29. September 2008, 16:26
von alexander
@lunar: Das war der Ausdruck, den ich gesucht habe, vielen Dank.

Verfasst: Montag 29. September 2008, 17:37
von lunar
Naja, da würde ich aber doch lieber den CSS-Ausdruck nehmen, denn "Readability counts"
Der Dank ist zwar nett, aber nicht nötig. Ich habe mir den Ausdruck nicht selbst ausgedacht:
Code: Alles auswählen
import lxml.cssselect
print lxml.cssselect.CSSSelector('td.timeDetail').path
# descendant-or-self::td[contains(concat(' ', normalize-space(@class), ' '), ' timeDetail ')]