python BeautifulSoup ayrıştırma tablosu


89

Python requestsve BeautifulSoup öğreniyorum . Bir egzersiz için, hızlı bir NYC park bileti ayrıştırıcısı yazmayı seçtim. Oldukça çirkin bir html yanıtı alabiliyorum. lineItemsTableTüm biletleri almam ve ayrıştırmam gerekiyor .

Buraya gidip https://paydirect.link2gov.com/NYCParking-Plate/ItemSearchbir NYplaka girerek sayfayı yeniden üretebilirsinizT630134C

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

Lütfen birisi bana yardım edebilir mi? Her trşeyi aramak beni hiçbir yere götürmez.


Daha yakından okuduğumda, sorunun ne olduğundan emin değilim. Tam olarak hangi kısımda yardıma ihtiyacınız olduğunu açıklayabilir misiniz?
TML

soru bağlantıları-bozuk: Genel bir <table> için çalışan bir örneğe aşağı bakın .
eusoubrasileiro

Yanıtlar:


173

Hadi bakalım:

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

Bu size şunları verir:

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

Dikkat edilmesi gereken birkaç nokta:

  • Yukarıdaki çıktıdaki son satır olan Ödeme Tutarı tablonun bir parçası değildir ancak tablo bu şekilde düzenlenir. Listenin uzunluğunun 7'den az olup olmadığını kontrol ederek onu filtreleyebilirsiniz.
  • Her satırın son sütunu, bir giriş metin kutusu olduğu için ayrı ayrı ele alınmalıdır.

6
Neden işe yaradığını merak ediyorum ... Anlıyorumrows = table_body.find_all('tr') AttributeError: 'NoneType' object has no attribute 'find_all'
Cmag

@Cmag Beautiful Soup 4 kullanıyor musunuz?
shaktimaan

1
Değiştir find_allilefindAll
user2314737

4
@ user2314737 BS, hem deve durumunu hem de alt çizgi gösterimini destekler. Python kodlama yönergelerine uygun alt çizgi kullanıyorum.
shaktimaan

3
Tamam, hatamı çözdüm: html'nin incelenmesi görünümünde tbody'i gösteriyor, ancak değerini yazdırdığımda table = soup.find('table', attrs={'class':'analysis'})orada hiç kimse görünmüyor, bu yüzden basitçe td ve tr'yi bulmak işi yaptı. Yani bana göre hata almanın nedeni AttributeError: 'NoneType' object has no attribute 'find_all'sayfanın html'sinde olmayan bir tag veya alanı geçmemizdir.
Umesh Kaushik

23

Çözüldü, sizin html sonuçlarını şu şekilde ayrıştırırsınız:

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

Güncelleme: 2020

Bir programcı yalnızca web sayfasındaki tabloyu ayrıştırmakla ilgileniyorsa, pandalar yöntemini kullanabilir. pandas.read_html .

GDP veri tablosunu web sitesinden çıkarmak istediğimizi varsayalım: https://worldpopulationreview.com/countries/countries-by-gdp/#worldCountries

Ardından aşağıdaki kodlar işi mükemmel bir şekilde yapar (Güzel gruplara ve süslü html'ye gerek yoktur):

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

Çıktı

Web sitesinden tablonun ilk beş satırı


Kabul edildi - bu açıkça 2020 itibariyle en iyi yaklaşım!
kfmfe04

2
Sadece projenizin herhangi bir yerinde pandaları zaten kullanıyorsanız. Bir masa için çok fazla bağımlılık
Aguilar

haha sen benim örneklenen feryatımı kopyaladın ve cevabı geliştirdin. En azından pandaların böyle bir yöntemi olduğunu bilmek hoşuma gitti. Güzel!
eusoubrasileiro

Evet, sizin örneğinizden GSYİH url'sini verirdim. Evet, hızlı yöntemleri seviyorsanız pd.read_html, isteklerin ve güzel grupların tüm dansı yerine kullanabiliriz .
Bhishan Poudel

4

İşte bir jenerik için çalışma örneği <table>. ( soru bağlantıları bozuk )

Tabloyu buradaki ülkelerden GSYİH'ye (Gayri Safi Yurtiçi Hasıla) göre çıkarıyoruz.

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

tableDataTextFonksiyon etiketiyle ait html segmenti ayrıştırır <table> birden ardından <tr>(tablo satır) ve iç <td>(tablo verileri) etiketler. İç sütunları olan satırların bir listesini döndürür. <th>İlk satırda yalnızca birini (tablo başlığı / verileri) kabul eder.

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

Kullanarak (ilk iki sıra) elde ederiz.

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

Bu, pandas.DataFramedaha gelişmiş araçlar için kolayca dönüştürülebilir .

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

pandas DataFrame html tablo çıktısı


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)+"]")
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.