Python BeautifulSoup Parsing-Tabelle

89

Ich lerne Python requestsund BeautifulSoup. Für eine Übung habe ich mich entschieden, einen schnellen Parser für Parktickets in NYC zu schreiben. Ich kann eine HTML-Antwort erhalten, die ziemlich hässlich ist. Ich muss mir die schnappen lineItemsTableund alle Tickets analysieren.

Sie können die Seite reproduzieren, indem Sie hier gehen https://paydirect.link2gov.com/NYCParking-Plate/ItemSearchund eine NYPlatte eingebenT630134C

soup = BeautifulSoup(plateRequest.text)
#print(soup.prettify())
#print soup.find_all('tr')

table = soup.find("table", { "class" : "lineItemsTable" })
for row in table.findAll("tr"):
    cells = row.findAll("td")
    print cells

Kann mir bitte jemand helfen? Einfaches Suchen für alle trbringt mich nicht weiter.

Cmag
quelle
Bei näherer Betrachtung bin ich mir nicht sicher, was Ihre Frage ist. Können Sie genau klären, bei welchem ​​Teil Sie Hilfe benötigen?
TML
Frage Links-defekt: Unten ein funktionierendes Beispiel für eine generische <Tabelle>.
eusoubrasileiro

Antworten:

173

Bitte schön:

data = []
table = soup.find('table', attrs={'class':'lineItemsTable'})
table_body = table.find('tbody')

rows = table_body.find_all('tr')
for row in rows:
    cols = row.find_all('td')
    cols = [ele.text.strip() for ele in cols]
    data.append([ele for ele in cols if ele]) # Get rid of empty values

Dies gibt Ihnen:

[ [u'1359711259', u'SRF', u'08/05/2013', u'5310 4 AVE', u'K', u'19', u'125.00', u'$'], 
  [u'7086775850', u'PAS', u'12/14/2013', u'3908 6th Ave', u'K', u'40', u'125.00', u'$'], 
  [u'7355010165', u'OMT', u'12/14/2013', u'3908 6th Ave', u'K', u'40', u'145.00', u'$'], 
  [u'4002488755', u'OMT', u'02/12/2014', u'NB 1ST AVE @ E 23RD ST', u'5', u'115.00', u'$'], 
  [u'7913806837', u'OMT', u'03/03/2014', u'5015 4th Ave', u'K', u'46', u'115.00', u'$'], 
  [u'5080015366', u'OMT', u'03/10/2014', u'EB 65TH ST @ 16TH AV E', u'7', u'50.00', u'$'], 
  [u'7208770670', u'OMT', u'04/08/2014', u'333 15th St', u'K', u'70', u'65.00', u'$'], 
  [u'$0.00\n\n\nPayment Amount:']
]

Einige Dinge zu beachten:

  • In der letzten Zeile der obigen Ausgabe ist der Zahlungsbetrag kein Teil der Tabelle, aber so ist die Tabelle aufgebaut. Sie können es herausfiltern, indem Sie überprüfen, ob die Länge der Liste weniger als 7 beträgt.
  • Die letzte Spalte jeder Zeile muss separat behandelt werden, da es sich um ein Eingabetextfeld handelt.
Shaktimaan
quelle
6
Ich frage mich, warum es für Sie funktioniert ... Ich bekommerows = table_body.find_all('tr') AttributeError: 'NoneType' object has no attribute 'find_all'
Cmag
@Cmag Verwenden Sie Beautiful Soup 4?
Shaktimaan
1
Ersetzen find_alldurchfindAll
user2314737
4
@ user2314737 BS unterstützt sowohl die Kamelfall- als auch die Unterstrichnotation. Ich verwende einen Unterstrich, der den Python-Codierungsrichtlinien entspricht.
Shaktimaan
3
Ok, ich habe meinen Fehler behoben: In der Inspektionsansicht von HTML wird tbody angezeigt, aber als ich den Wert druckte table = soup.find('table', attrs={'class':'analysis'}), wurde dort kein tbody angezeigt, also einfach td und tr zu finden, hat den Job erledigt. Meiner Meinung nach liegt die Fehlerursache darin, AttributeError: 'NoneType' object has no attribute 'find_all'dass wir ein Tag oder Feld übergeben, das nicht im HTML-Code der Seite enthalten ist.
Umesh Kaushik
23

Gelöst, so analysieren Sie ihre HTML-Ergebnisse:

table = soup.find("table", { "class" : "lineItemsTable" })
for row in table.findAll("tr"):
    cells = row.findAll("td")
    if len(cells) == 9:
        summons = cells[1].find(text=True)
        plateType = cells[2].find(text=True)
        vDate = cells[3].find(text=True)
        location = cells[4].find(text=True)
        borough = cells[5].find(text=True)
        vCode = cells[6].find(text=True)
        amount = cells[7].find(text=True)
        print amount
Cmag
quelle
17

Update: 2020

Wenn ein Programmierer nur daran interessiert ist, Tabellen von Webseiten zu analysieren, kann er die Pandas-Methode verwenden pandas.read_html.

Angenommen, wir möchten die BIP-Datentabelle von der Website extrahieren: https://worldpopulationreview.com/countries/countries-by-gdp/#worldCountries

Dann macht der folgende Code den Job perfekt (keine Notwendigkeit für schöne Suppe und ausgefallene HTML):

import pandas as pd
import requests

url = "https://worldpopulationreview.com/countries/countries-by-gdp/#worldCountries"

r = requests.get(url)
df_list = pd.read_html(r.text) # this parses all the tables in webpages to a list
df = df_list[0]
df.head()

Ausgabe

Die ersten fünf Zeilen der Tabelle von der Website

Bhishan Poudel
quelle
Einverstanden - dies ist eindeutig der beste Ansatz ab 2020!
kfmfe04
2
Nur wenn Sie bereits irgendwo in Ihrem Projekt Pandas verwenden. Zu viele Abhängigkeiten für eine Tabelle
21ергей Яхницкий
haha du hast meinen beispielhaften Balg kopiert und die Antwort verbessert. Zumindest wollte ich wissen, dass Pandas eine solche Methode haben. Nett!
eusoubrasileiro
Ja, ich habe die URL des BIP aus Ihrem Beispiel verwendet. Ja, wenn Sie schnelle Methoden mögen, können wir einfach pd.read_htmlanstelle des ganzen Tanzens von Anfragen und schönen Suppen verwenden.
Bhishan Poudel
4

Hier ist ein Arbeitsbeispiel für ein Generikum <table>. ( Frage Links-defekt )

Extrahieren der Tabelle von hier Ländern durch das BIP (Bruttoinlandsprodukt).

htmltable = soup.find('table', { 'class' : 'table table-striped' })
# where the dictionary specify unique attributes for the 'table' tag

Die tableDataTextFunktion analysiert ein HTML-Segment, das mit einem Tag beginnt, <table> gefolgt von mehreren <tr>(Tabellenzeilen) und inneren <td>(Tabellendaten) Tags. Es wird eine Liste von Zeilen mit inneren Spalten zurückgegeben. Akzeptiert nur einen <th>(Tabellenkopf / Daten) in der ersten Zeile.

def tableDataText(table):       
    rows = []
    trs = table.find_all('tr')
    headerow = [td.get_text(strip=True) for td in trs[0].find_all('th')] # header row
    if headerow: # if there is a header row include first
        rows.append(headerow)
        trs = trs[1:]
    for tr in trs: # for every table row
        rows.append([td.get_text(strip=True) for td in tr.find_all('td')]) # data row
    return rows

Damit bekommen wir (erste zwei Zeilen).

list_table = tableDataText(htmltable)
list_table[:2]

[['Rank',
  'Name',
  "GDP (IMF '19)",
  "GDP (UN '16)",
  'GDP Per Capita',
  '2019 Population'],
 ['1',
  'United States',
  '21.41 trillion',
  '18.62 trillion',
  '$65,064',
  '329,064,917']]

Das kann leicht in ein pandas.DataFramefür fortgeschrittenere Werkzeuge umgewandelt werden.

import pandas as pd
dftable = pd.DataFrame(list_table[1:], columns=list_table[0])
dftable.head(4)

pandas DataFrame HTML-Tabellenausgabe

eusoubrasileiro
quelle
0
from behave import *
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
import pandas as pd
import requests
from bs4 import BeautifulSoup
from tabulate import tabulate

class readTableDataFromDB: 
    def LookupValueFromColumnSingleKey(context, tablexpath, rowName, columnName):
        print("element present readData From Table")
        element = context.driver.find_elements_by_xpath(tablexpath+"/descendant::th")
        indexrow = 1
        indexcolumn = 1
        for values in element:
            valuepresent = values.text
            print("text present here::"+valuepresent+"rowName::"+rowName)
            if valuepresent.find(columnName) != -1:
                 print("current row"+str(indexrow) +"value"+valuepresent)
                 break
            else:
                 indexrow = indexrow+1    

        indexvalue = context.driver.find_elements_by_xpath(
            tablexpath+"/descendant::tr/td[1]")
        for valuescolumn in indexvalue:
            valuepresentcolumn = valuescolumn.text
            print("Team text present here::" +
                  valuepresentcolumn+"columnName::"+rowName)
            print(indexcolumn) 
            if valuepresentcolumn.find(rowName) != -1:
                print("current column"+str(indexcolumn) +
                      "value"+valuepresentcolumn)
                break
            else:
                indexcolumn = indexcolumn+1

        print("index column"+str(indexcolumn))
        print(tablexpath +"//descendant::tr["+str(indexcolumn)+"]/td["+str(indexrow)+"]")
        #lookupelement = context.driver.find_element_by_xpath(tablexpath +"//descendant::tr["+str(indexcolumn)+"]/td["+str(indexrow)+"]")
        #print(lookupelement.text)
        return context.driver.find_elements_by_xpath(tablexpath+"//descendant::tr["+str(indexcolumn)+"]/td["+str(indexrow)+"]")

    def LookupValueFromColumnTwoKeyssss(context, tablexpath, rowName, columnName, columnName1):
        print("element present readData From Table")
        element = context.driver.find_elements_by_xpath(
            tablexpath+"/descendant::th")
        indexrow = 1
        indexcolumn = 1
        indexcolumn1 = 1
        for values in element:
            valuepresent = values.text
            print("text present here::"+valuepresent)
            indexrow = indexrow+1
            if valuepresent == columnName:
                print("current row value"+str(indexrow)+"value"+valuepresent)
                break

        for values in element:
            valuepresent = values.text
            print("text present here::"+valuepresent)
            indexrow = indexrow+1
            if valuepresent.find(columnName1) != -1:
                print("current row value"+str(indexrow)+"value"+valuepresent)
                break

        indexvalue = context.driver.find_elements_by_xpath(
            tablexpath+"/descendant::tr/td[1]")
        for valuescolumn in indexvalue:
            valuepresentcolumn = valuescolumn.text
            print("Team text present here::"+valuepresentcolumn)
            print(indexcolumn)
            indexcolumn = indexcolumn+1
            if valuepresent.find(rowName) != -1:
                print("current column"+str(indexcolumn) +
                      "value"+valuepresentcolumn)
                break
        print("indexrow"+str(indexrow))
        print("index column"+str(indexcolumn))
        lookupelement = context.driver.find_element_by_xpath(
            tablexpath+"//descendant::tr["+str(indexcolumn)+"]/td["+str(indexrow)+"]")
        print(tablexpath +
              "//descendant::tr["+str(indexcolumn)+"]/td["+str(indexrow)+"]")
        print(lookupelement.text)
        return context.driver.find_element_by_xpath(tablexpath+"//descendant::tr["+str(indexrow)+"]/td["+str(indexcolumn)+"]")
Ritesh Toppo
quelle