【问题标题】:How to "send keys" to a canvas element for longer duration?如何将密钥“发送”到画布元素更长的时间?
【发布时间】:2020-10-11 12:50:25
【问题描述】:

我的目标:

我正在尝试制作一个 python 机器人来赢得chrome's dino game

游戏允许两种类型的跳跃:

  • 短距离跳跃
  • 跳远

使用main_body.send_keys(Keys.SPACE)(如下代码所示)可以进行短跳转。

我的问题:

我在跳远时遇到困难。

我的方法:

目前,对于跳远,我正在使用Keyboard 库:

keyboard.press(keyboard.KEY_UP)

不幸的是,这需要浏览器窗口始终处于焦点位置。后来,我想无头运行这个程序,所以这种方法行不通。

或者:

我试过ActionChains

ActionChains(driver) \
.key_down(Keys.SPACE) \
.pause(0.2) \
.key_up(Keys.SPACE) \
.perform()

但这最终会滚动整个页面。并且没有达到预期的目的。

我只是希望直接将这些动作“发送”到画布元素,而不是在整个页面上执行它们......

我想做这样的事情

main_body.key_down(Keys.SPACE) 
time.sleep(0.2)
main_body.key_up(Keys.SPACE) 

虽然这当然会给我这个错误:AttributeError: 'FirefoxWebElement' object has no attribute 'key_down' 因为canvas 没有属性key_downkey_up

这是一个 MCVE:

注意在代码中,恐龙会不断跳跃,但这不是重点,这只是为了检查跳跃的高度,而不是赢得比赛。

import keyboard
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains

driver = webdriver.Firefox()
driver.get('https://chromedino.com/')
canvas = driver.find_element_by_css_selector('.runner-canvas')
main_body = driver.find_element_by_xpath("//html")

try:
    canvas.click()
except:
    main_body.send_keys(Keys.SPACE)    


while True:
    #short jump
    main_body.send_keys(Keys.SPACE)

    #long jump
    ActionChains(driver) \
    .key_down(Keys.SPACE) \
    .pause(0.2) \
    .key_up(Keys.SPACE) \
    .perform()

    #long jump using keyboard:
    keyboard.press(keyboard.KEY_UP)

请为其他人注释掉代码,看看每种跳转的效果。

如果可能的话,建议一些其他的替代方法,在不使用 Keyboard 且不滚动整个页面的情况下进行跳远......

【问题讨论】:

  • 使用ActionChains 不会滚动整个页面。在我的电脑上运行良好。(我正在使用Chrome
  • @jizhihaoSAMA 哦。我正在使用火狐。让我在 chrome 中试一试...

标签: python selenium selenium-webdriver canvas keyboard-python


【解决方案1】:

这是一个简单的解决方案-

import keyboard

keyboard.press_and_release('space', 'space', 'space')

当我尝试时它起作用了,所以应该起作用。您可以增加和减少写入'space' 的次数来增加或减少持续时间。

希望它有所帮助:)

【讨论】:

  • 我不能使用keyboard。我想无头运行这个程序。所以不会打开浏览器窗口...
  • 我认为你仍然可以使用。你也能解释一下你所说的无头是什么意思
  • 当您通常打开 selenium 时,它会在您的设备上打开一个浏览器(firefox、chrome 等)。在无头模式下,您在后台打开浏览器。您不能像往常一样直接与它交互。没有浏览器实际上会弹出。
【解决方案2】:

你离得很近。不过几句话:

  • short jumps 的代码块中,您无需向任何元素发送 Keys.SPACE
  • long jumps 的代码块中,您也不需要将 Keys.SPACE 发送到任何元素,并且变量 keyboard 也未定义。

解决方案

作为一种解决方案,一旦打开 URL,您需要为 canvas 元素的 visibility_of_element_located() 引入 WebDriverWait,您可以使用以下基于 Locator Strategy 的解决方案之一。


短跳

对于短跳,您可以使用:

driver.get("https://chromedino.com/")
WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "canvas.runner-canvas")))
while True:
    #short jump
    ActionChains(driver).key_down(Keys.SPACE).key_up(Keys.SPACE).perform()

我始终能够得分 69-72

快照:


0.2 秒停顿的长跳

对于pause(0.2) 的跳远,您可以使用:

driver.get("https://chromedino.com/")
WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "canvas.runner-canvas")))
while True:
    #short jump
    # ActionChains(driver).key_down(Keys.SPACE).key_up(Keys.SPACE).perform()

    #long jump
    ActionChains(driver).key_down(Keys.SPACE).pause(0.2).key_up(Keys.SPACE).perform()

我一直能够得分 65

快照:


0.5 秒停顿的长跳

对于pause(0.5) 的跳远,您可以使用:

driver.get("https://chromedino.com/")
WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "canvas.runner-canvas")))
while True:
    #short jump
    # ActionChains(driver).key_down(Keys.SPACE).key_up(Keys.SPACE).perform()

    #long jump
    ActionChains(driver).key_down(Keys.SPACE).pause(0.5).key_up(Keys.SPACE).perform()

我一直能够得分 57-60

快照:

【讨论】:

    【解决方案3】:

    不幸的是,我在该游戏中看不到可重现的跳跃行为。当我按 UP o SPACE 时,我随机看到短跳或长跳,所以我不能确定我的方法是否适合你。

    但是,我认为,只需稍加努力,您就可以创建一个适合您需求的合适活动。 基本上,由于 Selenium 可以执行任意 javascript,我这里的方法是向 canvas 元素发送一个 keydown 事件(用 Firefox 77 测试)。

    为每次迭代制作一个屏幕截图,以确保恐龙实际跳跃。

    玩得开心。

    from selenium.webdriver.firefox.options import Options as FirefoxOptions
    from selenium import webdriver
    from selenium.webdriver.common.keys import Keys
    import time
    
    options = FirefoxOptions()
    options.add_argument("--headless")
    driver = webdriver.Firefox(options=options)
    driver.get('https://chromedino.com/')
    
    canvas = driver.find_element_by_css_selector('.runner-canvas')
    main_body = driver.find_element_by_xpath("//html")
    
    try:
        canvas.click()
    except:
        main_body.send_keys(Keys.SPACE)
    
    while True:
        driver.execute_script('''
        var keydownEvt = new KeyboardEvent('keydown', {
            altKey:false,
            altKey: false,
            bubbles: true,
            cancelBubble: false,
            cancelable: true,
            charCode: 0,
            code: "Space",
            composed: true,
            ctrlKey: false,
            currentTarget: null,
            defaultPrevented: true,
            detail: 0,
            eventPhase: 0,
            isComposing: false,
            isTrusted: true,
            key: " ",
            keyCode: 32,
            location: 0,
            metaKey: false,
            repeat: false,
            returnValue: false,
            shiftKey: false,
            type: "keydown",
            which: 32,
        });
        arguments[0].dispatchEvent(keydownEvt);
        ''', canvas)
        driver.get_screenshot_as_file('proof_%s.png' % int(time.time()))
        time.sleep(0.2)
    
    driver.quit()
    

    【讨论】:

    • 您对randomly see short or long jumps 的评估是正确的。我在尝试@jizhihaoSAMA 的suggestion 时也注意到了这一点...您的代码也可以正常工作。您能否解释一下为什么将new KeyboardEvent 中的参数设置为...?另外,您似乎重复了两次altKey:false
    • 为了获得一个有效的事件,我只是在 Firefox 控制台中的一个随机元素中添加了一个 keydown 事件,然后附加了一个转储该事件的函数。 “altKey”的重复肯定是复制粘贴垃圾。也许您不需要该事件的所有属性,您可以自己尝试并检查您想要使用的目标浏览器。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-15
    • 2016-04-23
    相关资源
    最近更新 更多