python BeautifulSoup tabela parsowania

89

Uczę się Pythona requestsi BeautifulSoup. W ramach ćwiczenia postanowiłem napisać szybki parser biletów parkingowych w Nowym Jorku. Jestem w stanie uzyskać odpowiedź HTML, która jest dość brzydka. Muszę pobrać lineItemsTablei przeanalizować wszystkie bilety.

Możesz odtworzyć stronę, przechodząc tutaj: https://paydirect.link2gov.com/NYCParking-Plate/ItemSearchi wprowadzając NYtablicęT630134C

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

Czy ktoś mógłby mi pomóc? Proste szukanie wszystkiego trnie prowadzi mnie do niczego.

Cmag
źródło
Przy bliższym przeczytaniu nie jestem do końca pewien, jakie jest twoje pytanie. Czy możesz dokładnie wyjaśnić, w jakiej części potrzebujesz pomocy?
TML
pytanie links-broken: Poniżej działający przykład dla ogólnej <table>.
eusoubrasileiro

Odpowiedzi:

171

Proszę bardzo:

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

To daje ci:

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

Kilka uwag:

  • Ostatni wiersz powyższego wyniku, Kwota płatności, nie jest częścią tabeli, ale tak jest w tabeli. Możesz ją odfiltrować, sprawdzając, czy długość listy jest mniejsza niż 7.
  • Ostatnia kolumna każdego wiersza będzie musiała być obsługiwana osobno, ponieważ jest to wejściowe pole tekstowe.
shaktimaan
źródło
5
Zastanawiam się, dlaczego to działa dla Ciebie ... Dostajęrows = table_body.find_all('tr') AttributeError: 'NoneType' object has no attribute 'find_all'
Cmag
@Cmag Czy używasz Beautiful Soup 4?
shaktimaan
1
Wymienić find_allzfindAll
user2314737
3
@ user2314737 BS obsługuje zarówno zapis wielbłąda, jak i podkreślenie. Używam podkreślenia, które jest zgodne z wytycznymi kodowania w Pythonie.
shaktimaan
2
Ok, rozwiązałem mój błąd: w widoku HTML pokazuje on tbody, jednak kiedy wydrukowałem jego wartość table = soup.find('table', attrs={'class':'analysis'}), nikogo tam nie było, więc po prostu znalezienie td i tr wykonało zadanie. Więc według mnie przyczyną błędu AttributeError: 'NoneType' object has no attribute 'find_all'jest przekazanie tagu lub pola, którego nie ma w html strony.
Umesh Kaushik
23

Rozwiązany, oto jak analizujesz ich wyniki 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
źródło
14

Aktualizacja: 2020

Jeśli programista jest zainteresowany tylko analizowaniem tabeli ze strony internetowej, może skorzystać z metody pandy pandas.read_html.

Powiedzmy, że chcemy wyodrębnić tabelę danych PKB ze strony internetowej: https://worldpopulationreview.com/countries/countries-by-gdp/#worldCountries

Następnie poniższe kody działają idealnie (nie ma potrzeby pięknego zupy i wyszukanego 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()

Wynik

Pierwsze pięć wierszy tabeli ze strony internetowej

Bhishan Poudel
źródło
Zgoda - to zdecydowanie najlepsze podejście od 2020 roku!
kfmfe04
1
Tylko jeśli używasz już pand gdzieś w swoim projekcie. Za dużo zależności dla jednej tabeli
Сергей Яхницкий
3

Oto działający przykład dla generycznego <table>. ( linki do pytań uszkodzone )

Wyodrębnianie tabelę z tu krajach PKB (Produkt Krajowy Brutto).

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

tableDataTextFunkcja analizuje segment HTML uruchomiony z etykietką <table> , a następnie przez kilka <tr>(wiersze tabeli) i wewnętrzny <td>(dane z tabeli) znaczników. Zwraca listę wierszy z wewnętrznymi kolumnami. Akceptuje tylko jeden <th>(nagłówek / dane tabeli) w pierwszym wierszu.

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

Korzystając z niego otrzymujemy (pierwsze dwa rzędy).

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

Można to łatwo przekształcić w pandas.DataFramebardziej zaawansowane narzędzia.

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

pandy Dane wyjściowe tabeli html DataFrame

eusoubrasileiro
źródło