您好:参可书籍范例要到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
3 个回答
- 旧至新
- 新至旧
- 最高Like数
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 是比较新的技术,有空也可以研究一下。
-
7 -
-
看更多先前的...收起先前的...
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笔资料
但,若改用 原本範例
要再去抓 下笔 按钮
就有抓不到
所以还是想了解有什么方式可以抓 路径
谢谢
我改用
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秒
再来就是思考怎么让它一次性跑全部表格内容,以这网页为例,页面上可以看它有列出共有几页(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)
等
似乎都无法过
这一段,又需要那些技巧呢?
谢谢
但是现在改用 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。
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
不过我还是很好奇,
用您最原先的方式,要如何用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 档,当然有的人可能会觉得这是在自找苦吃(现成的模组不用),这里只是举个例子,实际情况该怎么解题就需要自己动动脑了。
你是用下图的方法抓吗? 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
谢谢您的指导!
你是用下图的方法抓吗? 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"})
要用 开发者工具 找你要看的
-
1 -
-
noway
iT邦研究生 1 级 ‧
2024-10-06 18:42:14
您好:
这后续会找时间学习!
谢谢
修改