如何使用 Selenium 自动化国际象棋棋盘?

How can I automate a Chess board with Selenium?

我找到了一个国际象棋网站 (https://www.chess.com/play/computer),在 Selenium 上使用它非常令人困惑。我遇到的问题是,大多数正方形实际上并未被视为一个元素,只有当它上面有一块或它被突出显示时,它才会被视为一个元素。这使得在 Selenium 中工作变得非常非常困难,因为我无法在不单击它的情况下突出显示一个正方形,而要使用 Selenium 单击它我需要该元素。

也许我可以使用此处建议的坐标系: 但他们的代码无法正常工作,因为 post 已有 5 年历史了。

棋盘本身是一个视图框,作为元素的每个方块在其 class 中都有“square-”后跟一个两位数,其中第一个数字是棋盘上的数字,第二个数字是转换为数字的板上的字母。在图片中,我提供的确切 class 是“highlight square-45

感谢您的帮助!

您可以通过 driver.execute_script 使用 Javascript 片段来获取棋盘上每个方块的坐标:

from selenium import webdriver
d = webdriver.Chrome('/path/to/chromedriver')
d.get('https://www.chess.com/play/computer')
board = d.execute_script('''
  function coords(elem){
      var n = elem.getBoundingClientRect()
      return {top:n.top, left:n.left, width:n.width, height:n.height}
  }
  var pieces = []
  for (var i = 1; i < 9; i++){
     if (i > 6 || i < 3){
        pieces.push(Array.from((new Array(8)).keys()).map(function(x){
           var square = document.querySelector(`.piece.square-${x+1}${i}`)
           return {...coords(square), piece:square.getAttribute('class').split(' ')[1]}
        }));
     }
     else{
        pieces.push(Array.from((new Array(8)).keys()).map(function(x){
           var arr = pieces[pieces.length-1]
           return {left:arr[x].left, top:arr[x].top - arr[x].height, 
             width:arr[x].width, height:arr[x].height, piece:null}
        }));
     }
  }
  return pieces
''')[::-1]

输出:

[[{'height': 62, 'left': 187, 'piece': 'br', 'top': 66, 'width': 62}, {'height': 62, 'left': 249, 'piece': 'bn', 'top': 66, 'width': 62}, {'height': 62, 'left': 311, 'piece': 'bb', 'top': 66, 'width': 62}, {'height': 62, 'left': 373, 'piece': 'bq', 'top': 66, 'width': 62}, {'height': 62, 'left': 435, 'piece': 'bk', 'top': 66, 'width': 62}, {'height': 62, 'left': 497, 'piece': 'bb', 'top': 66, 'width': 62}, {'height': 62, 'left': 559, 'piece': 'bn', 'top': 66, 'width': 62}, {'height': 62, 'left': 621, 'piece': 'br', 'top': 66, 'width': 62}], [{'height': 62, 'left': 187, 'piece': 'bp', 'top': 128, 'width': 62}, {'height': 62, 'left': 249, 'piece': 'bp', 'top': 128, 'width': 62}, {'height': 62, 'left': 311, 'piece': 'bp', 'top': 128, 'width': 62}, {'height': 62, 'left': 373, 'piece': 'bp', 'top': 128, 'width': 62}, {'height': 62, 'left': 435, 'piece': 'bp', 'top': 128, 'width': 62}, {'height': 62, 'left': 497, 'piece': 'bp', 'top': 128, 'width': 62}, {'height': 62, 'left': 559, 'piece': 'bp', 'top': 128, 'width': 62}, {'height': 62, 'left': 621, 'piece': 'bp', 'top': 128, 'width': 62}], [{'height': 62, 'left': 187, 'piece': None, 'top': 190, 'width': 62}, {'height': 62, 'left': 249, 'piece': None, 'top': 190, 'width': 62}, {'height': 62, 'left': 311, 'piece': None, 'top': 190, 'width': 62}, {'height': 62, 'left': 373, 'piece': None, 'top': 190, 'width': 62}, {'height': 62, 'left': 435, 'piece': None, 'top': 190, 'width': 62}, {'height': 62, 'left': 497, 'piece': None, 'top': 190, 'width': 62}, {'height': 62, 'left': 559, 'piece': None, 'top': 190, 'width': 62}, {'height': 62, 'left': 621, 'piece': None, 'top': 190, 'width': 62}], [{'height': 62, 'left': 187, 'piece': None, 'top': 252, 'width': 62}, {'height': 62, 'left': 249, 'piece': None, 'top': 252, 'width': 62}, {'height': 62, 'left': 311, 'piece': None, 'top': 252, 'width': 62}, {'height': 62, 'left': 373, 'piece': None, 'top': 252, 'width': 62}, {'height': 62, 'left': 435, 'piece': None, 'top': 252, 'width': 62}, {'height': 62, 'left': 497, 'piece': None, 'top': 252, 'width': 62}, {'height': 62, 'left': 559, 'piece': None, 'top': 252, 'width': 62}, {'height': 62, 'left': 621, 'piece': None, 'top': 252, 'width': 62}], [{'height': 62, 'left': 187, 'piece': None, 'top': 314, 'width': 62}, {'height': 62, 'left': 249, 'piece': None, 'top': 314, 'width': 62}, {'height': 62, 'left': 311, 'piece': None, 'top': 314, 'width': 62}, {'height': 62, 'left': 373, 'piece': None, 'top': 314, 'width': 62}, {'height': 62, 'left': 435, 'piece': None, 'top': 314, 'width': 62}, {'height': 62, 'left': 497, 'piece': None, 'top': 314, 'width': 62}, {'height': 62, 'left': 559, 'piece': None, 'top': 314, 'width': 62}, {'height': 62, 'left': 621, 'piece': None, 'top': 314, 'width': 62}], [{'height': 62, 'left': 187, 'piece': None, 'top': 376, 'width': 62}, {'height': 62, 'left': 249, 'piece': None, 'top': 376, 'width': 62}, {'height': 62, 'left': 311, 'piece': None, 'top': 376, 'width': 62}, {'height': 62, 'left': 373, 'piece': None, 'top': 376, 'width': 62}, {'height': 62, 'left': 435, 'piece': None, 'top': 376, 'width': 62}, {'height': 62, 'left': 497, 'piece': None, 'top': 376, 'width': 62}, {'height': 62, 'left': 559, 'piece': None, 'top': 376, 'width': 62}, {'height': 62, 'left': 621, 'piece': None, 'top': 376, 'width': 62}], [{'height': 62, 'left': 187, 'piece': 'wp', 'top': 438, 'width': 62}, {'height': 62, 'left': 249, 'piece': 'wp', 'top': 438, 'width': 62}, {'height': 62, 'left': 311, 'piece': 'wp', 'top': 438, 'width': 62}, {'height': 62, 'left': 373, 'piece': 'wp', 'top': 438, 'width': 62}, {'height': 62, 'left': 435, 'piece': 'wp', 'top': 438, 'width': 62}, {'height': 62, 'left': 497, 'piece': 'wp', 'top': 438, 'width': 62}, {'height': 62, 'left': 559, 'piece': 'wp', 'top': 438, 'width': 62}, {'height': 62, 'left': 621, 'piece': 'wp', 'top': 438, 'width': 62}], [{'height': 62, 'left': 187, 'piece': 'wr', 'top': 500, 'width': 62}, {'height': 62, 'left': 249, 'piece': 'wn', 'top': 500, 'width': 62}, {'height': 62, 'left': 311, 'piece': 'wb', 'top': 500, 'width': 62}, {'height': 62, 'left': 373, 'piece': 'wq', 'top': 500, 'width': 62}, {'height': 62, 'left': 435, 'piece': 'wk', 'top': 500, 'width': 62}, {'height': 62, 'left': 497, 'piece': 'wb', 'top': 500, 'width': 62}, {'height': 62, 'left': 559, 'piece': 'wn', 'top': 500, 'width': 62}, {'height': 62, 'left': 621, 'piece': 'wr', 'top': 500, 'width': 62}]]

编辑:点击一个方块:

from selenium.webdriver.common.action_chains import ActionChains
def click_square(square):
   elem = d.execute_script('''return document.querySelector('body')''')
   ac = ActionChains(d)
   ac.move_to_element(elem).move_by_offset(square['left']+int(square['width']/2), square['top']+int(square['width']/2)).click().perform()

click_square(board[0][0])