selenium自动化调用cmd打开页面后鼠标监听事件重复

遇到一个棘手的问题,如下:
在用selenium进行自动化操作时,使用pynput库进行监听鼠标的操作,监听鼠标点击事件,并同时判断点击的标签是否是想要点击的标签,根据标签内容选择调用不同浏览器打开该网址,打开网址的操作是使用os.system()打开的,这个时候再进行点击操作就会重复点击刚才点击的标签,不论点击哪里都会无限重复打开,下面是我的代码:
python代码如下:

import datetime
import os
import pytz
import subprocess
import pathlib

from selenium import webdriver
from pynput import *
from apscheduler.schedulers.background import BackgroundScheduler

folder = pathlib.Path(__file__).parent.resolve()
try:
  os.system("taskkill /f /t /im chromedriver.exe")
except:
  pass
options = webdriver.ChromeOptions()
options.add_experimental_option('detach', True)
options.add_argument('--disable-gpu')
driver = webdriver.Chrome(Options=options)
driver.get('file:///' + f'{folder}/abc.html')
driver.maximize_window()
js='''
  window.hovered_element = null
  function track_mouse(event){
    var x = event.clientX
    var y = event.clientY
    var element = document.elementFromPoint(x, y)
    if(!element){
      window.hovered_element = null
      return //当前页面没有元素
    }
    window.hovered_element = element
  }
  window.onmousemove = track_mouse
'''
driver.execute_script(js)

def cl():
  with mouse.Listener(on_click=on_click) as Listener:
    Listener.join()

def on_click(x, y, button, pressed):
  element_js = driver.execute_script('return window.hovered_element')
  if pressed:
    tag_name = element_js.tag_name
    if tag_name == 'a':
      _href = element_js.get_attribute("_href")
      _type = element_js.get_attribute("_type")
      if _type == 'Chrome':
        subprocess.Popen('"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" '+ _href)
      elif _type == 'IE':
        subprocess.Popen('"C:\\Program Files\\Internet Explorer\\iexplore.exe" '+ _href)
      elif _type == 'FireFox':
        subprocess.Popen('"C:\\Program Files\\Mozilla Firefox\\firefox.exe" '+ _href)

scheduler = BackgroundScheduler(timezone=pytz.timezone("Asia/Shanghai"))
scheduler.add_job(cl, 'date', next_run_time=datetime.datetime.now())
scheduler.start()

abc.html代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
  <div>
    <a _href="https://www.baidu.com" _type="Chrome">百度(使用Chrome打开)</a>
  </div>
  <div>
    <a _href="https://www.baidu.com" _type="IE">百度(使用IE打开)</a>
  </div>
</body>
</html>

目前使用cmd调用exe文件打开都没有问题,想实现一个页面循环调用监听鼠标事件打开(点完这个点那个,但是中间不会重复调用),想问各位这代码怎么修改可以实现呢,现在我只知道调用cmd后鼠标会停留在selenium自动化页面点击的那个标签上,切换到我用cmd打开的浏览器里再点击就会重复点击selenium刚才点击的那个标签

你的鼠标监听器在执行完一次点击操作后没有正确地停止,而是继续监听鼠标事件,并且重复调用你的on_click函数。这样就会导致无限循环打开浏览器

创建一个线程事件,你试下效果如何

import datetime
import os
import pytz
import subprocess
import pathlib
import threading # 导入threading模块

from selenium import webdriver
from pynput import *
from apscheduler.schedulers.background import BackgroundScheduler

folder = pathlib.Path(__file__).parent.resolve()
try:
  os.system("taskkill /f /t /im chromedriver.exe")
except:
  pass
options = webdriver.ChromeOptions()
options.add_experimental_option('detach', True)
options.add_argument('--disable-gpu')
driver = webdriver.Chrome(Options=options)
driver.get('file:///' + f'{folder}/abc.html')
driver.maximize_window()
js='''
  window.hovered_element = null
  function track_mouse(event){
    var x = event.clientX
    var y = event.clientY
    var element = document.elementFromPoint(x, y)
    if(!element){
      window.hovered_element = null
      return //当前页面没有元素
    }
    window.hovered_element = element
  }
  window.onmousemove = track_mouse
'''
driver.execute_script(js)

def cl():
  with mouse.Listener(on_click=on_click) as Listener:
    Listener.join()

def on_click(x, y, button, pressed):
  element_js = driver.execute_script('return window.hovered_element')
  if pressed:
    tag_name = element_js.tag_name
    if tag_name == 'a':
      _href = element_js.get_attribute("_href")
      _type = element_js.get_attribute("_type")
      if _type == 'Chrome':
        subprocess.Popen('"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" '+ _href)
      elif _type == 'IE':
        subprocess.Popen('"C:\\Program Files\\Internet Explorer\\iexplore.exe" '+ _href)
      elif _type == 'FireFox':
        subprocess.Popen('"C:\\Program Files\\Mozilla Firefox\\firefox.exe" '+ _href)
      # 设置线程事件,通知主线程鼠标已经点击
      clicked.set()
      # 返回False,停止监听器
      return False

# 创建一个线程事件对象
clicked = threading.Event()

while True: # 用一个循环来实现页面循环调用监听鼠标事件打开
  # 每次循环前清除线程事件状态
  clicked.clear()
  listener = mouse.Listener(on_click=on_click)
  listener.start()
  try:
    # 等待线程事件,直到鼠标点击为止
    clicked.wait()
  finally:
    # 停止监听器
    listener.stop()


TechWhizKid参考GPT回答:

  • 你可以在打开新的浏览器后暂停或停止鼠标的监听,然后在适当的时候再重新启动监听。下面是一种可能的改进方式:

on_click 函数中,我添加了停止监听的代码,以及在新的浏览器启动后重新启动监听的代码。

def cl():
  with mouse.Listener(on_click=on_click) as Listener:
    Listener.join()

listener = None  # 把监听器设为全局变量

def on_click(x, y, button, pressed):
  global listener
  element_js = driver.execute_script('return window.hovered_element')
  if pressed:
    tag_name = element_js.tag_name
    if tag_name == 'a':
      _href = element_js.get_attribute("_href")
      _type = element_js.get_attribute("_type")
      listener.stop()  # 在打开新浏览器前停止监听
      if _type == 'Chrome':
        subprocess.Popen('"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" '+ _href)
      elif _type == 'IE':
        subprocess.Popen('"C:\\Program Files\\Internet Explorer\\iexplore.exe" '+ _href)
      elif _type == 'FireFox':
        subprocess.Popen('"C:\\Program Files\\Mozilla Firefox\\firefox.exe" '+ _href)
      listener = mouse.Listener(on_click=on_click)  # 启动新的监听器
      listener.start()  # 启动新的监听器

scheduler = BackgroundScheduler(timezone=pytz.timezone("Asia/Shanghai"))
scheduler.add_job(cl, 'date', next_run_time=datetime.datetime.now())
scheduler.start()

这个修改的代码假设你在每次点击后只打开一个新的浏览器,并且在新的浏览器打开后立即返回原来的页面进行下一次点击。如果你的实际情况有所不同,你可能需要调整监听器停止和重启的时间点。

采用chatgpt:
根据你提供的代码和描述,问题出现在鼠标监听事件的重复调用。这是因为你的监听器(mouse.Listener)在调用join()方法后会一直等待鼠标点击事件,而当你使用subprocess.Popen()调用其他浏览器时,监听器仍然在运行,因此会接收到从其他浏览器传递过来的鼠标点击事件,导致重复点击。

为了解决这个问题,你可以在调用其他浏览器之前暂停监听器。具体做法是在调用subprocess.Popen()之前调用Listener.stop()方法停止监听器。这样,当你切换到其他浏览器后,监听器就不会接收到鼠标点击事件了。

修改后的代码如下所示:

import datetime
import os
import pytz
import subprocess
import pathlib
 
from selenium import webdriver
from pynput import *
from apscheduler.schedulers.background import BackgroundScheduler
 
folder = pathlib.Path(__file__).parent.resolve()
try:
    os.system("taskkill /f /t /im chromedriver.exe")
except:
    pass
options = webdriver.ChromeOptions()
options.add_experimental_option('detach', True)
options.add_argument('--disable-gpu')
driver = webdriver.Chrome(options=options)
driver.get('file:///' + f'{folder}/abc.html')
driver.maximize_window()
js='''
  window.hovered_element = null
  function track_mouse(event){
    var x = event.clientX
    var y = event.clientY
    var element = document.elementFromPoint(x, y)
    if(!element){
      window.hovered_element = null
      return //当前页面没有元素
    }
    window.hovered_element = element
  }
  window.onmousemove = track_mouse
'''
driver.execute_script(js)
 
def cl():
    with mouse.Listener(on_click=on_click) as Listener:
        Listener.join()
 
def on_click(x, y, button, pressed):
    element_js = driver.execute_script('return window.hovered_element')
    if pressed:
        tag_name = element_js.tag_name
        if tag_name == 'a':
            _href = element_js.get_attribute("_href")
            _type = element_js.get_attribute("_type")
            if _type == 'Chrome':
                Listener.stop()  # 停止监听器
                subprocess.Popen('"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" '+ _href)
            elif _type == 'IE':
                Listener.stop()  # 停止监听器
                subprocess.Popen('"C:\\Program Files\\Internet Explorer\\iexplore.exe" '+ _href)
            elif _type == 'FireFox':
                Listener.stop()  # 停止监听器
                subprocess.Popen('"C:\\Program Files\\Mozilla Firefox\\firefox.exe" '+ _href)
 
scheduler = BackgroundScheduler(timezone=pytz.timezone("Asia/Shanghai"))
scheduler.add_job(cl, 'date', next_run_time=datetime.datetime.now())
scheduler.start()

这样,当你从 Selenium 打开其他浏览器时,监听器会停止,而在切换回 Selenium 页面后,监听器会继续运行,等待下一个鼠标点击事件。

在上述代码中,我在每个打开浏览器的条件分支中添加了Listener.stop(),以确保在调用其他浏览器之前停止监听器。

参考深度学习模型给你答复:
这个问题涉及到selenium驱动浏览器和pynput监听鼠标事件之间的交互,出现重复点击的原因是selenium驱动浏览器后,鼠标焦点还停留在浏览器上,而 pynput 继续监听鼠标事件,导致重复触发点击事件。

要解决这个问题,可以考虑以下方法:

  1. 在调用os.system()打开新浏览器后,使用pynput暂停监听鼠标事件,等待新浏览器页面加载完成后再继续监听:
# 暂停监听  
listener.stop() 

# 打开新浏览器
os.system(...)

# 等待新页面加载完成
time.sleep(5)  

# 继续监听
listener.start()
  1. 调用driver.switch_to.window()切换selenium控制的窗口,将焦点切换到新打开的浏览器上,避免继续点击原网页元素:
# 获取所有窗口句柄
all_windows = driver.window_handles 

# 切换到新打开窗口
driver.switch_to.window(all_windows[-1])
  1. 在调用os.system()后,使用pynput模拟鼠标移动到页面空白区域,避免继续点击原有元素:
import mouse

# 打开新浏览器后移动鼠标位置  
mouse.move(1000, 500)  
  1. 可以通过终止原selenium驱动的浏览器进程,使其完全释放鼠标焦点:
# 获取chromedriver进程id并终止
driver.service.process.pid 
os.kill(pid, signal.SIGTERM)

综合使用这些方法,可以避免selenium和pynput之间的鼠标事件冲突,解决重复点击的问题。

基于bing、GPT部分内容和本人思考总结:
可以考虑在监听鼠标点击事件时,记录上一次点击的标签内容,并在下一次点击时判断当前点击的标签内容是否与上一次相同。如果相同则不进行任何操作,如果不同则根据标签内容选择调用不同浏览器打开该网址。 示例代码:

from selenium import webdriver
from pynput import mouse
import os
last_label = None
def on_click(x, y, button, pressed):
    global last_label
    if pressed and button == mouse.Button.left:
        element = driver.find_element_by_xpath("//xpath/to/element")
        label = element.text
        if label != last_label:
            last_label = label
            if label == "xxx":
                os.system("start xxx.exe")
            elif label == "yyy":
                os.system("start yyy.exe")
        return False
driver = webdriver.Chrome()
driver.get("https://example.com")
with mouse.Listener(on_click=on_click) as listener:
    listener.join()

在上面的代码中,last_label用于记录上一次点击的标签内容。当监听到鼠标点击事件时,获取当前点击的标签内容,并与上一次点击的标签内容进行比较。如果不同,则记录当前标签内容为上一次点击的标签内容,并根据标签内容选择调用不同浏览器打开该网址。如果相同,则不进行任何操作。 需要注意的是,os.system()是一个同步的调用方式,会阻塞当前进程直到被调用的程序执行完毕。如果需要异步调用程序,可以考虑使用subprocess.Popen()。

在你的代码中,点击事件会触发on_click函数,而on_click函数会调用subprocess.Popen来打开浏览器。由于subprocess.Popen是一个异步操作,会立即返回,因此当你切换到通过subprocess.Popen打开的浏览器后,鼠标点击事件仍然会传递到原来的页面,导致重复点击。

为了解决这个问题,你可以尝试在on_click函数中添加一个适当的延迟,以确保切换到新打开的浏览器后再进行鼠标点击监听。你可以使用time.sleep()函数来添加延迟,例如:

import time

def on_click(x, y, button, pressed):
  element_js = driver.execute_script('return window.hovered_element')
  if pressed:
    tag_name = element_js.tag_name
    if tag_name == 'a':
      _href = element_js.get_attribute("_href")
      _type = element_js.get_attribute("_type")
      if _type == 'Chrome':
        subprocess.Popen('"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" '+ _href)
      elif _type == 'IE':
        subprocess.Popen('"C:\\Program Files\\Internet Explorer\\iexplore.exe" '+ _href)
      elif _type == 'FireFox':
        subprocess.Popen('"C:\\Program Files\\Mozilla Firefox\\firefox.exe" '+ _href)
  # 添加适当的延迟
  time.sleep(1)

这样,当你切换到通过subprocess.Popen打开的浏览器后,会等待1秒钟再进行鼠标点击监听,从而避免了重复点击问题。

请注意,根据实际情况,你可能需要调整延迟的时间来适应浏览器的加载速度和响应时间。

根据你提供的代码描述,可能出现无限重复打开的问题是因为在监听鼠标点击事件时没有正确地处理事件的触发条件。以下是一个示例代码,展示了如何在使用 Selenium 和 pynput 监听鼠标点击事件时避免重复点击的问题:

import os
from pynput.mouse import Listener, Button
from selenium import webdriver

def open_url(url):
    # 这里使用 Selenium 打开网页
    driver = webdriver.Chrome()
    driver.get(url)

def on_click(x, y, button, pressed):
    if button == Button.left and pressed:
        element = driver.find_element_by_xpath("//your/xpath")  # 替换为你想要点击的标签的 XPath
        if element.text == "Your desired text":  # 替换为你想要点击的标签的内容
            url = element.get_attribute("href")  # 获取标签的链接地址
            open_url(url)
            listener.stop()

# 这里需要创建一个 Selenium WebDriver 实例
driver = webdriver.Chrome()

# 这里设置监听鼠标点击事件
with Listener(on_click=on_click) as listener:
    listener.join()

在上述示例代码中,首先通过 Selenium 创建一个 WebDriver 实例(这里使用 ChromeDriver),然后在 on_click 回调函数中,通过 XPath 定位到你想要点击的标签,并检查其文本内容是否符合预期。如果符合预期,获取标签的链接地址,并调用 open_url 函数使用 Selenium 打开该链接。最后,通过 listener.stop() 停止监听鼠标点击事件。

请注意,在使用 Selenium 打开链接之前,你需要根据实际情况进行必要的 WebDriver 初始化操作(例如设置 ChromeDriver 的路径、配置选项等)。