from selenium import webdriver
from time import sleep
from tenacity import TryAgain, retry, wait_random
def get_element(locator):
'''
這個函數用來判斷是否存在某個元素
'''
try:
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
return WebDriverWait(driver, 5, 0.5).until(EC.visibility_of_element_located(locator))
except:
return False
driver = webdriver.Chrome()
driver.implicitly_wait(5)
driver.get('http://114.116.2.138:8090/forum.php') # 這個地址你可以存取,是我搭建在雲端的一個容器,開源論壇
driver.find_element('css selector', '#ls_username').send_keys('admin')
driver.find_element('css selector', '#ls_password').send_keys('123456')
driver.find_element('css selector', 'button.pn.vm').click()
@retry(wait=wait_random(min=3, max=5)) # 等待一個時間區間,3-5s,注意這個時間的下限建議>hint存在的最大時間
def code_login():
'''
這個函數是用來反覆驗證碼登入的
'''
# 驗證碼元素
ele_code = driver.find_element('css selector', '[id^=vseccode_cS]>img')
import ddddocr
ocr = ddddocr.DdddOcr()
code_text = ocr.classification(ele_code.screenshot_as_png)
# 當失敗的時候尤其要注意: 清空已輸入的內容
driver.find_element('css selector', 'input[id^=seccodeverify_cS]').clear()
test_data = ['1','a','','2','b']
from random import choice
choice_data = choice(test_data)
# 輸入識別後的資料+隨機字元
driver.find_element('css selector', 'input[id^=seccodeverify_cS]').send_keys(code_text + choice_data)
# 點選登入
driver.find_element('css selector', "[name='loginsubmit']>strong").click()
# 注意! 你可以去操作網頁,點選登入如果失敗會彈出提示 "抱歉,驗證碼填寫錯誤"
hint_locator = 'css selector', '.pc_inner>i'
if get_element(hint_locator): # 如果出現這個元素,就...
# 點選下驗證碼,重新整理一下
driver.find_element('css selector', '[id^=vseccode_cS]>img').click()
# 丟擲異常,重跑
raise TryAgain
code_login()
from tenacity import retry
@retry
def retry_without_anything():
print('retry...')
raise Exception # 不加這個可不會重試
retry_without_anything()
from tenacity import retry, stop_after_attempt
@retry(stop=stop_after_attempt(3))
def retry_times():
print(f'retry... times')
raise Exception
retry_times()
retry... times
retry... times
retry... times
Traceback (most recent call last):
File "D:\Python39\lib\site-packages\tenacity\__init__.py", line 407, in __call__
result = fn(*args, **kwargs)
File "demo_retry.py", line 20, in retry_times
raise Exception
Exception
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "demo_retry.py", line 23, in <module>
retry_times()
File "D:\Python39\lib\site-packages\tenacity\__init__.py", line 324, in wrapped_f
return self(f, *args, **kw)
File "D:\Python39\lib\site-packages\tenacity\__init__.py", line 404, in __call__
do = self.iter(retry_state=retry_state)
File "D:\Python39\lib\site-packages\tenacity\__init__.py", line 361, in iter
raise retry_exc from fut.exception()
tenacity.RetryError: RetryError[<Future at 0x16628b45460 state=finished raised Exception>]
from tenacity import retry, stop_after_delay
import arrow
from time import sleep
@retry(stop=stop_after_delay(10))
def retry_times():
print(arrow.now().format('YYYY-MM-DD HH:mm:ss'))
sleep(1)
raise Exception
retry_times()
2023-02-21 17:32:01
2023-02-21 17:32:02
2023-02-21 17:32:03
2023-02-21 17:32:04
2023-02-21 17:32:05
2023-02-21 17:32:06
2023-02-21 17:32:07
2023-02-21 17:32:08
2023-02-21 17:32:10
2023-02-21 17:32:11
# 最後丟擲異常
@retry(stop=(stop_after_delay(10) | stop_after_attempt(5)))
def retry_multi_conditions():
print("10秒後或者5次重試後停止重試")
raise Exception
之前的重試是無縫銜接的
你可以讓重試之間有延遲
@retry(wait=wait_fixed(2))
def retry_wait_time1():
print(arrow.now().format('YYYY-MM-DD HH:mm:ss'))
print("每次重試前等待2秒")
raise Exception
retry_wait_time1()
當然上面的仍然是個無限重試
你可以組合前面的停止
@retry(wait=wait_fixed(2),stop=stop_after_attempt(3))
重試等待間隔可以設定一個區間(最大最小值)
from tenacity import retry, stop_after_attempt, wait_random
import arrow
@retry(wait=wait_random(min=1,max=4),stop=stop_after_attempt(3))
def retry_wait_time1():
print(arrow.now().format('YYYY-MM-DD HH:mm:ss'))
print("每次重試前等待1~4秒區間")
raise Exception
retry_wait_time1()
這是最重要的了
重試的條件一:引發特定或一般異常的重試
from tenacity import retry, retry_if_exception_type, stop_after_attempt
@retry(retry=retry_if_exception_type(ZeroDivisionError),
stop= stop_after_attempt(5))
def retry_if_exception():
print('retry...')
print(10/0) # 現在觸發的就是ZeroDivisionError,如果把此處改為 print(a),則遇到的是NameError,那就不會重試
raise Exception
retry_if_exception()
引發 TryAgain 異常隨時顯式重試
比如這樣
@retry
def try_if_condition():
result = 23
if result == 23:
raise TryAgain
上面的demo中就用到了這個
官網還有很多的例子,相對高階一些,如有興趣可以自行前往或者搜尋之