【问题标题】:Selenium occasional UnreachableBrowserExceptionSelenium 偶尔出现 UnreachableBrowserException
【发布时间】:2015-01-13 15:50:50
【问题描述】:

我正在尝试使用 Java 中的 Selenium 访问多个网站。有时,我会收到UnreachableBrowserException。我已经阅读了许多有关此错误的线程,但似乎有许多不同的错误原因。当我尝试访问新页面时,大约有 1% 的时间会出现错误,但我找不到事件之间的任何相似之处。我目前正在使用 Firefox,但是我也尝试过 Internet Explorer 并遇到了类似的错误。我一次只打开一个页面,并尝试使用相同的窗口并在尝试访问另一个页面之前完全退出驱动程序,但无论哪种方式,错误仍然存​​在。重要的是要注意我并不总是得到这个错误,有时我的代码可以运行而不会发生这种情况。这是错误消息:

Jan 12, 2015 10:39:40 PM org.apache.http.impl.execchain.RetryExec execute
INFO: I/O exception (java.net.SocketException) caught when processing request to {}-      http://127.0.0.1:7055: Permission denied: connect
Jan 12, 2015 10:39:40 PM org.apache.http.impl.execchain.RetryExec execute
INFO: Retrying request to {}->http://127.0.0.1:7055
Jan 12, 2015 10:39:40 PM org.apache.http.impl.execchain.RetryExec execute
INFO: I/O exception (java.net.SocketException) caught when processing request to {}->http://127.0.0.1:7055: Permission denied: connect
Jan 12, 2015 10:39:40 PM org.apache.http.impl.execchain.RetryExec execute
INFO: Retrying request to {}->http://127.0.0.1:7055
Jan 12, 2015 10:39:40 PM org.apache.http.impl.execchain.RetryExec execute
INFO: I/O exception (java.net.SocketException) caught when processing request to {}->http://127.0.0.1:7055: Permission denied: connect
Jan 12, 2015 10:39:40 PM org.apache.http.impl.execchain.RetryExec execute
INFO: Retrying request to {}->http://127.0.0.1:7055
Jan 12, 2015 10:39:40 PM org.apache.http.impl.execchain.RetryExec execute
INFO: I/O exception (java.net.SocketException) caught when processing request to {}->http://127.0.0.1:7055: Permission denied: connect
Jan 12, 2015 10:39:40 PM org.apache.http.impl.execchain.RetryExec execute
INFO: Retrying request to {}->http://127.0.0.1:7055
Jan 12, 2015 10:39:40 PM org.apache.http.impl.execchain.RetryExec execute
INFO: I/O exception (java.net.SocketException) caught when processing request to {}->http://127.0.0.1:7055: Permission denied: connect
Jan 12, 2015 10:39:40 PM org.apache.http.impl.execchain.RetryExec execute
INFO: Retrying request to {}->http://127.0.0.1:7055
Jan 12, 2015 10:39:40 PM org.apache.http.impl.execchain.RetryExec execute
INFO: I/O exception (java.net.SocketException) caught when processing request to {}->http://127.0.0.1:7055: Permission denied: connect
Jan 12, 2015 10:39:40 PM org.apache.http.impl.execchain.RetryExec execute
INFO: Retrying request to {}->http://127.0.0.1:7055
Exception in thread "main" org.openqa.selenium.remote.UnreachableBrowserException: Error communicating with the remote browser. It may have died.
Build info: version: '2.44.0', revision: '76d78cf', time: '2014-10-23 20:03:00'
System info: host: '****', ip: '**.*.*.*', os.name: 'Windows 7', os.arch: 'amd64', os.version: '6.1', java.version: '1.7.0_60'
Driver info: driver.version: RemoteWebDriver
    at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:593)
    at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:614)
    at org.openqa.selenium.remote.RemoteWebDriver.quit(RemoteWebDriver.java:468)
    at scrape.Scraper.killInstance(Scraper.java:162)
    at scrape.Updater.main(Updater.java:93)
Caused by: java.net.SocketException: Permission denied: connect
    at java.net.DualStackPlainSocketImpl.connect0(Native Method)
    at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
    at java.net.PlainSocketImpl.connect(Unknown Source)
    at java.net.SocksSocketImpl.connect(Unknown Source)
    at java.net.Socket.connect(Unknown Source)
    at org.apache.http.conn.socket.PlainConnectionSocketFactory.connectSocket(PlainConnectionSocketFactory.java:72)
    at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:123)
    at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:318)
    at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:363)
    at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:219)
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
    at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86)
    at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:72)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:57)
    at org.openqa.selenium.remote.HttpCommandExecutor.fallBackExecute(HttpCommandExecutor.java:215)
    at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:184)
    at org.openqa.selenium.firefox.internal.NewProfileExtensionConnection.execute(NewProfileExtensionConnection.java:165)
    at org.openqa.selenium.firefox.FirefoxDriver$LazyCommandExecutor.execute(FirefoxDriver.java:362)
    at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:572)
    ... 4 more

我怎样才能防止这个错误或至少捕捉到错误并有效地处理它?

【问题讨论】:

  • 捕获异常、退出浏览器并重新启动时会发生什么?那它行得通吗?你有我的同情 - 这些例外是可怕的。
  • 这可能是一个愚蠢的问题,但是哪个异常以及您将如何尝试捕获它并重新启动?我试图捕捉 SocketException,因为它似乎首先发生但由于某种原因不起作用。你能给我举个例子吗?
  • 你看到我的回答了吗?有帮助吗?
  • 是的,这有帮助,但有时我在运行 newFirefoxDriver() 行时会遇到无法访问的浏览器异常,这意味着我在 catch 块中遇到了异常。你知道如何解决这个问题吗?
  • 你是对的 - 可以在 catch 块内抛出的异常是一个坏主意。我修改了答案中的代码以消除这种可能性。立即尝试 - 它应该会更好。

标签: java selenium exception-handling selenium-webdriver web-scraping


【解决方案1】:

UnreachableBrowserExceptions 的发生可能有多种原因 - 最明显的是浏览器已关闭,无论是在代码中还是在 GUI 中,然后代码尝试访问它。通常,就像您的情况一样,它们是由套接字错误引起的。这可能又意味着多方面的事情——你的程序试图打开太多的套接字,它无法连接到远程网站,等等。

我建议在这种情况下做的是等待一小段时间,然后重试看看是否仍然抛出异常。有时这些情况会自行解决,您的程序可以恢复。

这里有一些代码可以做到这一点。只要抛出 UnreachableBrowserException 并且重试次数低于您设置的某个限制,它就会一直重试。如果它达到重试限制并且仍然抛出异常,它会关闭浏览器并重新启动它,将重试计数重置为 0。还有一个重新启动计数器,以确保如果由于某种原因重新启动浏览器不会帮助,您不会无休止地循环运行代码->异常->等待->重试->达到重试限制,重新启动浏览器->运行代码->异常。这里,超过重启限制(或成功访问浏览器)将跳出循环。

如果您需要更多帮助,请告诉我。希望对您有所帮助!

WebDriver driver = new FirefoxDriver(); //or whatever you're using
boolean worked = false;
int numredos = 0;
final int REDO_LIMIT = 3; //or however many times you want to retry before giving up
final int RESTART_LIMIT = 3; //or however many times you want to restart the browser b/f terminating    
int numrestarts = 0;
boolean restart = false;
do
{
  try{

       if(restart)
       {
         driver = new FirefoxDriver();
         numrestarts++;
       }

    //RUN YOUR BROWSER CODE HERE
      worked = true;                                                        
     }

  //if the browser becomes unreachable (probably b/c of a socket issue), 
// write the error to the log and then sleep for 10 seconds
//if we've already retried the set limit number of times, restart the browser and try again
   catch (UnreachableBrowserException ube)
   {
     worked = false;
     if(numredos >= REDO_LIMIT)
     {
       //if you've already restarted the browser too many times, it will set it to null
       //and return an error code. If not, it will set the restart flag so it will be restarted on the next iteration.

         //try quitting. If it can't do it, it's already dead; just set it to null 
         //(set it to null either way, just in case)
          try
           {
            driver.quit();
           }
          catch(Exception j)
           {
            errorwriter.println(j);
           }

          driver = null;

          if(numrestarts < RESTART_LIMIT)
           {
             //log that you're restarting the driver (not coded here), then set the restart flag to true. This will cause the browser to be restarted after falling out of the catch block
             numredos = 0;
             restart = true;
           }

       }

      else
      {
        //print details of the exception to the error file
        errorfile.println("\n\n\n");
        //timestamp, and some exception details - you can decide which you want
        errorfile.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime()));
        errorfile.println(s.getClass());
        errorfile.println(s.getMessage());
        errorfile.println("Cause: " + s.getCause());
        errorfile.flush();

        //now sleep for some number of seconds - here 10
        try
        {
         TimeUnit.SECONDS.sleep(10);
        }

        catch(InterruptedException e)
        {
          System.out.println("waiting after socket crash interrupted");
        }

       numredos++;
      }

    }

}while(!worked && numredos <= REDO_LIMIT && numrestarts <= RESTART_LIMIT);

【讨论】: