64 lines
2.2 KiB
Markdown
64 lines
2.2 KiB
Markdown
# HTML OTP Parsing Notes
|
|
|
|
Use this when IMAP fetch succeeds but naive regex extraction returns the wrong verification code.
|
|
|
|
## Problem pattern
|
|
Some transactional emails contain multiple `\d{6}` candidates in one message because the HTML includes:
|
|
- the visible OTP block,
|
|
- responsive/mobile duplicate markup,
|
|
- unrelated build/tracking numbers embedded in template fragments.
|
|
|
|
If you do `re.findall(r'\\b\\d{6}\\b', body)[0]`, you may grab a stale or irrelevant number.
|
|
|
|
## Preferred extraction order
|
|
1. **Subject line first** if it explicitly contains the OTP.
|
|
2. **OTP-specific markup** next:
|
|
- `<strong>123456</strong>`
|
|
- `.otp-code`
|
|
- monospace / headline blocks near words like `verification code`, `验证码`, `OTP`, `code`
|
|
3. **Generic body regex** only as fallback.
|
|
4. If multiple distinct codes remain, prefer the one from the **latest message** that appears in OTP-specific markup, not the first raw body match.
|
|
|
|
## Minimal robust pattern
|
|
```python
|
|
from email.header import decode_header
|
|
import email as em
|
|
import re
|
|
|
|
|
|
def decode_subject(msg):
|
|
parts = []
|
|
for value, enc in decode_header(msg.get('Subject', '')):
|
|
if isinstance(value, bytes):
|
|
parts.append(value.decode(enc or 'utf-8', errors='replace'))
|
|
else:
|
|
parts.append(value)
|
|
return ''.join(parts)
|
|
|
|
|
|
def extract_code(subject, body):
|
|
# 1) explicit subject code
|
|
m = re.search(r'\b(\d{4,8})\b', subject)
|
|
if m:
|
|
return m.group(1)
|
|
|
|
# 2) OTP-specific HTML / text blocks
|
|
patterns = [
|
|
r'otp-code[^>]*>\s*(\d{4,8})\s*<',
|
|
r'<strong[^>]*>\s*(\d{4,8})\s*</strong>',
|
|
r'(?:verification code|验证码|OTP|code)[^\d]{0,40}(\d{4,8})',
|
|
]
|
|
for pat in patterns:
|
|
m = re.search(pat, body, re.I | re.S)
|
|
if m:
|
|
return m.group(1)
|
|
|
|
# 3) generic fallback
|
|
codes = re.findall(r'\b(\d{4,8})\b', body)
|
|
codes = [c for c in codes if not (2020 <= int(c) <= 2035)]
|
|
return codes[0] if codes else None
|
|
```
|
|
|
|
## Session-derived example shape
|
|
Observed shape: one provider email produced several six-digit matches from a single HTML message. The visible OTP and the first regex hit did not reliably align. The safe fix was to stop trusting `codes[0]` and rank candidates by subject / OTP markup / latest message.
|