Python 熊猫阅读html()不';I don’我不能在表格中列出所有条目

Python 熊猫阅读html()不';I don’我不能在表格中列出所有条目,python,pandas,selenium,beautifulsoup,Python,Pandas,Selenium,Beautifulsoup,我正在构建一个web scraper,它可以登录到一个经过身份验证的网页,导航到一个表,并每分钟对该表进行一次刮取。网页上的表格会自动更新为新条目。以下是该网页的外观: 我想把这页上的排名表刮掉。到目前为止,我是通过以下方式完成这项工作的: df = pd.read_html(driver.page_source)[7] 然而,根据我的经验,我并不总是能得到表中的所有条目(行)。在这种情况下,我可以使用: df = pd.read_html(driver.page_source)[8]

我正在构建一个web scraper,它可以登录到一个经过身份验证的网页,导航到一个表,并每分钟对该表进行一次刮取。网页上的表格会自动更新为新条目。以下是该网页的外观:

我想把这页上的排名表刮掉。到目前为止,我是通过以下方式完成这项工作的:

df = pd.read_html(driver.page_source)[7] 
然而,根据我的经验,我并不总是能得到表中的所有条目(行)。在这种情况下,我可以使用:

df = pd.read_html(driver.page_source)[8] 
df = pd.read_html(driver.page_source)[9] 
or even 
df = pd.read_html(driver.page_source)[10] 
因此,我的刮管容易出错。因此,我想问是否有更好的方法来刮这个表,即更健壮?结果必须是一个数据帧。表的XPath如下所示:

columns = len(driver.find_elements_by_xpath("/html/body/div[2]/div/form[3]/div[2]/div[1]/div/div/div/div[2]/div[4]/section[1]/div[2]/div/div/table/thead/tr[2]/th"))
rows = len(driver.find_elements_by_xpath("/html/body/div[2]/div/form[3]/div[2]/div[1]/div/div/div/div[2]/div[4]/section[1]/div[2]/div/div/table/tbody/tr"))

这是我所有的代码



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import pandas as pd
import time
import schedule

class Skraper:
    """
    Klassen Skraper inneholder metoder som kjører skraperen
    """
    @staticmethod 
    def oppstart():
        """
        Returns:
        driver -- en Chrome webdriver med notifications av, og som navigerer til startsiden.
        """
        driver = webdriver.Chrome()
        url = "http://www.hctiming.com/myphp/resources/login/browse_results.php?live_action=yes&smartphone_action=no" #a redirect to a login page occurs
        driver.get(url)
        driver.find_element_by_id("open").click()

        user = driver.find_element_by_name("username")
        password = driver.find_element_by_name("password")
        user.clear()
        user.send_keys("sorry cannot give you this")
        password.clear()
        password.send_keys("sorry cannot give you this")
        driver.find_element_by_name("submit").click()

        try:
            element = WebDriverWait(driver, 10).until(
                EC.presence_of_element_located((By.LINK_TEXT, "Results Services"))
            )
            element.click()

            element = WebDriverWait(driver, 10).until(
                EC.presence_of_element_located((By.LINK_TEXT, "View Live"))
            )
            element.click()

        except:
            driver.quit()
        
        return driver

    @staticmethod
    def henteTabell():

        #Jeg har satt sleep til 2, slik at jeg er sikker på at tabellen er lastet inn med Ajax før jeg skraper denne.
        #Dette er nok ikke beste praksis, men den funker så langt. Et alternativ er å legge den i en eksplisitt wai

        driver.refresh()
        time.sleep(4)
        #Her bruker jeg pandas read_html() til å lokalisere tabellen på siden. Denne metoden lokaliserer tabeller på en side og lagrer disse som en liste.
        #Jeg fant ut at tabellen jeg ønsker å skrape ligger i listeindex nr 10
        #Jeg assigner denne som en pandas dataframe objekt.
        print("dette er en test")
       # df = pd.read_html(driver.page_source)[7]
       


        print(df)
        
        
        
        #Tabellen er ikke ryddet og klar til analyse enda, så noen trinn må gjøres.
        #Jeg må om COMMENT til string
        df["COMMENT"] = df['COMMENT'].astype(str)
        #Jeg gjør noen replace(), slik at jeg får string verdien som jeg vil ha.
        df["COMMENT"] = df['COMMENT'].str.replace('1', '1')
        df["COMMENT"] = df['COMMENT'].str.replace('2', '2')
        df["COMMENT"] = df['COMMENT'].str.replace('3', '3')
        df["COMMENT"] = df['COMMENT'].str.replace('9', 'STRAIGHT-GLIDING')
        #Jeg renamer kolonnene slik jeg vil ha. D3.JS har problemer med '#', så jeg fjerner disse
        df = df.rename(columns={'COMMENT': 'COURSE'})
        df = df.rename(columns={'RUN#': 'RUN'})
        df = df.rename(columns={'STA#': 'STA'})
        df = df.rename(columns={'BIB#': 'BIB'})
        #Verdiene har i FINISH kolonnen har et veldig rart format fordi jeg skraper to kolonner. Jeg har funnet ut at denne anonyme labda funksjonen gjør nytten.
        #Den fjerner de fire siste stringene for alle verdiene i FINISH kolonnnen.
        #Jeg fant ut at jeg ikke trenger denne funksjonen (under)
        #df['FINISH']# = df['FINISH'].map(lambda x: str(x)[:-3])
        #Det neste jeg gjør er å endre dette fra et string objekt til et float objekt. Deklarerer dette til en ny variabel, siden dette er beste praksis.
        

        df['FINISH'] = pd.to_numeric(df['FINISH'], downcast='float', errors='raise')
        # print(df)

        # #Her velger jeg ut alle Straight-Gliding rundene
        filtrerStraightGliding = df['COURSE'] == 'STRAIGHT-GLIDING'

        velgStraightGliding = df.loc[filtrerStraightGliding].copy()
        # print("test 2")
        # print(velgStraightGliding)

        velgForsteStraightGlidingRunde = velgStraightGliding[velgStraightGliding.groupby(['BIB','COURSE']).cumcount() == 0][['BIB','FINISH']].copy()
        velgForsteStraightGlidingRunde = velgForsteStraightGlidingRunde.rename(columns={'FINISH':'REFERENCE'})

        ferdigstiltDF = pd.merge(df,velgForsteStraightGlidingRunde,on='BIB',how='left')
        ferdigstiltDF['RATIO'] = ferdigstiltDF['FINISH'] - ferdigstiltDF['REFERENCE']
        #Det jeg gjør nå er at jeg sorterer STA fra lav til høy. Dette er et superviktig trinn, hvis ikke vil skriptet mitt ikke fungere. Da vil den beste den dårlisgte rangerte utøveren.
        final_df = ferdigstiltDF.sort_values('STA', ascending=True)
        print(final_df)

        #print(final_df)

        final_df = final_df.iloc[[-1]]
        
        


        final_df.to_csv("lagplot.csv")
        #print(final_df)
        return ferdigstiltDF

driver = Skraper.oppstart()

schedule.every(5).seconds.do(Skraper.henteTabell)

while True:
    schedule.run_pending()
    time.sleep(1)








如果不直接访问页面源代码,很难判断

但是,通过检测表中哪些列包含特定字段,可能会有一个转折点:

#The target table contains the following fields
searched_columns = ["RANK","STA#", "RUN#","BIB#"]

#Grab all the tables of the page
page_tables = pd.read_html(driver.page_source)

#Loop over the page's tables
for table in page_tables:
    #Check if all columns are present in the current table
    if all([x in table.columns for x in searched_columns]):
        df = table
        break

这是一个非常有趣的方法。我发现pandas read_html()提供不同结果的原因是网页使用ajax,并且新表中添加了新条目。这是一个棘手的任务,刮这个页面,但我会尝试你的方法明天。如果你确定的ajax调用,它将更加强大!