Fetch EUR-Lex HTML and parse to markdown. Retries on HTTP 202 (Accepted) which EUR-Lex returns when generating large documents asynchronously.
(html_url: str)
| 468 | |
| 469 | |
| 470 | def fetch_and_parse(html_url: str) -> Optional[tuple[str, str]]: |
| 471 | """Fetch EUR-Lex HTML and parse to markdown. |
| 472 | |
| 473 | Retries on HTTP 202 (Accepted) which EUR-Lex returns when generating |
| 474 | large documents asynchronously. |
| 475 | """ |
| 476 | session = requests.Session() |
| 477 | session.headers.update(HEADERS) |
| 478 | |
| 479 | for attempt in range(1, MAX_RETRIES + 1): |
| 480 | try: |
| 481 | resp = session.get(html_url, timeout=120, allow_redirects=True) |
| 482 | except Exception as e: |
| 483 | logger.error(f" Failed to fetch {html_url}: {e}") |
| 484 | return None |
| 485 | |
| 486 | if resp.status_code == 202: |
| 487 | if attempt < MAX_RETRIES: |
| 488 | logger.info( |
| 489 | f" HTTP 202 Accepted (attempt {attempt}/{MAX_RETRIES}), " |
| 490 | f"retrying in {RETRY_DELAY}s...") |
| 491 | time.sleep(RETRY_DELAY) |
| 492 | continue |
| 493 | else: |
| 494 | logger.warning( |
| 495 | f" HTTP 202 after {MAX_RETRIES} attempts, giving up") |
| 496 | return None |
| 497 | |
| 498 | if resp.status_code != 200: |
| 499 | logger.error(f" HTTP {resp.status_code} for {html_url}") |
| 500 | return None |
| 501 | |
| 502 | break |
| 503 | |
| 504 | content_type = resp.headers.get("content-type", "").lower() |
| 505 | if "html" not in content_type: |
| 506 | logger.error(f" Unexpected content type: {content_type}") |
| 507 | return None |
| 508 | |
| 509 | html = resp.text |
| 510 | logger.info(f" Downloaded {len(html):,} bytes") |
| 511 | |
| 512 | title, markdown = parse_eurlex_html(html) |
| 513 | |
| 514 | if not markdown or len(markdown) < 100: |
| 515 | logger.warning(f" Extracted text too short ({len(markdown)} chars)") |
| 516 | return None |
| 517 | |
| 518 | return (title, markdown) |
| 519 | |
| 520 | |
| 521 | # --------------------------------------------------------------------------- |
no test coverage detected