您好:
參可書籍範例
要到
driver.get("http://stats.nba.com/players/traditional/?sort=PTS&dir=-1")
去抓資料以下程式碼中
1.應該是版本問題,select_one 位置不對所以抓不到
ValueError: No tables found
請問,一般要如何去解析 這一個表格的路徑?
By.XPATH 後面的路徑 與 select_one 有相關性?
謝謝
但是
pages_remaining = True
page_num = 1
while pages_remaining:
# 使用Beautiful Soup剖析HTML網頁
soup = BeautifulSoup(driver.page_source, "lxml")
table = soup.select_one("body > main > div.stats-container__inner > div > div.row > div > div > nba-stat-table > div.nba-stat-table > div.nba-stat-table__overflow > table")
df = pd.read_html(str(table))
# print(df[0].to_csv())
df[0].to_csv("ALL_players_stats" + str(page_num) + ".csv")
print("儲存頁面:", page_num)
try:
# 自動按下一頁按鈕
next_link = driver.find_element( By.XPATH ,'/html/body/main/div[2]/div/div[2]/div/div/nba-stat-table/div[3]/div/div/a[2]')
next_link.click()
time.sleep(5)
if page_num < 11:
page_num = page_num + 1
else:
pages_remaining = False
except Exception:
pages_remaining = False
1.應該是版本問題,select_one 位置不對所以抓不到
ValueError: No tables found
請問,一般要如何去解析 這一個表格的路徑?
以你這邊的例子來說,不是你想的那樣,而是因為網頁載入後會先彈出一個讓使用者按同意的按鈕,你沒處理這部份,後面的表格還沒載入(這是動態載入的表格)就會有 No tables found的錯誤,解決參考如下:
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
url="http://stats.nba.com/players/traditional/?sort=PTS&dir=-1"
driver.get(url) # 進網站首頁
btn = driver.find_element( By.XPATH ,"/html/body/div[3]/div[2]/div/div[1]/div/div[2]/div/button[1]")
btn.click() # 要先按我同意
到這裡為止才算真正看到表格內容刷新,接著就可以獲取表格內容:
table = driver.find_element( By.XPATH ,"/html/body/div[1]/div[2]/div[2]/div[3]/section[2]/div/div[2]/div[3]/table")
#print(table.get_attribute('innerHTML'))
print(table.text)
2.By.XPATH 後面的路徑 與 select_one 有相關性?
以你附的代碼來看它們是各個獨立的,前面的 select_one 是獲取 table 的 html 原始碼(但因為沒處理要先按我同意的部份造成 table未載入而失敗)。
By.XPATH是按Next Page的按鈕。
用 Python Selenium 寫網頁爬虫有個小妙招跟您分享,那就是用 Visual Studio Code + jupyter notebook,這裡有個簡單網頁教學:
https://ccutmis.github.io/study-coding/vscode-for-python.htm#ch4
這樣用的好處就是可以分段寫,以上面的範例來說,我的測試.ipynb檔案內容為:
第一段,開啟Chrome視窗進入網頁,按下我同意...
第二段,取得表格內容...
這樣就不用一再重覆執行第一段的 driver.get(url)
只需修改第二段的內容再按執行就能看到測試的內容,提供您參考,樓上的網友提到的 playright 是比較新的技術,有空也可以研究一下。
您好:
(1) 我參考您的第一個問題範例
btn = driver.find_element( By.XPATH ,"/html/body/div[3]/div[2]/div/div[1]/div/div[2]/div/button[1]")
這一段,結果 沒找到您說的按鈕
我改用
btn = driver.find_element( By.ID,"onetrust-accept-btn-handler")
可以用
接下來再用您的
table = driver.find_element( By.XPATH ,"/html/body/div[1]/div[2]/div[2]/div[3]/section[2]/div/div[2]/div[3]/table")
是有抓到您50筆資料
但,若改用 原本範例
要再去抓 下筆 按鈕
就有抓不到
所以還是想了解有什麼方式可以抓 路徑
謝謝
抓路徑的方式只能多試了,就如同樓上網友說的多利用瀏覽器的開發者工具,我用這個方式抓到按下筆按鈕:
再來就是思考怎麼讓它一次性跑全部表格內容,以這網頁為例,頁面上可以看它有列出共有幾頁(Ex:of 5),所以我用同樣的方式抓到它的 xpath,然後取得文字後將它轉成數值,再用for迴圈重覆去點下筆按鈕,但這邊有個要特別注意的地方是,按了下筆按鈕後,要暫停一下讓網頁表格載入新的內容(它是動態載入的)我這邊是用 time.sleep(5) 讓它停五秒,就可以抓到全部表格內容了,以下是測試過可運作的代碼:
import time
row_txt=driver.find_element( By.XPATH, "/html/body/div[1]/div[2]/div[2]/div[3]/section[2]/div/div[2]/div[2]/div[1]/div[4]")
page_num=int(row_txt.text.replace("of ",""))
for i in range(0,page_num,1):
print(f"Page{i+1}")
table = driver.find_element( By.XPATH ,"/html/body/div[1]/div[2]/div[2]/div[3]/section[2]/div/div[2]/div[3]/table")
print(table.text) #印出表格內容
btn = driver.find_element( By.XPATH ,"/html/body/div[1]/div[2]/div[2]/div[3]/section[2]/div/div[2]/div[2]/div[1]/div[5]/button[2]")
btn.click() #按下頁
time.sleep(5) #休5秒
您好:
謝謝您,您的方式可以
不過想再請教一下
原本他是用BeautifulSoup(driver.page_source, "lxml")
取得網頁TABLE內容
再用 df = pd.read_html(str(table))
轉成要匯出的內容
但是現在改用 table.text ,
他應該已經是一行一行的資料了
這時候試過
df=pd.read_csv(table.text)
等
似乎都無法過
這一段,又需要那些技巧呢?
謝謝
我用你在提問本文裡面附的代碼稍作修改如下:
import time
from bs4 import BeautifulSoup
import pandas as pd
row_txt=driver.find_element( By.XPATH, "/html/body/div[1]/div[2]/div[2]/div[3]/section[2]/div/div[2]/div[2]/div[1]/div[4]")
page_num=int(row_txt.text.replace("of ",""))
for i in range(0,page_num,1):
print(f"Page{i+1}")
soup = BeautifulSoup(driver.page_source, "lxml")
table = soup.select_one("table.Crom_table__p1iZz")
df=pd.read_html(str(table))
df[0].to_csv("ALL_players_stats" + str(i+1) + ".csv")
btn = driver.find_element( By.XPATH ,"/html/body/div[1]/div[2]/div[2]/div[3]/section[2]/div/div[2]/div[2]/div[1]/div[5]/button[2]")
btn.click()
time.sleep(5)
其實這題的作法有好幾種,第一種是上面的範例,第二種是已經有它的逐行text內容其實也可以直接寫成csv文字檔,當然還有第三種...略。
soup.select_one() 裡面的參數是 CSS SELECTOR,我用開發者工具查了一下,目標表格有個className="Crom_table__p1iZz",所以不用寫的落落長,直接用 "table.Crom_table__p1iZz" 應該就可以抓到正確的table。
謝謝您。
原本有想要抓TABLE,但用滑鼠點,一直找不到.....
下次要認真找
不過我還是很好奇,
用您最原先的方式,要如何用pandas 輸出CSV
我比較兩種table.text
他是有差異的
#table = soup.select_one("table.Crom_table__p1iZz") # <TABLE class="Crom_table__p1iZz" >
print(table.text) #印出表格內容
IngramNOP2640436.414.34.813.834.50.52.025.04.34.889.50.34.34.53.32.31.01.32.029.00.00.0-11.0
table = driver.find_element( By.XPATH ,"/html/body/div[1]/div[2]/div[2]/div[3]/section[2]/div/div[2]/div[3]/table")
print(table.text) #印出表格內容
50 Brandon Ingram NOP 26 4 0 4 36.4 14.3 4.8 13.8 34.5 0.5 2.0 25.0 4.3 4.8 89.5 0.3 4.3 4.5 3.3 2.3 1.0 1.3 2.0 29.0 0.0 0.0 -11.0
原本有想要抓TABLE,但用滑鼠點,一直找不到.....
你是用下圖的方法抓嗎? Step1. 點開發者工具左上角的小箭頭 Step.2 移到想找的網頁元素位置
用這方式很好找才是,只是要仔細看清楚是不是正確的位置,因為有些網頁標籤包來包去的...。
用您最原先的方式,要如何用pandas 輸出CSV
我比較兩種table.text
他是有差異的
原先我用那個方式是想說簡單列出表格內容,就知道是不是撈到正確的資訊,我在那一行的前面還有註解一行是可以取得表格內容的html原始碼,不知你有沒有留意到:
#print(table.get_attribute('innerHTML'))
達成目的的方法不只一種,只要能達成目的的就是好方法,以這邊來說,你最終要的是輸出CSV,既已獲得表格內容,就不一定需要用到 pandas了(當然這要看情況而定,這裡是可以不用),
CSV是用逗點跟換行把資料逐行逐行記錄下來的一種資料格式,例如: "111,222,333,444,555\n",
用 print(table.text) 得到的資料如下:
PLAYER TEAM AGE GP W L MIN PTS FGM FGA FG% 3PM 3PA 3P% FTM FTA FT% OREB DREB REB AST TOV STL BLK PF FP DD2 TD3 +/-
1 Jaden Ivey DET 22 1 1 0 22.9 22.0 6.0 8.0 75.0 3.0 3.0 100 7.0 9.0 77.8 2.0 0.0 2.0 1.0 0.0 0.0 1.0 2.0 28.9 0.0 0.0 -1.0
...略...
它是逐行列出內容,通常只需要留意內容有沒有逗點(,),有的話要把它轉成別的,沒有的話再把分隔符號(這裡是空格)轉成逗點,然後輸出文字檔即完成,例如:
source_ls = table.text.split("\n")
# print(source_ls)
with open("test1.csv","w+",encoding="utf-8") as f:
for ln in source_ls:
tmp = ln.replace(",","").replace(" ",",")
# 把逗點去掉,再把所有空格轉成逗點
f.writelines(f"{tmp}\n")
上面的範例結果會輸出一個 "test1.csv" ,看起來沒問題,但仔細一對照下就會看到裡面的內容有問題,這是因為球員名字通常是好幾個單字中間用空格作分隔,球員名字後面接著的是隊名縮寫,也是用空格作分隔,用上面的方式處理的話,會多出好幾欄原本不該有的欄位,導致資料變成廢料,那該怎麼解決呢? 我通常是儘量不用這方式處理的,我個人比較偏好分析html原始碼,直接用它撈資料(搭配RegEx),既然這裡提了,就用RegEx試一下,把名字跟隊名的部份分隔開,以下是範例:
import re
with open("test2.csv","w+",encoding="utf-8") as f:
for ln in source_ls:
re_ls = re.findall('^\d{1,3} (.*) [A-Z]{3} \d{1,3}', ln)
if len(re_ls)>0 and re_ls[0]!='':
tmp = ln.replace(re_ls[0],re_ls[0].replace(" ","_")).replace(" ",",")
print(tmp) # 把球員名稱中間的空格替換成"_"之後再把所有空格替換成",""
f.writelines(f"{tmp}\n")
如果對 RegEx 不熟的話,可能看最後這個範例會有點看不懂,也是沒關係的,你有興趣的話就去找 Python RegEx 的教學來看,很多技巧都是基本功的延伸,像這邊我不需要 panads 也可以整理數據並輸出為 csv 檔,當然有的人可能會覺得這是在自找苦吃(現成的模組不用),這裡只是舉個例子,實際情況該怎麼解題就需要自己動動腦了。
您好:
謝謝
你是用下圖的方法抓嗎? Step1. 點開發者工具左上角的小箭頭 Step.2 移到想找的網頁元素位置
對,我是用這方式去找, 箭頭是比較好找
但 TABLE 這一區塊,點半天點不到 TABLE
謝謝您的指導!
像這種爬蟲範例最常發生的問題就是網頁改版了,我通常會先去找一下出版社或作者的網頁,看有沒有更新的程式碼,若沒有更新的程式碼,您也可以向出版社或作者提出您的問題,當然會不會有回應就不一定啦~~
因為有段時間沒碰Python了,爬蟲也沒特別去研究,以下的方法是我可能會去嘗試的方向,僅供參考:
1.我會將soap內容print出來,複製到文字編輯器去,把XML內容排列成下方的格式,這樣就可以知道table到底在那個path上,當然這是個笨方法,偶一為之還可以,如果您以後還會持續處理這樣的問題,建議可以google一下,找個自動能將網頁自動格式化的工具來協助
<html>
<body>
...
</body>
</html>
2.直接用瀏覽器打開您要抓取的網頁,按F12進入網頁開發者工具,之前看過其他人都是從這裡去找網頁相關問題的,建議您花點時間學好這個工具,只要有在作網頁相關的工作,絕對是會經常用到。
python 爬蟲對於初學者用 playwright for python 比較簡單
錄製動作都幫你做好了 ........
只要重新播放就好
ps : python版本要求3.7+以上才可安裝
pip install playwright
python -m playwright install
playwright install
python -m playwright codegen --target python -o my.py -b chromium https://www.google.com
再配合 BeautifulSoup 抓取 所需資訊
例如:
soup = BeautifulSoup(page.content(),'html.parser')
element = soup.find("a", {"id": "totalpayed"})
要用 開發者工具 找你要看的