39

I initiated and close phantomjs in Python with the following

from selenium import webdriver    
driver = webdriver.PhantomJS()
driver.get(url)
html_doc = driver.page_source
driver.close()

yet after the script ends execution I still find an instance of phantomjs in my Mac Activity Monitor. And actually every time I run the script a new process phantomjs is created.

How should I close the driver?

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
CptNemo
  • 6,455
  • 16
  • 58
  • 107

7 Answers7

45

As of July 2016, driver.close() and driver.quit() weren't sufficient for me. That killed the node process but not the phantomjs child process it spawned.

Following the discussion on this GitHub issue, the single solution that worked for me was to run:

import signal

driver.service.process.send_signal(signal.SIGTERM) # kill the specific phantomjs child proc
driver.quit()                                      # quit the node proc
leekaiinthesky
  • 5,413
  • 4
  • 28
  • 39
  • 3
    This method works the best for me (as of this comment date). I also found the Github issue but wanted to second it here! – Geoff Sep 16 '16 at 19:02
  • 1
    +1 this works for me too. A little frustrating when the GitHub unresolved issue with what seems like PhantomJS builders seem to have no interest in resolving this rather substantial bug with their script. Perhaps this solution could be hard coded into the PhantomJS script itself? – ntk4 Sep 21 '16 at 16:10
  • until now, i had a fork bomb in my machine! – Panagiotis Simakis May 19 '17 at 13:27
  • @leekaiinthesky But after ‍‍`driver.quit()` and `driver.close()` I use it, I receive the `SessionManagerReqHand - _cleanupWindowlessSessions - Asynchronous Sessions clean-up phase starting NO` messages. What is the reason How can I fix the problem ?! – Ali Hesari Jul 21 '17 at 07:01
  • @AliHesari Your best shot at getting that answered here would be to post your question with the details as a new question on the site. Good luck! – leekaiinthesky Jul 21 '17 at 07:25
22

Please note that this will obviously cause trouble if you have several threads/processes starting PhantomJS on your machine.

I've seen several people struggle with the same issue, but for me, the simplest workaround/hack was to execute the following from the command line through Python AFTER you have invoked driver.close() or driver.quit():

pgrep phantomjs | xargs kill
whirlwin
  • 16,044
  • 17
  • 67
  • 98
  • 1
    @Arya It will indeed cause problems with any other process/thread using PhantomJS, so you should not use this approach unless you have absolute control over what is running on your machine. – whirlwin Aug 05 '15 at 08:44
  • 1
    See http://stackoverflow.com/questions/25110624/how-to-properly-stop-phantomjs-execution/38493285#38493285 for an answer that kills only the specific single `phantomjs` process involved rather than all of them. – leekaiinthesky Oct 03 '16 at 01:21
  • 2
    if you're going to cut corners then you might as well use `killall phantomjs` – likewhoa Mar 08 '17 at 03:00
  • @whirlwin Sorry, but I have to down-vote this given it'll be mostly new developers that follow this advice, which in turn will cause huge problems on shared machines with other developer's processes and unfair aggro in return. I'll up-vote though if the end-warning is moved to the start. – Tom Oct 25 '17 at 09:14
  • @Tom Thanks for the feedback! – whirlwin Oct 26 '17 at 06:58
19

The .close() method is not guaranteed to release all resources associated with a driver instance. Note that these resources include, but may not be limited to, the driver executable (PhantomJS, in this case). The .quit() method is designed to free all resources of a driver, including exiting the executable process.

JimEvans
  • 27,201
  • 7
  • 83
  • 108
6

I was having a similar issue on Windows machine. I had no luck with either

driver.close()

or

driver.quit()

actually closing out of the PhantomJS window, but when I used both, the PhantomJS window finally closed and exited properly.

driver.close()
driver.quit()
John H.
  • 61
  • 1
  • 4
  • 1
    This also worked for me, and I think it should be marked as the correct solution. It's not elegant, but it is much better than the other brute force methods mentioned. Nice find. – C.Buhl Jul 26 '16 at 13:17
  • 1
    +1 this worked for me too. I like this solution a little bit more then @leekaiinthesky 's solution by kiling the service using signal because I don't have to import another library `import signal` – ntk4 Sep 21 '16 at 16:12
2

driver.quit() did not work for me on Windows 10, so I ended up adding the following line right after calling driver.close():

os.system('taskkill /f /im phantomjs.exe')

where

/f = force
/im = by image name

And since this is a Windows only solution, it may be wise to only execute if os.name == 'nt'

FoxMulder900
  • 1,272
  • 13
  • 27
2

What is the OS you are using? I think it corresponds to the case of the next, if you are using the POSIX OS.

I create pull request, but it rejected. https://github.com/SeleniumHQ/selenium/pull/2244

But I think obviously the correct it. Therefore , I issued a issue. https://github.com/SeleniumHQ/selenium/issues/2272

The root cause of this problem is that the end method of ghost driver mode phatmojs is incorrect. It is not not use the shutdown API of ghost driver mode phantomjs at the end.

In the case of phantomjs that you installed in npm on Linux or OSX, A selenium call Popen for phantomjs, A phantomjs call spawn for lib/phantomjs.js. At this time, a selenium is parent, a phantomjs is child, and lib/phantomjs.js is grandchild.

You call quit() in parent(selenium), it send SIGTERM to child(phantomjs). and a child(phantomjs) send SIGTERM to grandchild(lib/phantomjs.js) in the child's SIGTERM handler function.

A grandchild will be zombie when parent send SIGKILL to child before the child send SIGTERM to grandchild.

This pull request ttps://github.com/SeleniumHQ/selenium/pull/2244 be shutdown using ghost driver mode shutdown api.

 def send_remote_shutdown_command(self):
      super(Service, self).send_remote_shutdown_command()  ## ADD A NEW LINE HERE
      if self._cookie_temp_file:
          os.close(self._cookie_temp_file_handle)
          os.remove(self._cookie_temp_file)

Other solutions, sleep between "self.process.ternimate()" and "self.process.kill()". ttps://github.com/SeleniumHQ/selenium/blob/051c8b110a1aec35247cd45fa4db85c6e522cdcb/py/selenium/webdriver/common/service.py#L151-L153

        self.process.terminate()
        time.sleep(1)  ## ADD A NEW LINE HERE
        self.process.kill()
        self.process.wait()
0

I also have a python script running on my mac using selenium to do some stuff using PhantomJS as the webdriver.

When my test is running, there are three processes to note are here:

$ ps -ef | grep [p]hantomjs
  501 28085 24925   0  9:03pm ttys002    0:00.34 python test.py
  501 28088 28085   0  9:03pm ttys002    0:00.14 node /usr/local/bin/phantomjs --cookies-file=/var/folders/nq/hjz03w6d4fs620197d_zwg0m0000gn/T/tmp8xLNaH --webdriver=55075
  501 28090 28088   0  9:03pm ttys002    0:00.71 /usr/local/lib/node_modules/phantomjs/lib/phantom/bin/phantomjs --cookies-file=/var/folders/nq/hjz03w6d4fs620197d_zwg0m0000gn/T/tmp8xLNaH --webdriver=55075

Note the second column which are the process numbers, and the third which are that processes parent. My test script is the parent. There is a node process which has my test script as a parent, then there is another PhantomJS process whose parent is the node process. Don't ask me why there's two PhantomJS processes, I guess it is just how it's designed to work?

Anyway, in my mac's activity monitor, I can see this:

enter image description here

Note the PID number 28090.

After my test finishes running this, processes hangs around, just like you too. If I check for still running processes I can see:

$ ps -ef | grep [p]hantomjs
  501 28090     1   0  9:03pm ttys002    0:18.93 /usr/local/lib/node_modules/phantomjs/lib/phantom/bin/phantomjs --cookies-file=/var/folders/nq/hjz03w6d4fs620197d_zwg0m0000gn/T/tmp8xLNaH --webdriver=55075

So it appears to me that driver.quit() exits the node process, the one with PID number 28088, but it leaves its child orphaned. I don't know if this is intentional. If it's not intentional, then I think there isn't a 'proper' way to exit this process in your code.

Therefore I would use your language's equivalent of kill -9 28090, just after driver.quit()

Sebastian
  • 205
  • 5
  • 12