iT邦幫忙

0

python 爬蟲 抓表格問請教

  • 分享至 

  • xImage

您好:
參可書籍範例
要到
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  
froce iT邦大師 1 級 ‧ 2024-10-07 15:38:17 檢舉
不是,你自己也有在寫網頁系統,那CSS selector/XPATH應該都有點概念吧?瀏覽器的開發者模式應該也會用吧?怎麼會去問第1個問題...

第2個問題,這串XPATH應該是下一頁的按鈕的XPATH。
https://www.runoob.com/xpath/xpath-tutorial.html

然後去看一下wait
https://selenium-python-zh.readthedocs.io/en/latest/waits.html
如果只教sleep的話,我覺得這本書可以丟了。翻官方文件自己寫一次比較快。
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
1
ccutmis
iT邦高手 2 級 ‧ 2024-10-05 03:24:18
最佳解答

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 是比較新的技術,有空也可以研究一下。

看更多先前的回應...收起先前的回應...
noway iT邦研究生 1 級 ‧ 2024-10-06 18:48:58 檢舉

您好:
(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筆資料

但,若改用 原本範例
要再去抓 下筆 按鈕
就有抓不到

所以還是想了解有什麼方式可以抓 路徑
謝謝

ccutmis iT邦高手 2 級 ‧ 2024-10-06 21:09:19 檢舉

抓路徑的方式只能多試了,就如同樓上網友說的多利用瀏覽器的開發者工具,我用這個方式抓到按下筆按鈕:

再來就是思考怎麼讓它一次性跑全部表格內容,以這網頁為例,頁面上可以看它有列出共有幾頁(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秒
noway iT邦研究生 1 級 ‧ 2024-10-07 22:43:25 檢舉

您好:
謝謝您,您的方式可以
不過想再請教一下
原本他是用BeautifulSoup(driver.page_source, "lxml")
取得網頁TABLE內容
再用 df = pd.read_html(str(table))
轉成要匯出的內容

但是現在改用 table.text ,
他應該已經是一行一行的資料了
這時候試過
df=pd.read_csv(table.text)

似乎都無法過

這一段,又需要那些技巧呢?
謝謝

ccutmis iT邦高手 2 級 ‧ 2024-10-08 01:35:51 檢舉

我用你在提問本文裡面附的代碼稍作修改如下:

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。

noway iT邦研究生 1 級 ‧ 2024-10-08 21:23:33 檢舉

謝謝您。
原本有想要抓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

ccutmis iT邦高手 2 級 ‧ 2024-10-09 10:00:11 檢舉

原本有想要抓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 檔,當然有的人可能會覺得這是在自找苦吃(現成的模組不用),這裡只是舉個例子,實際情況該怎麼解題就需要自己動動腦了。

noway iT邦研究生 1 級 ‧ 2024-10-09 21:21:06 檢舉

您好:
謝謝

你是用下圖的方法抓嗎? Step1. 點開發者工具左上角的小箭頭 Step.2 移到想找的網頁元素位置

對,我是用這方式去找, 箭頭是比較好找
但 TABLE 這一區塊,點半天點不到 TABLE

謝謝您的指導!

2
sam0407
iT邦大師 1 級 ‧ 2024-10-04 10:29:32

像這種爬蟲範例最常發生的問題就是網頁改版了,我通常會先去找一下出版社或作者的網頁,看有沒有更新的程式碼,若沒有更新的程式碼,您也可以向出版社或作者提出您的問題,當然會不會有回應就不一定啦~~

因為有段時間沒碰Python了,爬蟲也沒特別去研究,以下的方法是我可能會去嘗試的方向,僅供參考:
1.我會將soap內容print出來,複製到文字編輯器去,把XML內容排列成下方的格式,這樣就可以知道table到底在那個path上,當然這是個笨方法,偶一為之還可以,如果您以後還會持續處理這樣的問題,建議可以google一下,找個自動能將網頁自動格式化的工具來協助

<html>
    <body>
    ...
    </body>
</html>

2.直接用瀏覽器打開您要抓取的網頁,按F12進入網頁開發者工具,之前看過其他人都是從這裡去找網頁相關問題的,建議您花點時間學好這個工具,只要有在作網頁相關的工作,絕對是會經常用到。

2
echochio
iT邦高手 1 級 ‧ 2024-10-04 14:21:05

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"})

要用 開發者工具 找你要看的
https://ithelp.ithome.com.tw/upload/images/20241004/20110611Sv0hyKg53J.jpg

noway iT邦研究生 1 級 ‧ 2024-10-06 18:42:14 檢舉

您好:
這後續會找時間學習!
謝謝

我要發表回答

立即登入回答