一、表達式解析
這里假設已經識別出來表達式,如何識別驗證碼圖片里的表達式,放在下面講。涉及到的正則表達式的解析放在本篇文章最后面。
import re
# 表達式解析(支持小數的 +-*/ 和中文運算符)
def parse_math_expression(text):# 替換中文運算符為英文text = text.replace('加', '+').replace('減', '-').replace('乘', '*').replace('除', '/')text = text.replace('x', '*').replace('X', '*').replace('÷', '/')# 正則表達式提取表達式中的數字和運算符# match = re.search(r'(\d+)\s*([+\-*/]|加|減|乘|除)\s*(\d+)', text)match = re.search(r'(\d+(?:\.\d+)?)\s*([+\-*/]|加|減|乘|除)\s*(\d+(?:\.\d+)?)', text) # 可匹配小數if not match:return Nonenum1, operator, num2 = match.groups()try:num1 = float(num1)num2 = float(num2)# 計算結果if operator == '+':result = num1 + num2elif operator == '-':result = num1 - num2elif operator == '*':result = num1 * num2elif operator == '/':result = num1 / num2else:return None# 要么返回整數,要么返回最多兩位小數,round()四舍六入五平分,如果五平分的進位不對,刷新頁面重新識別吧,懶得處理了return int(result) if result.is_integer() else round(result, 2)except:return None# 調試用
ocr_result = "9.1加3.2等于"result = parse_math_expression(ocr_result)
if result is not None:print(f"計算結果: {result}")else:print("無法解析表達式")
二、配合selenium
#!/usr/bin/env python
# encoding: utf-8from selenium import webdriver
from selenium.webdriver.common.by import By
import time
import ddddocr
import re# 表達式解析(支持 +-*/ 和中文運算符)
def parse_math_expression(text):# 替換中文運算符為英文text = text.replace('加', '+').replace('減', '-').replace('乘', '*').replace('除', '/')text = text.replace('x', '*').replace('X', '*').replace('÷', '/')# 正則表達式提取表達式中的數字和運算符# match 對象包含匹配成功的信息,若未匹配到則返回 None# match = re.search(r'(\d+)\s*([+\-*/]|加|減|乘|除)\s*(\d+)', text) #匹配整數match = re.search(r'(\d+(?:\.\d+)?)\s*([+\-*/]|加|減|乘|除)\s*(\d+(?:\.\d+)?)', text) # 可匹配小數if not match:return Nonenum1, operator, num2 = match.groups() #groups() 僅包含捕獲組,索引從 0 開始,如('3.14', '乘', '2.71')try:num1 = float(num1)num2 = float(num2)# 計算結果if operator == '+':result = num1 + num2elif operator == '-':result = num1 - num2elif operator == '*':result = num1 * num2elif operator == '/':result = num1 / num2else:return None# 要么返回整數,要么返回最多兩位小數,round()四舍六入五平分,如果五平分的進位不對,刷新頁面重新識別吧return int(result) if result.is_integer() else round(result, 2)except:return None# 配置瀏覽器
options = webdriver.ChromeOptions()
driver = webdriver.Chrome(options=options)
driver.implicitly_wait(5)# 打開驗證碼頁面
driver.get('http://127.0.0.1/1.html')
time.sleep(2) # 等待頁面加載# 獲取驗證碼圖片
captcha_element = driver.find_element(By.ID, 'captcha')
captcha_png = captcha_element.screenshot_as_png# 初始化 OCR 引擎(支持中文運算符)
ocr = ddddocr.DdddOcr(show_ad=False)
# OCR 識別
ocr_result = ocr.classification(captcha_png)
print(f"OCR 識別結果: {ocr_result}")# 計算表達式結果
# ocr_result = '9.14加3.15等于' # debug使用
result = parse_math_expression(ocr_result)if result is not None:print(f"計算結果: {result}")# 自動填寫結果(假設輸入框 ID 為 'captcha_input')input_element = driver.find_element(By.ID, 'captcha_input')input_element.send_keys(str(result))# 提交表單(假設按鈕 ID 為 'submit')submit_button = driver.find_element(By.ID, 'submit')submit_button.click()print("已提交表單")
else:print("無法解析表達式")# 等待用戶查看結果后關閉瀏覽器
time.sleep(5)
driver.quit()
三、引申:正則表達式搭配group()
group() 是 Python 正則表達式模塊 re 中用于提取匹配結果的核心方法,必須與正則表達式的匹配對象(Match 對象)搭配使用。
1. group()基本定義
- match.group(n):返回正則表達式中第 n 個捕獲組的匹配內容。
- match.group(0):返回整個匹配的字符串(即正則表達式匹配到的完整文本)。
import retext = "今天是2023-06-07"
regex = r'(\d{4})-(\d{2})-(\d{2})' # 三個捕獲組:年、月、日
match = re.search(regex, text)if match:print(match.group(0)) # 整個匹配: "2023-06-07"print(match.group(1)) # 第一個捕獲組: "2023"print(match.group(2)) # 第二個捕獲組: "06"print(match.group(3)) # 第三個捕獲組: "07"
注意:match.groups()跟match.group()是不一樣的
,groups()返回內容僅包含捕獲組(即括號()中定義的內容),不包含整個匹配結果。上面的算術代碼用的是groups()。
2. 例子中的正則表達式解析
(1)(\d+(?:.\d+)?) 匹配整數或小數
部分 | 含義 | 示例匹配 |
---|---|---|
\. | 匹配小數點(. 需要轉義) | . |
\d+ | 匹配 1 個或多個數字 | 14、5 |
(?:...) | 非捕獲組(不創建分組) | 僅用于分組,不保存結果 |
? | 前面的內容可選(0 次或 1 次) | .14、空字符串 |
(2)空白字符 \s*
- \s:匹配任意空白字符(空格、制表符、換行等)
- *:匹配 0 次或多次(即可有可無)
(3)運算符部分 ([+-*/]|加|減|乘|除)
- 邏輯或 |:分隔多個可選模式,匹配其中任意一個
- 分組 ():捕獲匹配的內容,便于后續提取 [ ] :字符組,匹配其中任意一個字符
- 需要轉義(-),否則表示范圍(如 [0-9])
- +、* 理論上需要轉義,但多數引擎允許不轉義
四、用到的驗證碼識別庫ddddocr
DdddOcr 帶帶弟弟OCR通用驗證碼離線本地識別SDK免費開源版
(鏈接包含各種強大用法的使用文檔,感興趣可看,如下圖)