python - Pandas read_html() 没有在表格中给出我的所有条目
问题描述
我正在构建一个网络爬虫,它登录到经过身份验证的网页,导航到一个表格,并每分钟抓取一次这个表格。网页上的表格会自动更新为新条目。网页如下所示:
我想刮掉页面上的 RANKING 表。到目前为止,我已经通过使用:
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]
结果,我的抓取管道容易出错。因此,我在问是否有更好的方法来刮这个表,更健壮?结果必须是 Pandas DataFrame。该表的 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
推荐阅读
- passbook - 无效数据错误读取通过**********。清单签名未成功验证。你能告诉这个错误可能意味着什么吗?
- python - 如何使用给定的 bin 和频率值绘制 CDF 或直方图?Python
- r - Bioconductor 上所有可用软件包的列表
- python - 从 pandas 访问器访问 pandas DataFrame
- php - 如何在 API 中使用 ajax 将 laravel 表单数据发布到控制器
- python - 用于获取信息的 Vsphere API 脚本
- mysql - 我们可以在连接表时在连接条件中使用 Case When created 列吗
- javascript - 当我启动我的 React-Native 应用程序时如何修复我的错误?
- c++ - 保持 boost::allocators 作为类成员。可以使用它们来初始化容器
- html - 引导模式未显示在操作中