1

First, I'm using Django, and I'm building an API. For example, I've route like api/get-some-element.

browser_url = cache.get('user_%d.browser_url' % user_id)
is_browser_queued = cache.get('user_%d.browser_queued' % user_id)
browser_session_id = cache.get('user_%d.browser_session_id' % user_id) # session id of the main chrome

if browser_url:
    capabilities = DesiredCapabilities.CHROME.copy()
    driver = webdriver.Remote(
        command_executor=browser_url,
        desired_capabilities=capabilities,
        options=options,
    )
    driver.session_id = browser_session_id

    return driver

if not is_browser_queued:
    run_browser.delay(user_id) # launch main chrome
    cache.set('user_%d.browser_queued' % user_id, True, None)

On the first access to that route, it will send a task to Celery to launch Selenium Headless Chrome (main chrome). Why I'm using Celery? Because I need to make Chrome always running. (Idk better way, this is what I know).

Then, for the next access on that route, it will response with Waiting chrome to launched., until the Celery task got executed (chrome launched properly).

After main chrome is launched, then it will launch Selenium Headless Remote Driver immediately (without Celery), the purpose of this remote driver is to access the main chrome. Then, it just grab some element from a website.

But, after finish, the Remote Driver is still running. How to stop that?

I know the command such as driver.quit() or driver.close(). But, as far as I know, it's send the command to the main chrome, not to the chrome launched by remote driver. And that's not what I want. I don't want to quit the main chrome, just quit the chrome launched by remote driver.

nmfzone
  • 2,755
  • 1
  • 19
  • 32
  • Assuming your both chrome open using same chromedriver(server ) . Store the session id created by what you are referring as 'Remote driver'. someremotesession = driver.session_id . before this line driver.session_id = browser_session_id . Before calling quit again change driver.session_id = someremotesession and call quit() . Your 'Remote driver' chrome should quit. – Rahul L Jan 02 '20 at 05:29
  • @RahulL You're correct. That's what I've done right now (I mean before you came with that solution). – nmfzone Jan 02 '20 at 15:52
  • Sorry anyone, I think I've got the answer by myself. I'll post the solution next time.. – nmfzone Jan 02 '20 at 15:53

4 Answers4

0

driver.close(), driver.quit() , driver.close closes the current window and driver.quit closes all the windows , also I highly recommend you don’t use selenium , it is very slow and the same thing can be achieved with requests

Noah
  • 154
  • 11
  • Thanks for the answer. Have you ever try what you've suggested? I know that command, but, it's send command to the main chrome, not to the remote driver. I mean, it's `quit` the main chrome, not the remote driver. – nmfzone Dec 29 '19 at 22:54
  • Sorry, but I just give an example in my question, not the real one. No, in my case, it's impossible using requests or urllib. Because it needs to interract with javascripts. – nmfzone Dec 29 '19 at 22:58
  • You can execute JavaScript using requests @nmfzone – Noah Dec 29 '19 at 23:09
  • Have you ever try it? I've used it by the way. Show me then how..for example, execute jQuery on that page.. – nmfzone Dec 29 '19 at 23:29
  • https://stackoverflow.com/questions/26393231/using-python-requests-with-javascript-pages – Noah Dec 29 '19 at 23:44
  • What do you mean? Show me how. I've open that question before by the way. And `sberry` said that it's impossible execute javascript using requests. – nmfzone Dec 30 '19 at 00:00
  • Sorry, I'm not mad. I just suggest you to read `sberry` comment. I've read that `requests-html`, but It's just as the same, requests is just html parsing. We can't instruct requests to execute jQuery. If you could show me how, then all is good.. – nmfzone Dec 30 '19 at 00:27
  • Ah, my bad I didn’t see his comment below , sorry about that – Noah Dec 30 '19 at 00:29
  • @nmfzone - did u tried executing kill chromedriver.exe something like this on remote server. this may not be optimized solution – Amit Jain Jan 02 '20 at 19:50
  • @AmitJain Yes, I've tried that. I'm using `psutil`. But, it's only work except for Remote Driver, since I can't get PID of the remote driver. – nmfzone Jan 03 '20 at 02:52
  • @nmfzone - as u said you are running test cases in remote machine, so when you execute code to execute shell command in remote machine is that working or not ? – Amit Jain Jan 04 '20 at 16:34
  • @AmitJain Of course it's working fine. This is just about how to quit the driver properly.. – nmfzone Jan 06 '20 at 00:10
0

The problem is with driver.quit() method only for Chrome. The driver quit didnt work properly it didnt kill all processes of chrome (including child processes). what you can do is change Selenium jar codes to fix this.

or you can make use of finally block.

System.setProperty("webdriver.chrome.driver","/<path to chrome driver>/chromedriver");
    ChromeOptions options = new ChromeOptions();
    options.setBinary(new File("/<path to chrome >/google-chrome"));
    driver = new ChromeDriver(options);
    try
    {
        //automated steps
    }
    finally
    {
        driver.quit();
    }
Abhishek Garg
  • 3,092
  • 26
  • 30
0

Are you using multiple Chrome windows?

driver.close should work, because it closes only the focused window. Maybe you should try:

driver.switch_to.window(driver.window_handles[1])
driver.close()

In this case, you should find the certain window to switch for. Please, let me know if that works

rmesteves
  • 3,870
  • 7
  • 23
0

You are starting an external process and want to stop it from another (not the one that started it) python process. For this you can save process pid or name and kill it with os.kill(pid, signal.SIGKILL) or with similar alternatives.


You can also start chromedriver as a service:

service = Service('/path/to/chromedriver')
service.start()
...
service.stop()

which uses subprocess.Popen at the back. As you might be starting from one python process / celery worker and later stopping from another - save service.pid in cache / database / queue / etc and send signals to process pid directly, rather than doing service.stop().

One possible downside of killing process directly - it may not perform all clean-up tasks correctly (i.e. close opened sockets), it may hang, you may need more logic to control its status etc.


Alternatives to control another process - are to rely on external service manager, like systemd or docker. This separates all process control logic in their configuration (service file or Dockerfile), makes easier to control that only one instance is run and much more.

  • launch chromedriver as system service with systemd as a service. You can control from python with i.e. pystemd
  • or if you are working with docker - launch chromedriver as docker container with docker-python

With these options you are telling external managers to start / stop service, not trying to control it from your app.

In distributed environment you may want to make sure chromedriver is launched only on specific node - i.e. make only celery worker on it to listen to broker queue with chromedriver launch tasks or connect to docker engine on target node or use node_id / tag in swarm / k8s ...

Oleg Russkin
  • 4,234
  • 1
  • 8
  • 20