当我的selenium程序由于某些错误而崩溃时,它似乎留下了正在运行的进程。
例如,这是我的进程列表:
carol 30186 0.0 0.0 103576 7196 pts/11 Sl 00:45 0:00 /home/carol/test/chromedriver --port=51789
carol 30322 0.0 0.0 102552 7160 pts/11 Sl 00:45 0:00 /home/carol/test/chromedriver --port=33409
carol 30543 0.0 0.0 102552 7104 pts/11 Sl 00:48 0:00 /home/carol/test/chromedriver --port=42567
carol 30698 0.0 0.0 102552 7236 pts/11 Sl 00:50 0:00 /home/carol/test/chromedriver --port=46590
carol 30938 0.0 0.0 102552 7496 pts/11 Sl 00:55 0:00 /home/carol/test/chromedriver --port=51930
carol 31546 0.0 0.0 102552 7376 pts/11 Sl 01:16 0:00 /home/carol/test/chromedriver --port=53077
carol 31549 0.5 0.0 0 0 pts/11 Z 01:16 0:03 [chrome] <defunct>
carol 31738 0.0 0.0 102552 7388 pts/11 Sl 01:17 0:00 /home/carol/test/chromedriver --port=55414
carol 31741 0.3 0.0 0 0 pts/11 Z 01:17 0:02 [chrome] <defunct>
carol 31903 0.0 0.0 102552 7368 pts/11 Sl 01:19 0:00 /home/carol/test/chromedriver --port=54205
carol 31906 0.6 0.0 0 0 pts/11 Z 01:19 0:03 [chrome] <defunct>
carol 32083 0.0 0.0 102552 7292 pts/11 Sl 01:20 0:00 /home/carol/test/chromedriver --port=39083
carol 32440 0.0 0.0 102552 7412 pts/11 Sl 01:24 0:00 /home/carol/test/chromedriver --port=34326
carol 32443 1.7 0.0 0 0 pts/11 Z 01:24 0:03 [chrome] <defunct>
carol 32691 0.1 0.0 102552 7360 pts/11 Sl 01:26 0:00 /home/carol/test/chromedriver --port=36369
carol 32695 2.8 0.0 0 0 pts/11 Z 01:26 0:02 [chrome] <defunct>
这是我的代码:
from selenium import webdriver
browser = webdriver.Chrome("path/to/chromedriver")
browser.get("http://stackoverflow.com")
browser.find_element_by_id('...').click()
browser.close()
有时,浏览器无法足够快地加载网页元素,因此当Selenium尝试点击它找不到的内容时会崩溃。其他时候它工作正常。
为简单起见,这是一个简单的例子,但是对于更复杂的selenium程序,什么是保证干净的退出方式而不是留下正在运行的进程?它应该在意外崩溃和成功运行时干净地退出。
答案 0 :(得分:3)
每次Selenium在Chrome上运行时,Chromedriver.exe都会占用TaskManager(如果是Windows)。有时,即使浏览器没有崩溃,它也不会清除。
我经常运行bat文件或cmd来杀死所有现有的chromedriver.exe进程,然后启动另一个进程。
看看这个:release Selenium chromedriver.exe from memory
答案 1 :(得分:3)
发生的事情是你的代码抛出异常,暂停python进程继续。因此,close / quit方法永远不会在浏览器对象上调用,因此chromedrivers只是无限期地挂起。
您需要使用try / except块来确保每次调用close方法,即使抛出异常也是如此。一个非常简单的例子是:
from selenium import webdriver
browser = webdriver.Chrome("path/to/chromedriver")
try:
browser.get("http://stackoverflow.com")
browser.find_element_by_id('...').click()
except:
browser.close()
browser.quit() # I exclusively use quit
您可以在此处采用许多更复杂的方法,例如创建与with
语句一起使用的上下文管理器,但如果不更好地理解您的代码库,很难推荐它。
答案 2 :(得分:1)
我看到了这个相当古老的线程,但是也许我的情况对某人有用。
由于某些原因,对于带有Xvfb的Docker容器中的每个请求,我不得不使用带有headfull(非headless)浏览器的单独的webdriver实例运行大量抓取工具。因此,每个请求都会使用Firefox生成2-3个僵尸进程。 (和12个Chromedriver)。
因此,经过几分钟的刮擦,我经历了成千上万的僵尸进程。
driver.close()
和driver.quit()
没有成功。
Jimmy's Engelbrecht解决方案更好,但仅杀死了部分进程。
因此,对我而言,唯一可行的方法是在Docker容器中启用init
。
docker run --init container
它可以保护您免受意外创建僵尸进程的软件的侵害,僵尸进程会(随着时间的推移!)使整个系统缺乏PID(并使其无法使用)。
docker-compose
,则可以按照this answer 答案 3 :(得分:0)
正如已经指出的,您应该运行browser.quit()
但是在linux上(在docker内部),这将留下已失效的进程。这些通常不是真正的问题,因为它们仅是过程表中的一项,并且不消耗资源。但是,如果您有很多,您将不会失去流程。通常,我的服务器在65,000个进程中崩溃。
它看起来像这样:
# root@dockerhost1:~/odi/docker/bf1# ps -ef | grep -i defunct | wc -l
28599
root@dockerhost1:~/odi/docker/bf1# ps -ef | grep -i defunct | tail
root 32757 10839 0 Oct18 ? 00:00:00 [chrome] <defunct>
root 32758 895 0 Oct18 ? 00:00:02 [chrome] <defunct>
root 32759 15393 0 Oct18 ? 00:00:00 [chrome] <defunct>
root 32760 13849 0 01:23 ? 00:00:00 [chrome] <defunct>
root 32761 472 0 Oct18 ? 00:00:00 [chrome] <defunct>
root 32762 19360 0 01:35 ? 00:00:00 [chrome] <defunct>
root 32763 30701 0 00:34 ? 00:00:00 [chrome] <defunct>
root 32764 17556 0 Oct18 ? 00:00:00 [chrome] <defunct>
root 32766 8102 0 00:49 ? 00:00:00 [cat] <defunct>
root 32767 9490 0 Oct18 ? 00:00:00 [chrome] <defunct>
以下代码将解决问题:
def quit_driver_and_reap_children(driver):
log.debug('Quitting session: %s' % driver.session_id)
driver.quit()
try:
pid = True
while pid:
pid = os.waitpid(-1, os.WNOHANG)
log.debug("Reaped child: %s" % str(pid))
except ChildProcessError:
pass
答案 4 :(得分:0)
我遇到了同样的问题:在 docker 中运行 chromedriver。但是当调用 quit() 时,chromedriver 变成了一个僵尸线程。 我使用 dumb-init 来解决我的问题。我猜这个问题不仅仅出现在chromedriver中,它与docker的特性有关,缺少Linux的一些组件,导致无法正确处理子线程。
Dockerfile 添加:
RUN wget https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_amd64.deb
RUN sudo dpkg -i dumb-init_*.deb
ENTRYPOINT ["/usr/bin/dumb-init", "--", "./entrypoint.sh"]
入口点.sh:
#!/bin/sh
echo "使用参数为 $*"
exec java -jar $JAR_NAME "$@"
ENTRYPOINT 和 exec 在 docker 中非常重要。
答案 5 :(得分:-2)
我遇到了同样的问题:在 docker 中运行 chromedriver。但是当调用 quit() 时,chromedriver 变成了一个僵尸线程。 我使用 dumb-init 来解决我的问题。我猜这个问题不仅仅出现在chromedriver中,它与docker的特性有关,缺少Linux的一些组件,导致无法正确处理子线程。
Dockerfile 添加:
RUN wget https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_amd64.deb
RUN sudo dpkg -i dumb-init_*.deb
ENTRYPOINT ["/usr/bin/dumb-init", "--", "./entrypoint.sh"]
入口点.sh:
#!/bin/sh
echo "使用参数为 $*"
exec java -jar $JAR_NAME "$@"
ENTRYPOINT 和 exec 在 docker 中非常重要。