當我們使用 Selenium 通過 Chromedriver 啟動 Chrome 瀏覽網頁時,可能會由於某些異常情況導致程序崩潰,但 Chromedriver 進程不會退出。
例如,我們編寫一段顯然有問題的代碼:
from selenium.webdriver import Chrome
driver = Chrome('./chromedriver')
driver.get('https://www.kingname.info')
1 + 'a' # 這一行代碼必定導致程序崩潰代碼報錯以後,彈出的 Chrome 窗口不會自動關閉。並且 chromedriver 的進程也不會自動結束,如下圖所示:
這就會導致系統中出現越來越多的 chromedriver 進程,從而佔用大量的內存。
為了防止這種情況,我們必須想辦法,在任何情況下都需要保證退出 chromedriver。
你可能會使用一個超大型的 try ... except...把所有與 selenium 相關的代碼都包起來:
from selenium.webdriver import Chrome
driver = Chrome('./chromedriver')
try:
driver.get('https://www.kingname.info')
#第2行
#第3行
#第4行
#。。。
#第 n 行
except Exception:
driver.quit()當然你也可以把具體的操作步驟放在函數裡面,然後 try...except... 把函數包住。但本質上是一樣的。
但這種超大型的 try...except...一是會導致程序速度減慢,二是程序出現了其他異常的時候,真正的報錯信息無法正常列印出來:
>>> a = {}
>>> try:
>>> a['k']
>>> except Exception as e:
>>> print(e)當你看到這個沒頭沒尾的'k',你不知道是哪一行有問題,也不知道具體有什麼問題。
那麼,我們有沒有辦法,既不使用 try ... except ...,但是又能在程序崩潰的時候自動退出 chromedriver 呢?
這個時候我們就可以使用上下文管理器。
我們先來包裝一下 Selenium,實現一個帶有上下文管理器的類。創建一個SafeDriver.py文件:
from selenium.webdriver import Chrome
class SafeDriver:
def __init__(self):
self.driver = Chrome('./chromedriver')
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if self.driver:
self.driver.quit()然後,我們在另一個程序裡面調用它:
from SafeDriver import SafeDriver
safe_driver = SafeDriver()
with safe_driver as driver:
driver.driver.get('https://www.kingname.info')
a = {}
a['k']程序進入到with safe_driver as driver的縮進裡面,我們會得到一個driver變量,它可以用來操作瀏覽器。
我們只需要在縮進裡面正常寫代碼即可。一旦由於某種原因導致縮進裡面的代碼報錯,Python 自動會進入SafeDriver類的__exit__方法中,執行裡面的代碼。在這個方法裡面,我們就可以關閉 chromedriver。從而保證只要程序異常退出,瀏覽器一定會被關閉,不會遺留進程。
運行效果如下圖所示:
報錯信息和出錯的行數都能正常列印出來了。
我們來看看如何實現一個包含上下文管理器的程序:
增加__enter__(self)方法,定義進入上下文管理器時返回的內容增加__exit__(self, exc_type, exc_val, exc_tb)方法,定義退出上下文管理器時需要執行的代碼需要注意的是,__enter__和__exit__需要成對使用,不能單獨使用其中一個。
在上面的代碼中,__enter__方法僅僅返回了self,於是,下面兩段代碼:
safe_driver = SafeDriver()
with safe_driver as instance:
pass僅僅從功能上來說,instance 變量與safe_driver變量完全一樣,都可以使用safe_driver.driver和instance.driver。
所不同的是,使用with啟用上下文管理器以後,在退出縮進的時候會執行__exit__中的內容。
為了簡便起見,我們可以使用with safe_driver.driver as driver,直接拿到對象中的self.driver屬性,這樣可以直接使用類似於driver.get('https://www.kingname.info')訪問網站,而不是instance.driver.get('https://www.kingname.info')。少敲幾次鍵盤。