Seite 1 von 1

Parsing HTML Tabelle mit BeatifulSoup

Verfasst: Freitag 29. November 2013, 07:08
von mit
Hi,
ich habe folgendes HTML document

Code: Alles auswählen

<html><head>
<meta charset="utf-8">

</head>
<body>
<a name="Test1"> 
<center>
<b>Test 1</b> <table border="0">
	<tbody><tr>
		<th> Type </th>
		<th> Region </th>
	</tr>
	<tr>
		<td> <table border="0">
	<thead>
		<tr> 
			<th><b>Type</b></th>
			<th> &nbsp; </th>
			<th> Count </th>
			<th> Percent </th>
		</tr>
	</thead>
		<tbody><tr> 
			<td> <b>T1</b> </td> 
			<th> &nbsp; </th>
			<td class="numeric" bgcolor="#ff0000"> 34,314 </td> 
			<td class="numeric" bgcolor="#ff0000"> 31.648% </td>
		</tr>
		<tr> 
			<td> <b>T2</b> </td> 
			<th> &nbsp; </th>
			<td class="numeric" bgcolor="#bf3f00"> 25,820 </td> 
			<td class="numeric" bgcolor="#bf3f00"> 23.814% </td>
		</tr>
		<tr> 
			<td> <b>T3</b> </td> 
			<th> &nbsp; </th>
			<td class="numeric" bgcolor="#24da00"> 4,871 </td> 
			<td class="numeric" bgcolor="#24da00"> 4.493% </td>
		</tr>
	
</tbody></table><br>
 </td>
		<td> <table border="0">
	<thead>
		<tr> 
			<th><b> Type</b></th>
			<th> &nbsp; </th>
			<th> Count </th>
			<th> Percent </th>
		</tr>
	</thead>
		<tbody><tr> 
			<td> <b>T4</b> </td> 
			<th> &nbsp; </th>
			<td class="numeric" bgcolor="#ff0000"> 34,314 </td> 
			<td class="numeric" bgcolor="#ff0000"> 31.648% </td>
		</tr>
		<tr> 
			<td> <b>T5</b> </td> 
			<th> &nbsp; </th>
			<td class="numeric" bgcolor="#53ab00"> 11,187 </td> 
			<td class="numeric" bgcolor="#53ab00"> 10.318% </td>
		</tr>
		<tr> 
			<td> <b>T6</b> </td> 
			<th> &nbsp; </th>
			<td class="numeric" bgcolor="#bf3f00"> 25,820 </td> 
			<td class="numeric" bgcolor="#bf3f00"> 23.814% </td>
		</tr>
	
</tbody></table><br>
 </td>
	</tr>
</tbody></table>
</center>

	</a>
</body></html>
und aus dieser Tabell wuerde ich gerne die Werte "Count" und "Precentage" fuer T1 bis T6 extrahiren. Leider bin ich nicht in der lage auf die Werte zuzugreifen mit diesem code:

Code: Alles auswählen

from bs4 import BeautifulSoup

with open("/home/mit/tmp/test3.html") as f:
    soup = BeautifulSoup(f, "lxml")
    
    print soup.find_all('a', {'name':'Test1'})
    
    for tr in soup.find_all('a', attrs={'name':'Test1'}).find_all('tr')[2:]:
        tds = tr.find_all('td')
        print tds
Ich bekomme diesen Error:

Code: Alles auswählen

Traceback (most recent call last):
  File "test.py", line 8, in <module>
    for tr in soup.find_all('a', attrs={'name':'Test1'}).find_all('tr')[2:]:
AttributeError: 'ResultSet' object has no attribute 'find_all'
Wie ist es moeglich auf die Werte aus der Tabelle zuzugreifen?

Vielen Dank im Vorraus.

Re: Parsing HTML Tabelle mit BeatifulSoup

Verfasst: Freitag 29. November 2013, 09:01
von Sirius3
ein Resultset hat keine Methode 'find_all', die ja da auch keinen Sinn hat, weil ein ResultSet schon das Ergebnis eines find_all ist. Wie es richtig geht, hast Du ja auch schon in Deinem Code drin (for-Schleife).

Re: Parsing HTML Tabelle mit BeatifulSoup

Verfasst: Freitag 29. November 2013, 09:43
von mit
Danke, es funktioniert!

Code: Alles auswählen

from bs4 import BeautifulSoup

with open("/home/mit/tmp/test3.html") as f:
    soup = BeautifulSoup(f, "lxml")
    
    for a in soup.find_all('a', attrs={'name':'Test1'}):
        for tr in a.find_all('tr')[2:]:
            tds = tr.find_all('td')
            print tds

Re: Parsing HTML Tabelle mit BeatifulSoup

Verfasst: Freitag 29. November 2013, 11:31
von BlackJack
@mit: Da anscheinend nur *ein* Treffer für 'a' mit name=Test1 erwartet wird, könnte man auch einfach das `find_all()` sein lassen und nur `find()` verwenden und sich damit die äussere Schleife über eine Liste sparen von der man weiss das sie sowieso nur ein Element hat. Und die Werte unterscheiden sich von allen anderen Tabellenzellen durch die 'numeric'-Klasse — das könnte man auch nutzen:

Code: Alles auswählen

from bs4 import BeautifulSoup


def main():
    with open('test.html') as html_file:
        soup = BeautifulSoup(html_file)
        for td in soup.find('a', {'name': 'Test1'})('td', 'numeric'):
            print td.string


if __name__ == '__main__':
    main()

Re: Parsing HTML Tabelle mit BeatifulSoup

Verfasst: Sonntag 1. Dezember 2013, 11:58
von mit
Danke, für die Lösung. Ich wollte die Tabellen Inhalt in ein dict speichern "'T1': ('25,820', '23.814%')" und es hat mit dem folgenden Code funktioniert.

Code: Alles auswählen

from bs4 import BeautifulSoup

test = {}

with open("test3.html") as f:
    soup = BeautifulSoup(f, "lxml")

    for a in soup.find_all('a', attrs={'name': 'Test1'}):
        for tr in a.find_all('tr')[2:]:
            tds = tr.find_all('td')
            if len(tds) > 0:
                test[str(tds[0].text).strip()] = (str(tds[1].text).strip(),
                    str(tds[2].text).strip())

print test
print test['T1']
Gibt es eine bessere Lösung um die in eine Datenstruktur zu packen?

Re: Parsing HTML Tabelle mit BeatifulSoup

Verfasst: Sonntag 1. Dezember 2013, 12:24
von BlackJack
Alternativ:

Code: Alles auswählen

from itertools import izip
from bs4 import BeautifulSoup


def main():
    with open('test.html') as html_file:
        soup = BeautifulSoup(html_file)
        result = dict()
        for table in soup.table('table'):
            td_iter = iter(table('td'))
            items = izip(td_iter, td_iter, td_iter)
            for type_td, count_td, percent_td in items:
                result[type_td.text.strip()] = (
                    count_td.string.strip(), percent_td.string.strip()
                )
    print result



if __name__ == '__main__':
    main()
Solltest Du tatsächlich Bytestrings aus den Unicode-Objekten machen wollen, dann würde ich nicht einfach `str()` verwenden, sondern die Zeichenketten tatsächlich explizit kodieren.

Re: Parsing HTML Tabelle mit BeatifulSoup

Verfasst: Montag 2. Dezember 2013, 02:19
von mit
Warum funtioniert type_td.string.strip() nicht, aber count_td.string.strip() funktioniert?

Re: Parsing HTML Tabelle mit BeatifulSoup

Verfasst: Montag 2. Dezember 2013, 02:48
von BlackJack
@mit: `string` ist die Zeichenkette die *direkt* das Kind vom Element ist. Bei `type_td` ist der Text aber in einem weiteren Kindelement (<b>) verschachtelt. Das `text`-Attribut sammelt alle Texte rekursiv aus den Kindelementen zusammen und verbindet die zu einer Zeichenkette. Wenn man `string` verwenden wollen würde, hätte man explizit über das <b>-Element gehen müssen:

Code: Alles auswählen

In [23]: type_td
Out[23]: <td> <b>T1</b> </td>

In [24]: type_td.string

In [25]: type_td.text
Out[25]: u' T1 '

In [26]: type_td.b.string
Out[26]: u'T1'

Re: Parsing HTML Tabelle mit BeatifulSoup

Verfasst: Montag 2. Dezember 2013, 14:48
von mit
Danke, jetzt verstehe ich es.

Warum funktioniert der folgende Code nicht?

Code: Alles auswählen

from bs4 import BeautifulSoup

with open("test4.html") as f:
    soup = BeautifulSoup(f, "lxml")

    for rows in soup.find('a', attrs={'name': 'Test1'}).find("tbody").find_all("tr"):
        print rows
        for row in rows:
            print row.find_all("td")

Re: Parsing HTML Tabelle mit BeatifulSoup

Verfasst: Montag 2. Dezember 2013, 15:21
von BlackJack
@mit: Schreib mal statt ``print rows`` ``print list(rows)``, dann siehst Du über was Du da alles iterierst.

Re: Parsing HTML Tabelle mit BeatifulSoup

Verfasst: Montag 2. Dezember 2013, 15:44
von mit
Ich bekomme diese Fehlermeldung

Code: Alles auswählen

[u'\n', <th> Type </th>, u'\n', <th> Region </th>, u'\n']
Traceback (most recent call last):
  File "test.py", line 9, in <module>
    print row.find_all("td")
  File "/home/mit/apps/pymodules/lib/python2.7/site-packages/beautifulsoup4-4.3.2-py2.7.egg/bs4/element.py", line 675, in __getattr__
    self.__class__.__name__, attr))
AttributeError: 'NavigableString' object has no attribute 'find_all'

mit diesem Code

Code: Alles auswählen

from bs4 import BeautifulSoup

with open("test3.html") as f:
    soup = BeautifulSoup(f, "lxml")

    for rows in soup.find('a', attrs={'name': 'Test1'}).find("tbody").find_all("tr"):
        print list(rows)
        for row in rows:
            print row.find_all("td")
HTML Datei ist die selbe wie im ersten post. Ich vermute es liegt an th Elementen die keine td beinhalten, aber bin nicht sicher wie man es lösen könnte.

Re: Parsing HTML Tabelle mit BeatifulSoup

Verfasst: Montag 2. Dezember 2013, 15:51
von BlackJack
@mit: Nein, es liegt genau an dem was die Fehlermeldung sagt und was man in der ausgegebenen Liste ziemlich deutlich sehen sollte: `NavigableString`-Objekte haben keine `find_all()`-Methode. Das erste, dritte, und das letzte Element in der Liste sind *Zeichenketten* (Unterklasse von `unicode`) und keine Elemente die Kinder haben können nach denen man suchen kann.