Estoy aprendiendo Python requests
y 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 lineItemsTable
y analizar todas las entradas.
Puedes reproducir la página yendo aquí: https://paydirect.link2gov.com/NYCParking-Plate/ItemSearch
e ingresando una NY
placaT630134C
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 tr
no me lleva a ninguna parte.
python
beautifulsoup
Cmag
fuente
fuente
Respuestas:
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:
fuente
rows = table_body.find_all('tr') AttributeError: 'NoneType' object has no attribute 'find_all'
find_all
confindAll
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 errorAttributeError: 'NoneType' object has no attribute 'find_all'
es cuando pasamos una etiqueta o campo que no está en el html de la página.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
fuente
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
fuente
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
tableDataText
funció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.DataFrame
herramientas más avanzadas.import pandas as pd dftable = pd.DataFrame(list_table[1:], columns=list_table[0]) dftable.head(4)
fuente