File size: 1,677 Bytes
ebab0e2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import re
from typing import Optional, Tuple

import trafilatura
from bs4 import BeautifulSoup


def extract_content(html: str, url: Optional[str] = None) -> Tuple[str, str]:
    """Extract a title and the main readable text from a page's HTML.

    Uses trafilatura (a readability-style extractor) as the primary
    strategy, since it identifies and drops boilerplate (nav, ads, social
    widgets, footers) far more reliably than a class/id substring match.
    Falls back to a plain BeautifulSoup text dump for pages trafilatura
    can't parse at all (e.g. near-empty pages), so the API still returns
    something rather than an error.
    """
    document = trafilatura.bare_extraction(
        html,
        url=url,
        include_comments=False,
        include_tables=False,
        favor_recall=True,
        with_metadata=True,
    )

    text = (document.text or "").strip() if document else ""
    title = (document.title or "").strip() if document else ""

    if not text:
        title, text = _fallback_extract(html)

    if not title:
        title = _title_from_html(html)

    return title or "No title found", text


def _title_from_html(html: str) -> str:
    soup = BeautifulSoup(html, "lxml")
    if soup.title and soup.title.string:
        return soup.title.string.strip()
    return ""


def _fallback_extract(html: str) -> Tuple[str, str]:
    soup = BeautifulSoup(html, "lxml")
    title = _title_from_html(html)

    for tag in soup(["script", "style", "iframe", "noscript", "meta", "link", "svg"]):
        tag.decompose()

    text = soup.get_text(separator="\n", strip=True)
    text = re.sub(r"\n{3,}", "\n\n", text)
    return title, text