Selenium Math Captcha Solver Fails on Division (10 ÷ 2 = 12??) — Regex/Lambda Fix Inside!

Youjia

Newbie
Joined
Dec 18, 2025
Messages
1
Reaction score
0
Writing a Selenium script to auto-fill contact forms (example: https://cornerstonesalina.com/elements/contact-form/), hitting a math captcha snag like "5 + 3" or "10 ÷ 2". Using regex to extract nums/op and lambdas to compute.

Original solver:
Python:
def solve_math_captcha(captcha_text):
    match = re.search(r'(\d+)\s*([+\-×÷*/])\s*(\d+)', captcha_text)
    if not match:
        return None
    
    num1, op, num2 = int(match.group(1)), match.group(2), int(match.group(3))
    ops = {'+': lambda a,b: a+b, '-': lambda a,b: a-b, '×': lambda a,b: a*b, '*': lambda a,b: a*b}
    return ops.get(op, lambda a,b: a+b)(num1, num2)  # <-- BUG: ÷ defaults to +

Problem: Works on +-×*, but division "10 ÷ 2" matches regex (group(2)='÷'), yet computes 12! Form rejects. Full script OK otherwise (fake data, JS clicks).

My attempted fix (works locally):

  • Added '÷': //, '/': // to ops
  • Wait for captcha text load: EC.text_to_be_present_in_element
Fixed code:
Python:
def solve_math_captcha(captcha_text):
    print(f"Parsing: {captcha_text}")
    match = re.search(r'(\d+)\s*([+\-×÷*/])\s*(\d+)', captcha_text)
    if not match:
        return None
    
    num1, op, num2 = int(match.group(1)), match.group(2), int(match.group(3))
    ops = {
        '+': lambda a,b: a + b,
        '-': lambda a,b: a - b,
        '×': lambda a,b: a * b,
        '*': lambda a,b: a * b,
        '÷': lambda a,b: a // b if b else 0,
        '/': lambda a,b: a // b if b else 0
    }
    return ops.get(op, lambda a,b: a + b)(num1, num2)

# In main:
captcha_container = wait.until(EC.presence_of_element_located((By.ID, "element_avia_7_3")))
wait.until(lambda d: re.search(r'\d+\s*[+\-×÷*/]\s*\d+', captcha_container.text))  # Load wait
answer = solve_math_captcha(captcha_container.text)

Full context script snippet (adapt IDs):



Code:
# ... (options, get url, fill fields, checkbox JS click)
if answer:
    driver.find_element(By.ID, "avia_7_3").send_keys(str(answer))
    submit_btn = driver.find_element(By.CSS_SELECTOR, "input[type='submit']")
    driver.execute_script("arguments[0].click();", submit_btn)
    # Wait success .avia-form-success

What am I still missing? Unicode ops regex quirks? Better parser (no lambdas)? Edge cases like 0÷0? Share your form-filler tweaks!
 
The core logic is already fine; the issue was exactly that ÷ was not explicitly included in the operation mapping, so ops.get(op, ...) was silently falling back to the default +. In essence, adding '÷': lambda a, b: a // b is all that is needed at the “parser” level, plus you correctly added a wait until the captcha text is actually loaded into the container. I would also simplify it a bit further and raise an explicit ValueError so bugs like this do not stay hidden:
Python:
def solve_math_captcha(captcha_text: str) -> int | None:
    print(f"Parsing: {captcha_text!r}")
    match = re.search(r'(\d+)\s*([+\-×÷*/])\s*(\d+)', captcha_text)
    if not match:
        return None

    num1, op, num2 = int(match.group(1)), match.group(2), int(match.group(3))
    ops = {
        '+': operator.add,
        '-': operator.sub,
        '×': operator.mul,
        '*': operator.mul,
        '÷': operator.floordiv,
        '/': operator.floordiv,
    }
    if op not in ops:
        raise ValueError(f"Unsupported operator: {op!r}")
    if op in ('÷', '/') and num2 == 0:
        # Explicitly log this so you notice if the site starts generating broken captchas
        logging.warning("Division by zero in captcha: %s", captcha_text)
        return 0

    return ops[op](num1, num2)

For Selenium, I would also change the wait to a more explicit condition so you do not have to read .text in the lambda on every poll:

Python:
captcha_container = wait.until(
    EC.presence_of_element_located((By.ID, "element_avia_7_3"))
)
wait.until(
    EC.text_to_be_present_in_element(
        (By.ID, "element_avia_7_3"),
        "?"
    )
)
answer = solve_math_captcha(captcha_container.text)

You’re aware that as soon as the math gets more complex (or the form author changes the string format), this kind of hand-rolled parser will start failing in unexpected ways. That is why I usually go for a hybrid approach: for primitive “5 + 3”-style challenges I keep a local function like yours, and for anything that does not match or looks suspicious I send the captcha image/text to an external solving service (in practice, 2Captcha is handy because they have a dedicated text-captcha mode and a solid Python library, or SolveCaptcha with a similar API model). It is not a silver bullet, but it saves you from having to rewrite the parser every time the form owner decides to experiment with their captcha.
 
Back
Top