我使用Selenium Webdriver自动化网站(填写表格并点击)以节省用户的时间。我遇到了一个恼人的问题:
Selenium似乎不支持浏览器的任何事件侦听器 他们自己。关闭浏览器时
driver.quit()
未被调用 还有一个无法使用的驱动程序,它会抛出各种异常。有 无法知道浏览器何时关闭,我无法创建新的 驱动程序实例。
我需要的是在浏览器关闭时通知我的程序的一些方法。
用户可能因任何破坏我程序的原因而关闭浏览器。如果不重新启动应用程序,用户将无法再次运行自动化任务。知道浏览器何时关闭将允许我调用driver.quit()
并在用户想要再次运行时创建一个新的驱动程序实例。
问题进一步复杂化的事实是,当浏览器死亡时,抛出的错误在浏览器中并不统一。使用Firefox,我可能会使用Chrome NullPointer和WebDriverExceptions获得UnreachableBrowserException。
为了澄清,我知道如何关闭驱动程序和浏览器,但我不知道它们何时被外部资源关闭。这可以在Selenium 2中以跨浏览器的方式完成(如果是,如何)或者我是否需要找到另一种方式(如另一个库)来观看浏览器窗口?
答案 0 :(得分:4)
我已经在Selenium中已经包含的JNA(Java Native Access)3.4的帮助下解决了这个问题。我的目标平台仅限Windows,但制作这种跨平台并不需要太多工作。我必须做以下事情:
tasklist
或powershell Get-Process
等实用程序来完成此操作。如果您确定在启动应用程序之前没有运行任何浏览器进程,则可以省略此步骤。Kernel32.INSTANCE.OpenProcess
方法允许我们从pid创建HANDLE
个对象。Kernel32.INSTANCE.WaitForMultipleObjects
方法发出信号。这是我使用的代码,以便它可以帮助其他人:
public void startAutomation() throws IOException {
Set<Integer> pidsBefore = getBrowserPIDs(browserType);
automator.initDriver(browserType); //calls new ChromeDriver() for example
Set<Integer> pidsAfter = getBrowserPIDs(browserType);
pidsAfter.removeAll(pidsBefore);
ProcessGroupExitWatcher watcher = new ProcessGroupExitWatcher(pidsAfter);
watcher.addProcessExitListener(new ProcessExitListener() {
@Override
public void processFinished() {
if (automator != null) {
automator.closeDriver(); //calls driver.quit()
automator = null;
}
}
});
watcher.start();
//do webdriver stuff
}
private Set<Integer> getBrowserPIDs(String browserType) throws IOException {
Set<Integer> processIds = new HashSet<Integer>();
//powershell was convenient, tasklist is probably safer but requires more parsing
String cmd = "powershell get-process " + browserType + " | foreach { $_.id }";
Process processes = Runtime.getRuntime().exec(cmd);
processes.getOutputStream().close(); //otherwise powershell hangs
BufferedReader input = new BufferedReader(new InputStreamReader(processes.getInputStream()));
String line;
while ((line = input.readLine()) != null) {
processIds.add(Integer.parseInt(line));
}
input.close();
return processIds;
}
守望者的代码:
/**
* Takes a <code>Set</code> of Process IDs and notifies all listeners when all
* of them have exited.<br>
*/
public class ProcessGroupExitWatcher extends Thread {
private List<HANDLE> processHandles;
private List<ProcessExitListener> listeners = new ArrayList<ProcessExitListener>();
/**
* Initializes the object and takes a set of pids and creates a list of
* <CODE>HANDLE</CODE>s from them.
*
* @param processIds
* process id numbers of the processes to watch.
* @see HANDLE
*/
public ProcessGroupExitWatcher(Set<Integer> processIds) {
processHandles = new ArrayList<HANDLE>(processIds.size());
//create Handles from the process ids
for (Integer pid : processIds) {
processHandles.add(Kernel32.INSTANCE.OpenProcess(Kernel32.SYNCHRONIZE, false, pid)); //synchronize must be used
}
}
public void run() {
//blocks the thread until all handles are signaled
Kernel32.INSTANCE.WaitForMultipleObjects(processHandles.size(), processHandles.toArray(new HANDLE[processHandles.size()]), true,
Kernel32.INFINITE);
for (ProcessExitListener listener : listeners) {
listener.processFinished();
}
}
/**
* Adds the listener to the list of listeners who will be notified when all
* processes have exited.
*
* @param listener
*/
public void addProcessExitListener(ProcessExitListener listener) {
listeners.add(listener);
}
/**
* Removes the listener.
*
* @param listener
*/
public void removeProcessExitListener(ProcessExitListener listener) {
listeners.remove(listener);
}
}
注意:以这种方式使用PowerShell时,会执行用户的配置文件脚本。这可能会产生意外的输出,从而破坏了上述代码。因此,建议使用任务列表。