
本文探讨了如何在多个独立浏览器中并行执行自动化任务,并模拟独立的鼠标光标操作。传统方法难以实现,因此我们提出采用基于发布-订阅(pub-sub)模式的领导者-跟随者架构。通过消息队列系统,领导者程序广播指令,而多个跟随者程序各自驱动一个selenium浏览器实例,接收并执行这些指令,从而实现高效且独立的并行自动化。
在自动化测试、数据抓取或多账号操作等场景中,开发者常常面临一个挑战:如何在多个独立的浏览器实例中同时执行任务,并且每个实例都能模拟独立的鼠标光标操作。这与简单的多线程或多进程启动浏览器不同,因为操作系统通常只提供一个物理鼠标光标,像pyautogui这类库的操作会影响到全局光标,无法实现多个独立浏览器中“各自”的鼠标移动和点击。传统的虚拟化方案虽然能隔离环境,但若要实现程序化的独立鼠标控制,通常会带来较高的复杂性和资源开销。
为了有效解决这一问题,核心策略是采用一种分布式通信模式——发布-订阅(Publish-Subscribe, Pub-Sub)模式,并结合领导者-跟随者(Leader-Follower)架构。这种模式将任务的生成与执行解耦,使得多个浏览器实例能够独立地接收和响应指令。
该解决方案的核心在于构建一个领导者程序和N个跟随者程序,并通过一个消息队列系统进行通信。
领导者程序 (Leader Program)
跟随者程序 (Follower Programs)
消息队列系统 (Message Queue System)
以下是使用Python和Selenium结合消息队列系统实现此架构的概念性示例:
首先,选择一个消息队列框架并进行基本配置。以RabbitMQ为例,你需要安装pika库。
pip install pika selenium webdriver_manager
领导者程序负责生成并发布指令。
# leader.py (概念性代码,需根据实际消息队列库调整)
import json
import pika # 假设使用RabbitMQ
class Leader:
def __init__(self, queue_name='browser_commands'):
self.connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
self.channel = self.connection.channel()
self.channel.queue_declare(queue=queue_name)
self.queue_name = queue_name
def publish_command(self, browser_id, command_type, payload):
"""
发布一个指令到消息队列。
:param browser_id: 目标浏览器实例的ID。
:param command_type: 指令类型,如 'mouse_move', 'click', 'navigate'。
:param payload: 指令的具体参数,如坐标、URL等。
"""
message = {
"browser_id": browser_id,
"type": command_type,
"payload": payload
}
self.channel.basic_publish(
exchange='',
routing_key=self.queue_name,
body=json.dumps(message)
)
print(f"Leader published: {message}")
def close(self):
self.connection.close()
if __name__ == "__main__":
leader = Leader()
# 模拟为不同的浏览器实例发布指令
leader.publish_command(1, 'navigate', {'url': 'https://www.example.com'})
leader.publish_command(2, 'navigate', {'url': 'https://www.google.com'})
leader.publish_command(1, 'mouse_move', {'x': 100, 'y': 200})
leader.publish_command(1, 'click', {'x': 100, 'y': 200})
leader.publish_command(2, 'mouse_move', {'x': 50, 'y': 50})
leader.publish_command(2, 'click', {'x': 50, 'y': 50})
leader.close()每个跟随者程序启动一个独立的Selenium浏览器实例,并监听消息队列。
# follower.py (概念性代码,需根据实际消息队列库调整)
import json
import pika
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
import time
class Follower:
def __init__(self, browser_id, queue_name='browser_commands'):
self.browser_id = browser_id
# 初始化Selenium WebDriver
options = webdriver.ChromeOptions()
# 可以添加headless模式,或者为每个浏览器设置不同的用户数据目录以完全隔离
# options.add_argument('--headless')
# options.add_argument(f'--user-data-dir=/tmp/chrome-profile-{browser_id}')
self.driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()), options=options)
print(f"Follower {self.browser_id}: Browser initialized.")
# 初始化消息队列连接
self.connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
self.channel = self.connection.channel()
self.channel.queue_declare(queue=queue_name)
self.queue_name = queue_name
def _execute_command(self, command):
"""根据接收到的指令在浏览器中执行操作。"""
if command['browser_id'] != self.browser_id:
return # 忽略不属于本实例的指令
command_type = command['type']
payload = command['payload']
print(f"Follower {self.browser_id}: Executing {command_type} with payload {payload}")
try:
if command_type == 'navigate':
self.driver.get(payload['url'])
elif command_type == 'mouse_move':
# Selenium的ActionChains模拟鼠标移动到相对页面左上角的坐标
# 注意:这只是将Selenium的内部光标移动到该位置,不影响物理光标
body = self.driver.find_element(By.TAG_NAME, 'body')
ActionChains(self.driver).move_to_element_with_offset(body, payload['x'], payload['y']).perform()
elif command_type == 'click':
# 模拟点击,通常需要先移动到目标位置再点击
body = self.driver.find_element(By.TAG_NAME, 'body')
ActionChains(self.driver).move_to_element_with_offset(body, payload['x'], payload['y']).click().perform()
# 可以添加更多指令类型,如 keyboard_input, find_element_and_click 等
else:
print(f"Follower {self.browser_id}: Unknown command type {command_type}")
except Exception as e:
print(f"Follower {self.browser_id}: Error executing command {command_type}: {e}")
def start_listening(self):
"""开始监听消息队列并处理指令。"""
def callback(ch, method, properties, body):
command = json.loads(body)
self._execute_command(command)
ch.basic_ack(delivery_tag=method.delivery_tag) # 确认消息已处理
self.channel.basic_consume(
queue=self.queue_name,
on_message_callback=callback,
auto_ack=False # 手动确认,确保消息被成功处理
)
print(f"Follower {self.browser_id}: Started listening for commands...")
self.channel.start_consuming()
def close(self):
"""关闭浏览器和消息队列连接。"""
self.driver.quit()
self.connection.close()
print(f"Follower {self.browser_id}: Closed.")
if __name__ == "__main__":
# 启动多个跟随者实例,每个实例在独立的终端或进程中运行
# 例如,在不同的终端分别运行:
# python follower.py 1
# python follower.py 2
import sys
if len(sys.argv) > 1:
browser_id = int(sys.argv[1])
follower = Follower(browser_id)
try:
follower.start_listening()
except KeyboardInterrupt:
print(f"Follower {browser_id}: Shutting down...")
finally:
follower.close()
else:
print("Usage: python follower.py <browser_id>")通过采用发布-订阅模式和领导者-跟随者架构,我们能够有效解决在多个独立浏览器中并行执行自动化任务并模拟独立鼠标光标的复杂问题。这种方案将任务生成与执行解耦,提高了系统的灵活性、可扩展性和鲁棒性。开发者可以根据具体需求选择合适的消息队列系统,并结合Selenium WebDriver等工具,构建出高效、可靠的多浏览器并行自动化解决方案。
以上就是基于发布-订阅模式实现多浏览器并行独立自动化操作的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号