diff --git a/моя/sales-finder/salesfinder/sitefetch.py b/моя/sales-finder/salesfinder/sitefetch.py new file mode 100644 index 00000000..db10c49c --- /dev/null +++ b/моя/sales-finder/salesfinder/sitefetch.py @@ -0,0 +1,29 @@ +"""Загрузка HTML сайта фирмы для шага «рекламируется?». Сеть флакует (таймауты, TLS-хендшейк +на Windows), поэтому ПОВТОРЯЕМ: не открылся с первого раза — не значит «не рекламируется». +Раньше не открывшийся сайт давал «не проверено», и фирма молча вылетала из списка.""" + +_UA = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " + "(KHTML, like Gecko) Chrome/124.0 Safari/537.36"} + + +def fetch_site_html(domain, get, render=None, attempts: int = 2, timeout: int = 12) -> str: + """HTML сайта: быстрый `get` (https, потом http), с повтором `attempts` раз; если так и не + вышло — `render` (xfetch-рендер, у него свой внутренний повтор). Пусто = не смогли достать. + + get / render инъектируются (тестируем без сети). Успех = статус <400 И непустой текст.""" + if not domain: + return "" + for _ in range(max(1, attempts)): + for scheme in ("https://", "http://"): + try: + r = get(scheme + domain, headers=_UA, timeout=timeout, allow_redirects=True) + if getattr(r, "status_code", 600) < 400 and getattr(r, "text", ""): + return r.text + except Exception: + pass + if render: + try: + return render(domain) or "" + except Exception: + return "" + return "" diff --git a/моя/sales-finder/tests/test_sitefetch.py b/моя/sales-finder/tests/test_sitefetch.py new file mode 100644 index 00000000..9a401e0f --- /dev/null +++ b/моя/sales-finder/tests/test_sitefetch.py @@ -0,0 +1,48 @@ +"""Загрузка HTML сайта фирмы с повтором — чтобы флак сети не выбрасывал рекламодателя +из списка (шаг «рекламируется?» лезет на сайт; не открылся → «не проверено» → фирма вылетала).""" +from salesfinder.sitefetch import fetch_site_html + + +class R: + def __init__(self, status, text): + self.status_code = status + self.text = text + + +def test_returns_text_on_success(): + got = fetch_site_html("x.ru", get=lambda u, **k: R(200, "ok")) + assert got == "ok" + + +def test_retries_transient_failure_across_attempts(): + calls = {"n": 0} + def get(u, **k): + calls["n"] += 1 + if calls["n"] < 3: # оба схемы в 1-й попытке падают + raise TimeoutError("медленно") + return R(200, "ok") + got = fetch_site_html("x.ru", get=get, attempts=2) + assert got == "ok" and calls["n"] == 3 + + +def test_falls_back_to_render_when_requests_fail(): + def get(u, **k): + raise ConnectionError() + got = fetch_site_html("x.ru", get=get, render=lambda d: "rendered", attempts=2) + assert got == "rendered" + + +def test_error_status_not_success_then_render(): + got = fetch_site_html("x.ru", get=lambda u, **k: R(500, "err"), + render=lambda d: "r") + assert got == "r" + + +def test_empty_when_all_fail_and_no_render(): + def get(u, **k): + raise ConnectionError() + assert fetch_site_html("x.ru", get=get, render=None, attempts=2) == "" + + +def test_empty_domain(): + assert fetch_site_html("", get=lambda u, **k: R(200, "x")) == "" diff --git a/моя/sales-finder/web/app.py b/моя/sales-finder/web/app.py index 70cefbc3..460ae616 100644 --- a/моя/sales-finder/web/app.py +++ b/моя/sales-finder/web/app.py @@ -64,20 +64,14 @@ def _read_secret(name): return "" def _fetch_site(domain): - """HTML сайта фирмы: быстрый requests, при блоке/пустоте — через xfetch (рендер).""" + """HTML сайта фирмы: быстрый requests с ПОВТОРОМ (флак сети не должен выбрасывать + рекламодателя), при полном провале — через xfetch (рендер).""" import requests from salesfinder.xfetch import render as xf_render - ua = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " - "(KHTML, like Gecko) Chrome/124.0 Safari/537.36"} - for scheme in ("https://", "http://"): - try: - r = requests.get(scheme + domain, headers=ua, timeout=12, allow_redirects=True) - if r.status_code < 400 and r.text: - return r.text - except requests.RequestException: - pass + from salesfinder.sitefetch import fetch_site_html key = _read_secret("xfetch_key.txt") - return xf_render("https://" + domain, key) if key else "" + render = (lambda d: xf_render("https://" + d, key)) if key else None + return fetch_site_html(domain, get=requests.get, render=render) def _fetch_site_url(url): """HTML страницы реквизитов — ТОЛЬКО быстрый requests (без xfetch-фолбэка, чтобы не тормозить: