多进程下点击终止按钮无法关闭Selenium WebDriver浏览器的问题
问题
启动程序点击按钮后,爬取和进程终止功能正常,但浏览器无法关闭。需求是点击终止按钮时关闭浏览器,但不想直接调用self.browser.quit()。
现有代码:
Wrapper类代码
class Wrapper(Process): def __init__(self): self.crawler = self.Cralwer() super().__init__(target=self.crawler.start_crawling) def starter(self): self.start() def exit(self): self.terminate() class Cralwer: def __del__(self) -> None: try: self.browser.quit() except: pass def start_crawling(self): service = Service(EdgeChromiumDriverManager().install()) self.browser = Edge(service=service, options=options) while True: # ~爬取代码~ pass
Tkinter入口代码
def entry_point(): global processor try: processor.starter() except AssertionError: processor = Wrapper() processor.starter() def force_close_crawling(): global processor try: processor.exit() except AttributeError: pass if __name__ == "__main__": app = tkinter.Tk() processor = Wrapper() btn1 = tkinter.Button(app, text='run', command=entry_point) btn2 = tkinter.Button(app, text='terminate', command=force_close_crawling) btn1.pack() btn2.pack() app.mainloop()
原因
调用Process.terminate()是强制终止进程,这种情况下子进程的析构方法__del__不会被Python解释器正常执行,所以浏览器进程无法被关闭。
解决方案
使用进程间通信让爬虫进程优雅退出,触发__del__方法自动关闭浏览器:
- 在
Wrapper类中添加一个multiprocessing.Event对象作为退出信号 - 将信号传递给内部的
Crawler类 - 修改
start_crawling的循环逻辑,每次循环检查退出信号,收到信号就终止循环 - 修改
exit方法,不再强制杀进程,而是设置退出信号并等待进程结束
修改后的代码
Wrapper类修改版
from multiprocessing import Process, Event # 其他导入保持不变 class Wrapper(Process): def __init__(self): self.exit_event = Event() # 退出信号事件 self.crawler = self.Crawler(self.exit_event) # 传递信号给爬虫 super().__init__(target=self.crawler.start_crawling) def starter(self): # 启动前重置退出信号,避免重复启动时信号处于触发状态 self.exit_event.clear() self.start() def exit(self): self.exit_event.set() # 发送退出信号 self.join() # 等待进程优雅结束 class Crawler: def __init__(self, exit_event: Event): self.exit_event = exit_event def __del__(self) -> None: try: self.browser.quit() print("浏览器已关闭") except Exception as e: print(f"关闭浏览器时出错: {e}") def start_crawling(self): service = Service(EdgeChromiumDriverManager().install()) self.browser = Edge(service=service, options=options) while True: # 检查退出信号,若触发则终止循环 if self.exit_event.is_set(): break # ~爬取代码~ # 可在爬取逻辑的适当位置增加信号检查,比如每次请求后 pass
Tkinter代码无需修改
原Tkinter入口代码保持不变即可,force_close_crawling调用的processor.exit()已改为优雅退出逻辑。
说明
- 用
Event实现进程间通信,主进程通过设置事件通知子进程退出 - 子进程在循环中检查事件,收到信号后跳出循环,进程结束时自动触发
__del__方法关闭浏览器 - 完全避免了直接调用
self.browser.quit(),符合需求
内容的提问来源于stack exchange,提问作者Loire
相关产品推荐
相关产品推荐

