Python BeautifulSoup tabla de análisis

89

Estoy aprendiendo Python requestsy BeautifulSoup. Como ejercicio, he optado por escribir un analizador rápido de multas de estacionamiento de la ciudad de Nueva York. Puedo obtener una respuesta html que es bastante fea. Necesito agarrar lineItemsTabley analizar todas las entradas.

Puedes reproducir la página yendo aquí: https://paydirect.link2gov.com/NYCParking-Plate/ItemSearche ingresando una NYplacaT630134C

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

¿Puede alguien ayudarme? La simple búsqueda de todos trno me lleva a ninguna parte.

Cmag
fuente
En una lectura más cercana, no estoy seguro de cuál es tu pregunta. ¿Puede aclarar exactamente con qué parte necesita ayuda?
TML
Pregunta enlaces rotos: A continuación, se muestra un ejemplo práctico para una <tabla> genérica.
eusoubrasileiro

Respuestas:

171

Aqui tienes:

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

Esto te da:

[ [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 par de cosas a tener en cuenta:

  • La última fila del resultado anterior, el Monto de pago no forma parte de la tabla, pero así es como se presenta la tabla. Puede filtrarlo comprobando si la longitud de la lista es inferior a 7.
  • La última columna de cada fila deberá manejarse por separado ya que es un cuadro de texto de entrada.
Shaktimaan
fuente
5
me pregunto por qué funciona para usted ... lo entiendorows = table_body.find_all('tr') AttributeError: 'NoneType' object has no attribute 'find_all'
Cmag
@Cmag ¿Estás usando Beautiful Soup 4?
shaktimaan
1
Reemplazar find_allconfindAll
user2314737
3
@ user2314737 BS admite tanto el caso de camello como la notación de subrayado. Utilizo un guión bajo que está en sintonía con las pautas de codificación de Python.
Shaktimaan
2
Ok, resolví mi error: en la vista de inspección de html, muestra tbody, sin embargo, cuando imprimí el valor, table = soup.find('table', attrs={'class':'analysis'})no mostraba tbody allí, así que simplemente encontrar td y tr hizo el trabajo. Entonces, según yo, la causa del error AttributeError: 'NoneType' object has no attribute 'find_all'es cuando pasamos una etiqueta o campo que no está en el html de la página.
Umesh Kaushik
23

Resuelto, así es como analiza sus resultados 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
Cmag
fuente
14

Actualización: 2020

Si un programador está interesado solo en analizar la tabla de la página web, puede utilizar el método pandas pandas.read_html .

Digamos que queremos extraer la tabla de datos del PIB del sitio web: https://worldpopulationreview.com/countries/countries-by-gdp/#worldCountries

Luego, los siguientes códigos hacen el trabajo a la perfección (sin necesidad de una sopa hermosa y un html elegante):

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()

Salida

Primeras cinco líneas de la tabla del sitio web

Bhishan Poudel
fuente
De acuerdo, ¡este es claramente el mejor enfoque a partir de 2020!
kfmfe04
1
Solo si ya usa pandas en algún lugar de su proyecto. Demasiadas dependencias para una tabla
Сергей Яхницкий
3

Aquí hay un ejemplo de trabajo para un genérico <table>. ( enlaces de preguntas rotos )

Extrayendo la tabla de aquí países por PIB (Producto Interno Bruto).

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

La tableDataTextfunción analiza un segmento html iniciado con una etiqueta <table> seguida de varias <tr>(filas de tabla) y <td>etiquetas internas (datos de tabla). Devuelve una lista de filas con columnas internas. Acepta solo uno <th>(encabezado / datos de la tabla) en la primera fila.

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

Usándolo obtenemos (primeras dos filas).

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

Eso se puede transformar fácilmente en pandas.DataFrameherramientas más avanzadas.

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

Salida de tabla html de pandas DataFrame

eusoubrasileiro
fuente