tabella di analisi python BeautifulSoup


89

Sto imparando Python requestse BeautifulSoup. Per un esercizio, ho scelto di scrivere un rapido parser di biglietti per il parcheggio di New York. Sono in grado di ottenere una risposta html che è piuttosto brutta. Devo prendere il filelineItemsTable e analizzare tutti i biglietti.

Puoi riprodurre la pagina andando qui: https://paydirect.link2gov.com/NYCParking-Plate/ItemSearche inserendo una NYtargaT630134C

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

Qualcuno può aiutarmi per favore? La semplice ricerca di tutto trnon mi porta da nessuna parte.


A una lettura più attenta, non sono sicuro di quale sia la tua domanda. Puoi chiarire esattamente con quale parte hai bisogno di aiuto?
TML

link alla domanda interrotti: sotto un esempio funzionante per un generico <table>.
eusoubrasileiro

Risposte:


173

Ecco qui:

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

Questo ti dà:

[ [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:']
]

Un paio di cose da notare:

  • L'ultima riga nell'output sopra, l'Importo del pagamento non fa parte della tabella ma è così che è disposta la tabella. Puoi filtrarlo controllando se la lunghezza dell'elenco è inferiore a 7.
  • L'ultima colonna di ogni riga dovrà essere gestita separatamente poiché è una casella di testo di input.

6
mi chiedo perché funziona per te ... Ottengorows = table_body.find_all('tr') AttributeError: 'NoneType' object has no attribute 'find_all'
Cmag

@Cmag Stai usando Beautiful Soup 4?
Shaktimaan

1
Sostituisci find_allconfindAll
user2314737

4
@ user2314737 BS supporta sia la notazione camel sia la notazione di sottolineatura. Uso il carattere di sottolineatura che è in sintonia con le linee guida di codifica Python.
shaktimaan

3
Ok ho risolto il mio errore: nella visualizzazione di ispezione di html mostra tbody, tuttavia, quando ho stampato il valore table = soup.find('table', attrs={'class':'analysis'})non mostrava nessuno laggiù, quindi è stato sufficiente trovare td e tr. Quindi secondo me la causa dell'errore AttributeError: 'NoneType' object has no attribute 'find_all'è quando passiamo un tag o un campo che non si trova nell'html della pagina.
Umesh Kaushik

23

Risolto, ecco come analizzi i risultati html:

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

18

Risposta aggiornata

Se un programmatore è interessato solo all'analisi della tabella dalla pagina Web, può utilizzare il metodo panda pandas.read_html .

Supponiamo di voler estrarre la tabella dei dati del PIL dal sito web: https://worldpopulationreview.com/countries/countries-by-gdp/#worldCountries

Quindi i seguenti codici fanno il lavoro perfettamente (non c'è bisogno di beautifulsoup e di fantasia 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()

Produzione

Prime cinque righe della tabella dal sito web


D'accordo: questo è chiaramente l'approccio migliore al 2020!
kfmfe04

2
Solo se usi già i panda da qualche parte nel tuo progetto. Troppe dipendenze per una tabella
Сергей Яхницкий

haha hai copiato il mio esempio di muggito e migliorato la risposta. Beh, almeno mi è piaciuto sapere che i panda hanno un tale metodo. Bello!
eusoubrasileiro

Sì, ho usato l'URL dei dati del PIL dal tuo esempio. Sì, se ti piacciono i metodi veloci, possiamo semplicemente usare al pd.read_htmlposto dell'intera danza di richieste e bella zuppa.
Bhishan Poudel

4

Ecco un esempio funzionante per un generico <table>. ( link alla domanda interrotti )

Estraendo la tabella da qui paesi per PIL (Prodotto Interno Lordo).

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

La tableDataTextfunzione analizza un segmento html iniziato con un tag <table> seguito da più tag <tr>(righe della tabella) e <td>tag interni (dati della tabella). Restituisce un elenco di righe con colonne interne. Accetta solo uno <th>(intestazione / dati della tabella) nella prima riga.

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

Usandolo otteniamo (prime due righe).

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']]

Questo può essere facilmente trasformato in pandas.DataFramestrumenti più avanzati.

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

output della tabella HTML DataFrame panda


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)+"]")
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.