如何在 Python 中仅使用 Selenium 长按(按住)鼠标左键

How to long press (Press and Hold) mouse left key using only Selenium in Python

我正在尝试使用 Python 中的 Selenium 从沃尔玛网站上抓取一些评论数据,但它连接此 site 以供人工验证。检查此“按住”按钮后,不知何故,当我找到该元素时,它以 [object HTMLIFrameElement] 的形式出现,而不是网络元素。并且该元素随机出现在任何 iframe 中,在 10 个 iframe 中。它可以使用循环来检查,但是,最终我们不能在没有 web 元素的情况下在 selenium 中采取任何行动。

虽然此验证也作为弹出窗口出现,但我试图先针对此页面解决它。我以某种方式使用 div 作为网络元素找到了此按钮的位置。

actions = ActionChains(driver)
iframe = driver.find_element_by_xpath("//div[@id='px-captcha']")
frame_x = iframe.location['x']
frame_y = iframe.location['y']
actions.move_to_element(iframe).move_by_offset(frame_x-550, frame_y+70).build().perform()

如果我执行 context.click() 或右键单击,可以看到鼠标位置在按钮的中间。

现在,如果我可以执行长按按住鼠标左键一会儿,我想这个验证可以被清除。为此,我尝试在 action 中使用 click()click_and_hold 以及 key_down 方法(按 ctrl 和 enter 与长按相同)进行操作,但是没有响应,因为这些方法释放按钮,不能长按。我试过了

actions.move_to_element(iframe).move_by_offset(frame_x-550,frame_y+70).click_and_hold().pause(20).perform()
actions.move_to_element(iframe).move_by_offset(frame_x-550, frame_y+70).actions.key_down(Keys.CONTROL).actions.key_down(Keys.ENTER).pause(20).perform()

.....还有很多方法! 我如何使用 Selenium 解决它

这是我的临时解决方案。关键是10秒后松开再点击。这就是我如何能够欺骗验证码,让它认为我将其保持 只是 正确的时间(在我的实验中,验证码保持时间是随机的,10 秒确保足够是时候完全完成验证码了)。

element = driver.find_element_by_css_selector('#px-captcha')
action = ActionChains(driver)
click = ActionChains(driver)
action.click_and_hold(element)
action.perform()
time.sleep(10)
action.release(element)
action.perform()
time.sleep(0.2)
action.release(element)

这是处理长按验证码案例的完整代码。我已经添加了代码来自动调整验证码框的大小,以便在需要验证的验证码中间单击并按住。

import os
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.action_chains import ActionChains

import time
from time import sleep
from random import randint

chromedriver = "C:\Program Files\Python39\Scripts\chromedriver"
os.environ["webdriver.chrome.driver"] = chromedriver
driver = webdriver.Chrome(chromedriver)
chromedriver = "C:\Program Files\Python39\Scripts\chromedriver"
os.environ["webdriver.chrome.driver"] = chromedriver
driver = webdriver.Chrome(chromedriver)

url = "{Your URL}"
driver.get(url)
sleep(randint(2,3))

element = driver.find_element_by_xpath("//div[@id='px-captcha']")
# print(len(element.text), '- Value found by method text')
action = ActionChains(driver)
click = ActionChains(driver)
frame_x = element.location['x']
frame_y = element.location['y']
# print('x: ', frame_x)
# print('y: ', frame_y)
# print('size box: ', element.size)
# print('x max click: ', frame_x + element.size['width'])
# print('y max click: ', frame_y + element.size['height'])
x_move = frame_x + element.size['width']*0.5
y_move = frame_y + element.size['height']*0.5
action.move_to_element_with_offset(element, x_move, y_move).click_and_hold().perform()
time.sleep(10)
action.release(element)
action.perform()
time.sleep(0.2)
action.release(element)

@Prata Palit
'Press & Hold' 按钮使用 10 个 iframe, 随机一个 iframe 可见,其他 9 个 ifame 隐藏, iframe 具有 cross-domain,无法获取具有 javascript 的元素。 'Press and hold' 按钮完成验证速度也是随机的。 我使用了 FLANN 的特征匹配。

  1. 获取经过验证的验证码图片https://i.stack.imgur.com/ADzMT.png
    使用 selenium 屏幕截图获取验证码元素图像。
    不要使用 OS 屏幕截图。 因为你需要比较相同的截图。
  2. 检查页面是否包含验证码
    按住
    每隔0.5秒循环执行以下操作
    使用pre-prepared验证码与页面上的验证码进行比较
    直到匹配或超时

匹配验证码https://i.stack.imgur.com/xCqhy.jpg

def solve_blocked(self, retry=3):
    '''
    Solve blocked
    (Cross-domain iframe cannot get elements temporarily)
    Simulate the mouse press and hold to complete the verification
    '''
    if not retry:
        return False
    element = None
    try:
        element = WebDriverWait(self.browser,15).until(EC.presence_of_element_located((By.ID,'px-captcha')))
        # Wait for the px-captcha element styles to fully load
        time.sleep(0.5)
    except BaseException as e:
        self.logger.info(f'px-captcha element not found')
        return
    self.logger.info(f'solve blocked:{self.browser.current_url}, Retry {retry} remaining times')
    template = cv2.imread(os.path.join(settings.TPL_DIR, 'captcha.png'), 0)
    # Set the minimum number of feature points to match value 10
    MIN_MATCH_COUNT = 8 
    if  element:
        self.logger.info(f'start press and hold')
        ActionChains(self.browser).click_and_hold(element).perform()
        start_time = time.time()
        while 1:
            # timeout
            if time.time() - start_time > 20:
                break
            x, y = element.location['x'], element.location['y']
            width, height = element.size.get('width'), element.size.get('height')                
            left = x*self.pixelRatio
            top = y*self.pixelRatio
            right = (x+width)*self.pixelRatio
            bottom = (y+height)*self.pixelRatio
            # full screenshot
            png = self.browser.get_screenshot_as_png() 
            im = Image.open(BytesIO(png))
            # px-captcha screenshot
            im = im.crop((left, top, right, bottom)) 
            target = cv2.cvtColor(np.asarray(im),cv2.COLOR_RGB2BGR)  
            # Initiate SIFT detector
            sift = cv2.SIFT_create()
            # find the keypoints and descriptors with SIFT
            kp1, des1 = sift.detectAndCompute(template,None)
            kp2, des2 = sift.detectAndCompute(target,None)
            # create set FLANN match
            FLANN_INDEX_KDTREE = 0
            index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
            search_params = dict(checks = 50)
            flann = cv2.FlannBasedMatcher(index_params, search_params)
            matches = flann.knnMatch(des1,des2,k=2)
            # store all the good matches as per Lowe's ratio test.
            good = []
            # Discard matches greater than 0.7
            for m,n in matches:
                if m.distance < 0.7*n.distance:
                    good.append(m)
            self.logger.info( "matches are found - %d/%d" % (len(good),MIN_MATCH_COUNT))
            if len(good)>=MIN_MATCH_COUNT:
                self.logger.info(f'release button')
                ActionChains(self.browser).release(element).perform()
                return
            time.sleep(0.5)
    time.sleep(1)
    retry -= 1
    self.solve_blocked(retry)