2016-06-04 5 views
0

Ich versuche, Informationen aus einer Tabelle mit bs4 und Python zu extrahieren. wenn ich den folgenden Code verwende Informationen aus Header der Tabelle zu extrahieren:Extrahieren von Informationen aus einer Tabelle außer Header der Tabelle mit bs4

tr_header=table.findAll("tr")[0] 
    tds_in_header = [td.get_text() for td in tr_header.findAll("td")] 
    header_items= [data.encode('utf-8') for data in tds_in_header] 
    len_table_header = len (header_items) 

Es funktioniert, aber für die folgenden Codes, die ich Informationen aus der ersten Reihe zum Ende der Tabelle zu extrahieren versuchen:

tr_all=table.findAll("tr")[1:] 
    tds_all = [td.get_text() for td in tr_all.findAll("td")] 
    table_info= [data.encode('utf-8') for data in tds_all] 

es gibt die folgende Fehlermeldung:

AttributeError: 'list' object has no attribute 'findAll' 

Kann mir jemand helfen, es zu bearbeiten. Diese

ist Tabelleninformationen:

<table class="codes"><tr><td><b>Code</b> 
</td><td><b>Display</b></td><td><b>Definition</b></td> 
</tr><tr><td>active<a name="active"> </a></td> 
<td>Active</td><td>This account is active and may be used.</td></tr> 
<tr><td>inactive<a name="inactive"> </a></td> 
<td>Inactive</td><td>This account is inactive 
and should not be used to track financial information.</td></tr></table> 

Dies ist die Ausgabe für tr_all:

[<tr><td><b>Code</b></td><td><b>Display</b></td><td><b>Definition</b></td></tr>, <tr><td>active<a name="active"> </a></td><td>Active</td><td>This account is active and may be used.</td></tr>, <tr><td>inactive<a name="inactive"> </a></td><td>Inactive</td><td>This account is inactive and should not be used to track financial information.</td></tr>] 
+0

Sie sollten wahrscheinlich HTML-Code posten Sie versuchen, zu analysieren. Da 'tr_header' ein einzelnes bs4-Element ist, können Sie findAll aufrufen. Aber für die Liste der Zeilen (und das ist, was Sie wahrscheinlich von "tr_all" erhalten) Sie müssen findAll für jedes Element nicht auf der ganzen Liste aufrufen (mit doppeltem Listenverständnis oder besser - getrennt für Schleife - für Lesbarkeit) – JustMe

+0

@JustMe, thank Sie ! Selbst für diesen Code tr_all = table.findAll ("tr") bekomme ich immer noch eine Liste, aber dafür: tr_header = table.findAll ("tr") [0] Ich bekomme keine Liste. Ich bin total verwirrt. Ich habe eine andere Frage. Ist es möglich, diesen Code zu bearbeiten, um Tabelleninformationen aus der ersten Zeile am Ende der Tabelle hinzuzufügen: table_information = [td.get_text() für td in table.findAll ("td")]. Vielen Dank ! – Mary

Antwort

1

Für Ihre erste Frage,

import bs4 

text = """ 
<table class="codes"><tr><td><b>Code</b> 
</td><td><b>Display</b></td><td><b>Definition</b></td> 
</tr><tr><td>active<a name="active"> </a></td> 
<td>Active</td><td>This account is active and may be used.</td></tr> 
<tr><td>inactive<a name="inactive"> </a></td> 
<td>Inactive</td><td>This account is inactive 
and should not be used to track financial information.</td></tr></table>""" 

table = bs4.BeautifulSoup(text) 
tr_all = table.findAll("tr")[1:] 
tds_all = [] 
for tr in tr_all: 
    tds_all.append([td.get_text() for td in tr.findAll("td")]) 
    # if You prefer double list comprefension instead... 
table_info = [data[i].encode('utf-8') for data in tds_all 
             for i in range(len(tds_all))] 
print(table_info) 

ergibt

['active ', 'Active', 'inactive ', 'Inactive'] 

Und in Bezug auf Ihre zweite Frage

tr_header=table.findAll("tr")[0] i do not get a list

Zwar [] ist Indexierungsvorgang, der aus der Liste erstes Element auswählt, damit Sie einzelnes Element erhalten. [1:] ist Slicing-Operator (werfen Sie einen Blick auf schöne tutorial, wenn Sie weitere Informationen benötigen).

Eigentlich erhalten Sie die Liste zweimal für jeden Aufruf von table.findAll ("tr") - für Kopfzeile und Rest der Zeilen. Sicher, das ist ziemlich überflüssig. Wenn Sie wollen Token trennen von Kopf- und Ruhe, ich glaube, Sie mögen diese wahrscheinlich etwas wollen

tr_all = table.findAll("tr") 
header = tr_all[0] 
tr_rest = tr_all[1:] 
tds_rest = [] 
header_data = [td.get_text().encode('utf-8') for td in header] 

for tr in tr_rest: 
    tds_rest.append([td.get_text() for td in tr.findAll("td")]) 

und in Bezug auf dritte Frage

Is it possible to edit this code to add table information from the first row to the end of the table?

Gegeben Ihre gewünschte Ausgabe in den Kommentaren unten:

rows_all = table.findAll("tr") 
header = rows_all[0] 
rows = rows_all[1:] 

data = [] 
for row in rows: 
    for td in row: 
     try: 
      data.append(td.get_text()) 
     except AttributeError: 
      continue 
print(data) 

# or more or less same as above, oneline 
data = [td.get_text() for row in rows for td in row.findAll("td")] 

ergibt

[u'active', u'Active', u'This account is active and may be used.', u'inactive', u'Inactive', u'This account is inactive and should not be used to track financial information.'] 
+0

danke! mein gewünschter Output ist folgender: [u'active ', u'Active', u'Dieser Account ist aktiv und kann verwendet werden. ', u'inactive', u'Inactive ', u'Dieser Account ist inaktiv und sollte nicht sein zum Verfolgen von Finanzinformationen. '] – Mary

+0

Die Ausgabe Ihres Codes enthält nur [' aktiv ',' Aktiv ',' inaktiv ',' Inaktiv '] – Mary

+0

Perfekter Code! Ich schätze deine Zeit sehr – Mary

0

JustMe hat diese Frage richtig beantwortet. Eine weitere äquivalente Variante wäre:

import bs4 

text = """ 
<table class="codes"><tr><td><b>Code</b> 
</td><td><b>Display</b></td><td><b>Definition</b></td> 
</tr><tr><td>active<a name="active"> </a></td> 
<td>Active</td><td>This account is active and may be used.</td></tr> 
<tr><td>inactive<a name="inactive"> </a></td> 
<td>Inactive</td><td>This account is inactive 
and should not be used to track financial information.</td></tr></table>""" 

table = bs4.BeautifulSoup(text) 
tr_all = table.findAll("tr")[1:] 
# critical line: 
tds_all = [ td.get_text() for each_tr in tr_all for td in each_tr.findAll("td")] 
# and after that unchanged: 
table_info= [data.encode('utf-8') for data in tds_all] 

# for control: 
print(table_info) 

Diese seltsame Konstruktion in der kritischen Linie dient als Abflachung der Liste der Liste ‚tds_all‘. Lambda z: [x für y in z für x in y] flacht die Liste der Liste z ab. Ich ersetzte x und y und z entsprechend dieser spezifischen Situation.

Eigentlich kam ich dazu, weil ich als Zwischenschritt die kritische Zeile hatte: tds_all = [[td.get_text() für td in jedem_tr.findAll ("td")] für each_tr in tr_all] , das eine Liste von Listen für tds_all erzeugt: [[u'active ', u'Active', u 'Dieser Account ist aktiv und kann verwendet werden.]], [ u'inactive ', u'Inactive', u'Dieser Account ist inaktiv \ n und sollte nicht zum Verfolgen von Finanzinformationen verwendet werden. ']] Um dies zu reduzieren, braucht man [x für y in z für x in y] Zusammensetzung. Aber dann dachte ich, warum nicht diese Struktur direkt auf die kritische Linie anwenden und sie damit abflachen?

z ist die Liste der bs4-Objekte (tr_all). In diesem 'for ... in ...'-Konstrukt wird each_tr (ein bs4-Objekt) aus der Liste' tr_all 'genommen, und das each_tr-Objekt erzeugt im dahinterliegenden' for-in'-Konstrukt eine Liste von allem 'td' stimmt mit dem Ausdruck each_tr.findAll ("td") überein, aus dem jede Übereinstimmung "td" durch diese hinter 'for ... in ...'-Schleife isoliert ist, und ganz am Anfang dieser Listen-Session steht Was sollte dann in der endgültigen Liste gesammelt werden: der Text von diesem Objekt isoliert ("td.get_text()"). Und diese resultierende endgültige Liste wird td_all zugewiesen.

Das Ergebnis dieses Codes ist diese Ergebnisliste:

['active ', 'Active', 'This account is active and may be used.', 'inactive ', 'Inactive', 'This account is inactive\n and should not be used to track financial information.'] 

Die beiden längeren Elemente im Beispiel fehlten ist von JustMe. Ich denke, Mary, du willst sie haben, oder?