HTML in reine Textdatei umwandeln?Wie?

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.
Antworten
ana88
User
Beiträge: 6
Registriert: Donnerstag 6. Mai 2004, 08:55

Hallo Euch!!!!!!!!!!!
Weißt vielleicht jemand wie man HTML-Dateien in reine Textdatei verwandelt?
fs111
User
Beiträge: 170
Registriert: Samstag 15. November 2003, 11:42
Kontaktdaten:

Code: Alles auswählen

import urllib2,htmllib,formatter
h = htmllib.HTMLParser(formatter.AbstractFormatter(formatter.DumbWriter()))
h.feed(open("deineDatei.html").read()) 
Tut das von Dir gewünschte.

HTH

Grüße fs111
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Hi ana88,

HTML-Dateien sind reine Textdateien, da is nix schmutziges dabei ;)


Gruß

Dookie
lord.hong
User
Beiträge: 26
Registriert: Samstag 19. August 2006, 12:32

Hallo fs111,

das stimmt, es tut genau das von mir gewünscht! Aber warum? Was passiert da genau? Und das ganze nur in einer Zeile.

Wenn ich nun eine html table habe, kann ich dann die entspechende Elemente erkennen und mit TAB, CR oder vielleicht auch XML versehen?

Bsp:
<table>
<tr>
<td>19.08.2006</td><td>15:00</td><td>Hamburg</td>
<td>20.08.2006</td><td>15:00</td><td>München</td
</tr>
</table>

Zur Zeit ist alles in einer Zeile allerdings schon mit einem Leerzeichen getrennt.
19.08.2006 15:00 Hamburg 20.08.2006 15:00 München.

Könnte ich zum Beispiel nach dem dritten Element ein CR einfügen? Wo findet eigentlich die Ausgabe auf der Konsole statt?

Oder in XML
<event>
<date>19.08.2006</date>
<time>15:00</time>
<location>Hamburg</location>
</event>

<event>
<date>20.08.2006</date>
<time>15:00</time>
<location>München</location>
</event>

Danke im Voraus

Lord Hong
BlackJack

Zum lesen von HTML bietet sich BeautifulSoup an und zum schreiben von XML ElementTree.

Code: Alles auswählen

import sys
from itertools import cycle, izip
from BeautifulSoup import BeautifulSoup
from elementtree.SimpleXMLWriter import XMLWriter

source = '''\
<table>
<tr>
<td>19.08.2006</td><td>15:00</td><td>Hamburg</td>
<td>20.08.2006</td><td>15:00</td><td>München</td
</tr>
</table>
'''

def convert(source, out_file):
    soup = BeautifulSoup(source)
    writer = XMLWriter(out_file)
    writer.start('events')
    table = soup.find('table')
    table_row = table.find('tr')
    for tagname, cell in izip(cycle(('date', 'time', 'location')),
                              table_row.findAll('td')):
        if tagname == 'date':
            writer.start('event')
        writer.start(tagname)
        writer.data(cell.contents[0])
        writer.end(tagname)
        if tagname == 'location':
            writer.end('event')
    writer.end('events')


if __name__ == '__main__':
    convert(source, sys.stdout)
lord.hong
User
Beiträge: 26
Registriert: Samstag 19. August 2006, 12:32

Hallo BlackJack,

klappt wunderbar! Denke das ich mit dem XML File besser arbeiten kann. Allerdings noch eine Frage: Die Ausgabe sieht zur Zeit so aus:

<events><event><date>19.08.2006</date><time>15:00</time><location>Hamburg</locat
ion></event><event><date>20.08.2006</date><time>15:00</time><location>Muenchen</
location></event></events>

Damit ich es besser lesen könnte, müsste ich mit Zeilenumbrüche und Einrückungen arbeiten. Wie könnte ich nach <events> ein Zeilenumbruch einfügen und <event> um ein Tab einrücken?

Gruß

Lord Hong
BlackJack

An den entsprechenden Stellen die Zeichen mit `writer.data()` einfügen:

Code: Alles auswählen

def convert(source, out_file):
    soup = BeautifulSoup(source)
    writer = XMLWriter(out_file)
    writer.start('events')
    writer.data('\n')
    table = soup.find('table')
    table_row = table.find('tr')
    for tagname, cell in izip(cycle(('date', 'time', 'location')),
                              table_row.findAll('td')):
        if tagname == 'date':
            writer.start('event')
            writer.data('\n')
        writer.data('  ')
        writer.start(tagname)
        writer.data(cell.contents[0])
        writer.end(tagname)
        writer.data('\n')
        if tagname == 'location':
            writer.end('event')
            writer.data('\n')
    writer.end('events')
    writer.data('\n')
Aber wer will schon XML lesen.
lord.hong
User
Beiträge: 26
Registriert: Samstag 19. August 2006, 12:32

Also nein, es ist einfach zu verzweifeln. In meinem Beispiel ist ein kleiner Fehler.
<table>
<tr>
<td>19.08.2006</td><td>15:00</td><td>Hamburg</td>
</tr>
<tr>
<td>20.08.2006</td><td>15:00</td><td>München</td>
</tr>
</table>
Drei TD Elemente werden von einem TR Element umgeben.
Nun dachte ich, es ist wohl kein Problem ein while oder for Schleife darum zu legen. Alles was ich versucht habe, ist gescheitert.

Code: Alles auswählen

counter = table.find('tr') / 2
while counter:
funktioniert nicht. Mir gelingt es noch nicht mal counter ausgeben lassen.

Code: Alles auswählen

while table.find('tr'):
   [...]
   table.find('tr')
endet in einer Endlosschleife, obwohl mir nicht ganz klar ist wieso.

Wie debuggt ich eigentlich unter Python? Ist so was möglich?

Gruß

Lord Hong
BlackJack

lord.hong hat geschrieben:Also nein, es ist einfach zu verzweifeln. In meinem Beispiel ist ein kleiner Fehler.
<table>
<tr>
<td>19.08.2006</td><td>15:00</td><td>Hamburg</td>
</tr>
<tr>
<td>20.08.2006</td><td>15:00</td><td>München</td>
</tr>
</table>
Drei TD Elemente werden von einem TR Element umgeben.
Na das macht die Sache etwas regelmässiger und damit einfacher.

Code: Alles auswählen

counter = table.find('tr') / 2
while counter:
funktioniert nicht. Mir gelingt es noch nicht mal counter ausgeben lassen.
`table.find()` ist ja auch nicht durch zwei Teilbar. Das Ergebnis ist ein `Tag` Objekt das die erste Tabellenzeile in der Tabelle repräsentiert.

Code: Alles auswählen

while table.find('tr'):
   [...]
   table.find('tr')
endet in einer Endlosschleife, obwohl mir nicht ganz klar ist wieso.
Weil der Ausdruck in der ``while`` Schleife jedesmal das erste <tr>-Element in der Tabelle findet. Das ist ein "wahres" Objekt, also wird die Schleife ewig ausgeführt.
Wie debuggt ich eigentlich unter Python? Ist so was möglich?
Es gibt auch IDEs mit "richtigem" Debugger, aber ich benutze meistens ``print``-Anweisungen und probiere im interaktiven Interpretierer aus. Dein Beispiel interaktiv untersucht:

Code: Alles auswählen

In [145]: source = '''\
   .....: <table>
   .....: <tr><td>19.08.2006</td><td>15:00</td><td>Hamburg</td></tr>
   .....: <tr><td>20.08.2006</td><td>15:00</td><td>München</td></tr>
   .....: </table>
   .....: '''

In [146]: table = BeautifulSoup.BeautifulSoup(source)

In [147]: rows = table.findAll('tr')

In [148]: rows
Out[148]:
[<tr><td>19.08.2006</td><td>15:00</td><td>Hamburg</td></tr>,
 <tr><td>20.08.2006</td><td>15:00</td><td>München</td></tr>]

In [149]: rows[0].findAll('td')
Out[149]: [<td>19.08.2006</td>, <td>15:00</td>, <td>Hamburg</td>]
Führt letztendlich zu diesem Quelltext:

Code: Alles auswählen

import sys
from BeautifulSoup import BeautifulSoup
from elementtree.SimpleXMLWriter import XMLWriter

source = '''\
<table>
<tr><td>19.08.2006</td><td>15:00</td><td>Hamburg</td></tr>
<tr><td>20.08.2006</td><td>15:00</td><td>München</td></tr>
</table>
'''

def convert(source, out_file):
    soup = BeautifulSoup(source)
    writer = XMLWriter(out_file)
    writer.start('events')
    writer.data('\n')
    table = soup.find('table')
    for table_row in table.findAll('tr'):
        writer.start('event')
        writer.data('\n')
        for tagname, cell in zip(('date', 'time', 'location'),
                                 table_row.findAll('td')):
            writer.data('  ')
            writer.start(tagname)
            writer.data(cell.contents[0])
            writer.end(tagname)
            writer.data('\n')
        writer.end('event')
        writer.data('\n')
    writer.end('events')
    writer.data('\n')


if __name__ == '__main__':
    convert(source, sys.stdout)
lord.hong
User
Beiträge: 26
Registriert: Samstag 19. August 2006, 12:32

Vielen Dank BlackJack,

ich war inzwischen auch dicht davor, dass Problem zu lösen. Immerhin ist mir klar geworden voran ich gescheitert bin. Ich dachte die ganze Zeit table_row wäre ein String Objekt. Mit dieser Annahme lag ich total falsch. Nun verstehe ich auch warum sich der Interpreter die ganze Zeit drüber beschwert hat, dass er es sich um keinen Integer handelt, wenn ich find aufgerufen hatte. Da mit dem Tupel sollte auch Klick gemacht haben.

Das Ganze verhält sich wie ein XML Baum.

Ich höre für heute auf, bin aber schon auf das nächste Problem gestossen. Es gibt ein Element, welches leer ist, dann bekomme ich eine Exception.

<locationTraceback (most recent call last):
File "test1.py", line 62, in ?
convert(source, sys.stdout)
File "test1.py", line 53, in convert
writer.data(cell.contents[0])
IndexError: list index out of range

Vermutlich habe ich zwei Möglichkeiten, die Exception fangen und dafür zu sorgen, das der XML Tag leer bleibt oder schon vorher das leere HTML Tag ausfiltern. Aber wie gesagt, für heute bin ich mit meinem Einstand in Python zufrieden, obwohl mir das 900 Seiten Buch neben mir gar nicht weiterhelfen konnte. :(

Das geschützte Leerzeichen &nbsp; muss ich auch noch rausfiltern, sieht etwas kryptisch aus im XML File. Deutsche Sonderzeichen sind auch noch nicht ok.
BlackJack

lord.hong hat geschrieben:Vermutlich habe ich zwei Möglichkeiten, die Exception fangen und dafür zu sorgen, das der XML Tag leer bleibt oder schon vorher das leere HTML Tag ausfiltern.
Du kannst testen ob es einen Inhalt in `tag.contents` gibt.
Das geschützte Leerzeichen   muss ich auch noch rausfiltern, sieht etwas kryptisch aus im XML File. Deutsche Sonderzeichen sind auch noch nicht ok.
Das `` `` ist in XML im allgemeinen auch gar nicht gültig. Man kann dem `BeautifulSoup` Objekt mit auf den Weg geben welche Kodierung der HTML-Quelltext verwendet und das HTML-Entities dekodiert werden sollen:

Code: Alles auswählen

import sys
from BeautifulSoup import BeautifulSoup
from elementtree.SimpleXMLWriter import XMLWriter

source = '''\
<table>
<tr><td>19.08.2006</td><td>15:00</td><td>Hamburg</td></tr>
<tr><td>20.08.2006</td><td>15:00</td><td>München</td></tr>
<tr><td>No break</td><td></td><td></td></tr>
</table>
'''

def convert(source, out_file):
    soup = BeautifulSoup(source,
                         fromEncoding='utf-8',
                         convertEntities=BeautifulSoup.HTML_ENTITIES)
    writer = XMLWriter(out_file)
    writer.start('events')
    writer.data('\n')
    table = soup.find('table')
    for table_row in table.findAll('tr'):
        writer.start('event')
        writer.data('\n')
        for tagname, cell in zip(('date', 'time', 'location'),
                                 table_row.findAll('td')):
            writer.data('  ')
            writer.start(tagname)
            if len(cell) > 0:
                writer.data(cell.contents[0])
            writer.end(tagname)
            writer.data('\n')
        writer.end('event')
        writer.data('\n')
    writer.end('events')
    writer.data('\n')


if __name__ == '__main__':
    convert(source, sys.stdout)
Ausgabe:

Code: Alles auswählen

<events>
<event>
  <date>19.08.2006</date>
  <time>15:00</time>
  <location>Hamburg</location>
</event>
<event>
  <date>20.08.2006</date>
  <time>15:00</time>
  <location>München</location>
</event>
<event>
  <date>No break</date>
  <time />
  <location />
</event>
</events>
lord.hong
User
Beiträge: 26
Registriert: Samstag 19. August 2006, 12:32

Hallo BlackJack,

nach dem mein Beispielprogramm ohne Problem läuft, habe ich es mit der eigentlichen Homepageseite versucht, mit der es laufen soll.

Nun hatte jedes <td> Element noch ein <font> Element. Das Problem hatte ich mit

Code: Alles auswählen

writer.data(cell.next.contents[0])
relativ schnell gelöst.

Dummerweise hat aber ein Element noch einen geschlossenes Image Tag.
<location><font><img/>München</font><location>
Nun kann ich mir mit

Code: Alles auswählen

print cell.next.name
sehen, dass der font Tag-Name ausgedruckt wird. Mit

Code: Alles auswählen

print cell.next.next.name
wird mir der img Tag-Name ausgegeben. Aber ich komme nicht an den Inhalt (München).

Sowohl

Code: Alles auswählen

cell.next.contents[0]
als auch

Code: Alles auswählen

cell.next.next.contents[0]
führen zu "IndexError: list index out of range". Ich hatte dann auch mit [1] und [2] versucht, weil ich [0] wäre das Image Element und [1] dann der Inhalt.

Kannst du mir nochmal helfen?

Gruß

Lord Hong
BlackJack

Hier kann auch wieder das rumspielen mit dem interaktiven Interpretierer helfen. Damit kann man den `BeatifulSoup` Baum einfach untersuchen und einiges ausprobieren.

Code: Alles auswählen

In [24]: soup
Out[24]: <td><font><img />München</font></td>

In [25]: soup.td.font
Out[25]: <font><img />München</font>

In [26]: soup.td.font.contents
Out[26]: [<img />, u'M\xfcnchen']

In [27]: soup.td.font.contents[1]
Out[27]: u'M\xfcnchen'

In [28]: soup.td.font.img.next
Out[28]: u'M\xfcnchen'
Wie man sieht enthält das <font> Tag zwei Kinder. Ein Image-Tag und einen Text-Knoten. Die letzte Variante ist ein bischen sicherer, weil sich die Anzahl der Elemente ändert, wenn zusätzliche Zeilenumbrüche zwischen den Tags vorkommen können.
lord.hong
User
Beiträge: 26
Registriert: Samstag 19. August 2006, 12:32

Hallo BlackJack,

ein herzliches Dankeschön an dich. Ich kann jetzt die Daten aus dem Internet lesen und sie in ein XML Datei schreiben. Es ist viel weniger Code als ich dachte. Das mit dem interaktiven Mode hat leider nicht geklappt. Sowohl mit python -i test1.py oder zeilenweise Eingabe in >>> gaben nicht mehr Ergebnis aus als ich gebraucht hätte.

Allerdings bin ich nun zufrieden mit dem was ich habe und denke, dass es nun leichter sein sollte ein XML File auszuwerten.

Nochmals Vielen Dank und sollte ich für die statische Auswertung Fragen haben, dann werde ich sie hier im Forum posten.

Gruß

Lord Hong
Antworten