如何点击 SVG 元素?

How can I make click over a SVG element?

我是网络抓取和使用 selenium 的新手我想点击 SVG 元素以访问模态显示的信息 window。

点击此网页篮球场上的一个点或十字:https://www.fiba.basketball/euroleaguewomen/21-22/game/1310/MBA-Moscow-ZVVZ-USK-Praha#tab=shot_chart您将获得一个模态window,其中包含您可以在下一张图片中看到的信息:

我已经找到了这个有效的开发,因为找到了打印他的属性“x”和“y”的值的“svg”元素:

b = self.driver
b.set_window_size(300, 300)
b.get("https://www.fiba.basketball/euroleaguewomen/21-22/game/1310/MBA-Moscow-ZVVZ-USK-Praha#tab=shot_chart")
periods = b.find_element_by_css_selector('g.team-A').find_element_by_css_selector('g.period-list').find_elements_by_css_selector("g.period-item")
for quarter in periods:
    shots = quarter.find_element_by_css_selector("g.shot-list").find_elements_by_css_selector("g.shot-item")
    for item in shots:
        print(f"x: {item.find_element_by_tag_name('svg').get_attribute('x')} - y: {item.find_element_by_tag_name('svg').get_attribute('y')}")
    print("\n")

您可以在此处查看代码的退出:

x: 40.5 - y: 99.5
x: 151.5 - y: 211.5
x: 34.5 - y: 125.5
x: 35.5 - y: 121.5
x: 157.5 - y: 204.5
x: 59.5 - y: 122.5
x: 32 - y: 142
x: 40 - y: 121
x: 27.5 - y: 117.5
x: 164 - y: 124


x: 80.5 - y: 7.5
x: 49.5 - y: 111.5
x: 135.5 - y: 42.5
x: 34.5 - y: 67.5
x: 27.5 - y: 117.5
x: 138 - y: 54
x: 22 - y: 140
x: 119.5 - y: 32.5
x: 135.5 - y: 42.5
x: 154.5 - y: 186.5
x: 37.5 - y: 106.5
x: 39 - y: 117
x: 31 - y: 114
x: 40.5 - y: 117.5
x: 22 - y: 5
x: 46.5 - y: 4.5


x: 20 - y: 125
x: 148.5 - y: 197.5
x: 71.5 - y: 169.5
x: 118 - y: 230
x: 30.5 - y: 263.5
x: 25 - y: 124
x: 135.5 - y: 213.5
x: 82.5 - y: 128.5
x: 40 - y: 119
x: 158.5 - y: 131.5
x: 50.5 - y: 174.5


x: 166.5 - y: 82.5
x: 26 - y: 149
x: 36 - y: 133
x: 114.5 - y: 239.5
x: 48 - y: 222
x: 127.5 - y: 226.5
x: 23 - y: 132
x: 110.5 - y: 107.5
x: 114 - y: 138
x: 15 - y: 260
x: 137.5 - y: 131.5
x: 34 - y: 118
x: 75 - y: 65
x: 54.5 - y: 167.5
x: 30.5 - y: 127.5

但是,如果我尝试点击“svg”组件并添加以下代码:

        point = item.find_element_by_tag_name('svg')
        point.click()

最后,我的代码将是:

b = self.driver
b.set_window_size(300, 300)
b.get("https://www.fiba.basketball/euroleaguewomen/21-22/game/1310/MBA-Moscow-ZVVZ-USK-Praha#tab=shot_chart")
periods = b.find_element_by_css_selector('g.team-A').find_element_by_css_selector('g.period-list').find_elements_by_css_selector("g.period-item")
for quarter in periods:
    shots = quarter.find_element_by_css_selector("g.shot-list").find_elements_by_css_selector("g.shot-item")
    for item in shots:
        print(f"x: {item.find_element_by_tag_name('svg').get_attribute('x')} - y: {item.find_element_by_tag_name('svg').get_attribute('y')}")
        point = item.find_element_by_tag_name('svg')
        point.click()
    print("\n")

我遇到了这个错误:

selenium.common.exceptions.WebDriverException: Message: unknown error: Element <svg class="shot-miss icon icon-miss clickable" x="40.5" y="99.5" width="16" height="16" viewBox="0 0 30 30" title="">...</svg> is not clickable at point (44, 165). Other element would receive the click: <p class="cc_message">...</p>
  (Session info: chrome=72.0.3626.121)
  (Driver info: chromedriver=2.44.609551 (5d576e9a44fe4c5b6a07e568f1ebc753f1214634),platform=Linux 5.4.0-88-generic x86_64)

这怎么可能?我究竟做错了什么?如何获取模态 window?

的内容

编辑一(@Prophet提供的解决方案):

现在,我的代码是:

from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

b = self.driver
wait = WebDriverWait(b, 20)
b.set_window_size(1920, 1080)
b.get("https://www.fiba.basketball/euroleaguewomen/21-22/game/1310/MBA-Moscow-ZVVZ-USK-Praha#tab=shot_chart")
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "a.cc_btn_accept_all"))).click()
b.execute_script("window.scrollTo(0, document.body.scrollHeight);")
periods = b.find_element_by_css_selector('g.team-A').find_element_by_css_selector('g.period-list').find_elements_by_css_selector("g.period-item")
for quarter in periods:
    shots = quarter.find_element_by_css_selector("g.shot-list").find_elements_by_css_selector("g.shot-item")
    for item in shots:
        print(f"x: {item.find_element_by_tag_name('svg').get_attribute('x')} - y: {item.find_element_by_tag_name('svg').get_attribute('y')}")
        point = item.find_element_by_tag_name('svg')
        point.click()
    print("\n")

我遇到了这个错误:

selenium.common.exceptions.WebDriverException: Message: unknown error: Element <svg class="shot-miss icon icon-miss clickable" x="151.5" y="211.5" width="16" height="16" viewBox="0 0 30 30" title="">...</svg> is not clickable at point (805, 312). Other element would receive the click: <th class="player"></th>
  (Session info: chrome=72.0.3626.121)
  (Driver info: chromedriver=2.44.609551 (5d576e9a44fe4c5b6a07e568f1ebc753f1214634),platform=Linux 5.4.0-88-generic x86_64)

编辑二(@cruisepandey提供的解决方案:

我已经编辑了我的代码,现在我得到了这个代码:

from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC


b = self.driver
    b.set_window_size(1920, 1080)
    b.get("https://www.fiba.basketball/euroleaguewomen/21-22/game/1310/MBA-Moscow-ZVVZ-USK-Praha#tab=shot_chart")
            b.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    periods = b.find_element_by_css_selector('g.team-A').find_element_by_css_selector('g.period-list').find_elements_by_css_selector("g.period-item")
    WebDriverWait(b, 20).until(EC.element_to_be_clickable((By.XPATH, "//*[name()='svg']"))).click()
    for quarter in periods:
        shots = quarter.find_element_by_css_selector("g.shot-list").find_elements_by_css_selector("g.shot-item")
        for item in shots:
            print(f"x: {item.find_element_by_tag_name('svg').get_attribute('x')} - y: {item.find_element_by_tag_name('svg').get_attribute('y')}")
            point = item.find_element_by_xpath("//*[name()='svg']")
            point.click()
        print("\n")

但是,它不起作用 :( 我遇到了这个错误:

Traceback (most recent call last):
  File "/home/josecarlos/Workspace/python/basketmetrics/test/test_shot_chart_get_data.py", line 27, in test_something
    WebDriverWait(b, 20).until(EC.element_to_be_clickable((By.XPATH, "//*[name()='svg']"))).click()
  File "/home/josecarlos/Workspace/python/basketmetrics/venv/python/lib/python3.8/site-packages/selenium/webdriver/support/wait.py", line 80, in until
    raise TimeoutException(message, screen, stacktrace)
selenium.common.exceptions.TimeoutException: Message: 

编辑三:

我试图通过他的绝对 XPath 路由访问“svg”标签。使用 Firefox 或 Chrome 的开发者工具,我们可以获得这个绝对的 Xpath。在我的例子中,我得到了蓝色元素的 XPath:

这是路线:

/html/body/div[3]/div[3]/div/section/div[2]/div/div/ul[2]/li[6]/div/div[1]/div/div[2]/div[2]/div[2]/svg/g[1]/g/g[1]/g/g[1]/svg

如果我尝试在我的代码中访问此路由,我会遇到错误

b = self.driver
wait = WebDriverWait(b, 20)
b.set_window_size(300, 300)
b.get("https://www.fiba.basketball/euroleaguewomen/21-22/game/1310/MBA-Moscow-ZVVZ-USK-Praha#tab=shot_chart")
        periods = b.find_element_by_css_selector('g.team-A').find_element_by_css_selector('g.period-list').find_elements_by_css_selector("g.period-item")
#WebDriverWait(b, 20).until(EC.element_to_be_clickable((By.XPATH, "//*[name()='svg']"))).click()
for quarter in periods:
    shots = quarter.find_element_by_css_selector("g.shot-list").find_elements_by_css_selector("g.shot-item")
    for item in shots:
        print(f"x: {item.find_element_by_tag_name('svg').get_attribute('x')} - y: {item.find_element_by_tag_name('svg').get_attribute('y')}")
        point = b.find_element_by_xpath("/html/body/div[3]/div[3]/div/section/div[2]/div/div/ul[2]/li[6]/div/div[1]/div/div[2]/div[2]/div[2]/svg/g[1]/g/g[1]/g/g[1]/svg")
        point.click()
    print("\n")

我得到的错误是:

selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"xpath","selector":"/html/body/div[3]/div[3]/div/section/div[2]/div/div/ul[2]/li[6]/div/div[1]/div/div[2]/div[2]/div[2]/svg/g[1]/g/g[1]/g/g[1]/svg"}
  (Session info: chrome=72.0.3626.121)
  (Driver info: chromedriver=2.44.609551 (5d576e9a44fe4c5b6a07e568f1ebc753f1214634),platform=Linux 5.4.0-88-generic x86_64)

我做错了什么?发生了什么事?

编辑四:

我已经在我的代码中修改了允许我访问 svg 的字符串,但它不起作用。

b = self.driver
wait = WebDriverWait(b, 20)
b.set_window_size(1920, 1080)
b.get("https://www.fiba.basketball/euroleaguewomen/21-22/game/1310/MBA-Moscow-ZVVZ-USK-Praha#tab=shot_chart")
        periods = b.find_element_by_css_selector('g.team-A').find_element_by_css_selector('g.period-list').find_elements_by_css_selector("g.period-item")
#WebDriverWait(b, 20).until(EC.element_to_be_clickable((By.XPATH, "//*[name()='svg']"))).click()
for quarter in periods:
    shots = quarter.find_element_by_css_selector("g.shot-list").find_elements_by_css_selector("g.shot-item")
    for item in shots:
        print(f"x: {item.find_element_by_tag_name('svg').get_attribute('x')} - y: {item.find_element_by_tag_name('svg').get_attribute('y')}")
        point = b.find_element_by_xpath("//div[@class='shot-chart_canvas']/*[name()='svg']/*[name()='g'][1]/*[name()='g']/*[name()='g'][1]/*[name()='g']/*[name()='g'][1]/*[name()='svg']")
        point.click()
    print("\n")
self.assertEqual(True, True, "Error!!!")

我得到的错误是:

selenium.common.exceptions.WebDriverException: Message: unknown error: Element <svg class="shot-miss icon icon-miss clickable" x="40.5" y="99.5" width="16" height="16" viewBox="0 0 30 30" title="">...</svg> is not clickable at point (687, 917). Other element would receive the click: <div class="cc_banner cc_container cc_container--open">...</div>
  (Session info: chrome=72.0.3626.121)
  (Driver info: chromedriver=2.44.609551 (5d576e9a44fe4c5b6a07e568f1ebc753f1214634),platform=Linux 5.4.0-88-generic x86_64)

抱歉,我不明白发生了什么:(((

编辑 V:

我必须检索所有十字和点的信息。我有两个团队,在这个标签中,我们有一系列周期,在每个周期中,我们有所有拍摄和我需要检索信息的每个拍摄项目。那么,如何制作 Xpath 循环来检索每次拍摄的信息?

您尝试点击的元素最初不在可见屏幕内。底部还有一个“接受 cookie”的横幅。
您需要关闭 cookie 横幅并向上滚动页面才能使您的代码正常工作。
请试试这个:

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
b = self.driver
wait = WebDriverWait(b, 20)
b.set_window_size(300, 300)
b.get("https://www.fiba.basketball/euroleaguewomen/21-22/game/1310/MBA-Moscow-ZVVZ-USK-Praha#tab=shot_chart")
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "a.cc_btn_accept_all"))).click()

b.execute_script("window.scrollTo(0, document.body.scrollHeight);")

periods = b.find_element_by_css_selector('g.team-A').find_element_by_css_selector('g.period-list').find_elements_by_css_selector("g.period-item")
for quarter in periods:
    shots = quarter.find_element_by_css_selector("g.shot-list").find_elements_by_css_selector("g.shot-item")
    for item in shots:
        print(f"x: {item.find_element_by_tag_name('svg').get_attribute('x')} - y: {item.find_element_by_tag_name('svg').get_attribute('y')}")
        point = item.find_element_by_tag_name('svg')
        point.click()
    print("\n")

我还建议使用正常的 window 尺寸,如下所示:

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
b = self.driver
wait = WebDriverWait(b, 20)
b.set_window_size(1920, 1080)
b.get("https://www.fiba.basketball/euroleaguewomen/21-22/game/1310/MBA-Moscow-ZVVZ-USK-Praha#tab=shot_chart")
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "a.cc_btn_accept_all"))).click()

b.execute_script("window.scrollTo(0, document.body.scrollHeight);")

periods = b.find_element_by_css_selector('g.team-A').find_element_by_css_selector('g.period-list').find_elements_by_css_selector("g.period-item")
for quarter in periods:
    shots = quarter.find_element_by_css_selector("g.shot-list").find_elements_by_css_selector("g.shot-item")
    for item in shots:
        print(f"x: {item.find_element_by_tag_name('svg').get_attribute('x')} - y: {item.find_element_by_tag_name('svg').get_attribute('y')}")
        point = item.find_element_by_tag_name('svg')
        point.click()
    print("\n")

UPD
svg、rect、g等都是特殊的标签名。
/svg/g Xpath 将不起作用。您将需要分别使用 /*[name()='svg']/*[name()='g']
此外,强烈不推荐使用绝对路径。您需要使用更好的定位器。
例如代替

/html/body/div[3]/div[3]/div/section/div[2]/div/div/ul[2]/li[6]/div/div[1]/div/div[2]/div[2]/div[2]/svg/g[1]/g/g[1]/g/g[1]/svg

这会更好:

//div[@class='shot-chart_canvas']/*[name()='svg']/*[name()='g'][1]/*[name()='g']/*[name()='g'][1]/*[name()='g']/*[name()='g'][1]/*[name()='svg']

所以不用

point = b.find_element_by_xpath("/html/body/div[3]/div[3]/div/section/div[2]/div/div/ul[2]/li[6]/div/div[1]/div/div[2]/div[2]/div[2]/svg/g[1]/g/g[1]/g/g[1]/svg")
point.click()

试试这个:

point = b.find_element_by_xpath("//div[@class='shot-chart_canvas']/*[name()='svg']/*[name()='g'][1]/*[name()='g']/*[name()='g'][1]/*[name()='g']/*[name()='g'][1]/*[name()='svg']")
point.click()

g tagsvg tag 之下。所以为了定位 g.team-A

请使用以下 xpath :

//*[name()='g' and @class='team-A']

这是一个 xpath 表达式。

所以可能的解决方法:

point = item.find_element_by_tag_name('svg')
point.click()

使用这个:

point = item.find_element_by_xpath("//*[name()='svg']")
point.click()

我在这里建议的是定义一个明确的等待,然后你可以尝试点击它。

代码试用:

WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//*[name()='svg']"))).click()

对于这种明确的等待,您还必须导入这些:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

更新:

driver = webdriver.Chrome(driver_path)
driver.maximize_window()
wait = WebDriverWait(driver, 30)

driver.get("https://www.fiba.basketball/euroleaguewomen/21-22/game/1310/MBA-Moscow-ZVVZ-USK-Praha#tab=shot_chart")
ele = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "div.shot-chart_canvas")))
driver.execute_script("arguments[0].scrollIntoView(true);", ele)
wait.until(EC.element_to_be_clickable((By.XPATH, "//*[name()='svg' and @class='chart']//*[name()='g']//descendant::*[name()='g' and @class='shot-item']"))).click()
a = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "div.player-profile"))).get_attribute('innerText')
print(a)

进口:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

输出:

Q1 09:520 0
Alexandra Stolyar
2pt jump shot missed
View Player Profile
    FG  2Pts    3Pts    FT  Pts
In this game    
2/6
33.33%
    
1/4
25%
    
1/2
50%
    
3/3
100%
    8

更新 2:

driver.get("https://www.fiba.basketball/euroleaguewomen/21-22/game/1310/MBA-Moscow-ZVVZ-USK-Praha#tab=shot_chart")
ele = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "div.shot-chart_canvas")))
driver.execute_script("arguments[0].scrollIntoView(true);", ele)
all_points  = wait.until(EC.presence_of_all_elements_located((By.XPATH, "//*[name()='svg' and @class='chart']//*[name()='g']//descendant::*[name()='g' and @class='shot-item']")))
print(len(all_points))
for point in all_points:
    point.click()
    a = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "div.player-profile"))).get_attribute('innerText')
    print(a)
    wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button.icon--close"))).click()
    time.sleep(1)

进口:

119
Q1 09:520 0
Alexandra Stolyar
2pt jump shot missed
View Player Profile
    FG  2Pts    3Pts    FT  Pts
In this game    
2/6
33.33%
    
1/4
25%
    
1/2
50%
    
3/3
100%
    8
Q1 09:350 0
Karina Nizamova
3pt jump shot missed
View Player Profile
    FG  2Pts    3Pts    FT  Pts
In this game    
1/7
14.29%
    
1/3
33.33%
    
0/4
0%
    
2/3
66.67%
    4