From cc87a11f070c9156199980de7ac162f06163a619 Mon Sep 17 00:00:00 2001 From: jze9 Date: Mon, 24 Aug 2026 14:37:12 +0500 Subject: [PATCH] =?UTF-8?q?feat(parsers):=20CyberLeninka=20full=5Ftext=20?= =?UTF-8?q?=D0=B8=D0=B7=20OCR-=D1=84=D1=80=D0=B0=D0=B3=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=BE=D0=B2=20=D0=BF=D0=BE=D0=B8=D1=81=D0=BA=D0=B0=20(?= =?UTF-8?q?=D0=B1=D0=B5=D0=B7=20=D0=B4=D0=BE=D0=BF.=20=D0=B7=D0=B0=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D1=81=D0=BE=D0=B2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Поисковый API КиберЛенинки отдаёт поле "ocr" — список OCR-фрагментов текста статьи (начало + фрагмент с ключевыми словами), прямо в ответе search. Раньше transform() игнорировал его (full_text всегда None), и Winnowing-отпечатки (L1) считались только по короткой annotation (~150 симв.) либо не считались вовсе, если аннотации не было. Теперь ocr (список) склеивается, чистится (/сущности) и идёт в full_text — add_document уже умеет: `text = full_text or abstract`. Даёт заметно более точные L1-отпечатки для ВСЕХ будущих CyberLeninka-документов бесплатно — 0 доп. HTTP- запросов, работает и там, где annotation вообще пустая (проверено вживую). 2 новых теста (ocr→full_text, отсутствие ocr→None). Тестов в файле: 7. Co-Authored-By: Claude Opus 4.8 --- scripts/parsers/cyberleninka.py | 9 ++++++++- scripts/parsers/tests/test_cyberleninka.py | 11 +++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/scripts/parsers/cyberleninka.py b/scripts/parsers/cyberleninka.py index 10cf6b1..822ec0d 100644 --- a/scripts/parsers/cyberleninka.py +++ b/scripts/parsers/cyberleninka.py @@ -137,6 +137,13 @@ class CyberLeninkaParser(BaseParser): link = raw.get("link", "") url = f"{BASE_URL}{link}" if link.startswith("/") else link or None + # OCR-фрагменты текста статьи — приходят прямо в ответе поиска (список + # кусков, обычно начало статьи + фрагмент с ключевыми словами), без + # доп. запроса. Не полный текст всей статьи, но заметно богаче аннотации — + # используем как full_text для более точных Winnowing-отпечатков (L1). + ocr = raw.get("ocr") + full_text = _clean(" ".join(ocr)) if isinstance(ocr, list) and ocr else None + return { "source": self.source_name, "ext_id": ext_id, @@ -151,7 +158,7 @@ class CyberLeninkaParser(BaseParser): "pages": raw.get("pages") or None, "abstract": _clean(raw.get("annotation")) or None, "url": url, - "full_text": None, + "full_text": full_text, } def fetch_article_details(self, url: str) -> dict[str, Any]: diff --git a/scripts/parsers/tests/test_cyberleninka.py b/scripts/parsers/tests/test_cyberleninka.py index ade586a..09effaa 100644 --- a/scripts/parsers/tests/test_cyberleninka.py +++ b/scripts/parsers/tests/test_cyberleninka.py @@ -51,3 +51,14 @@ def test_transform_handles_string_authors(): def test_transform_empty_without_id_or_link(): assert CyberLeninkaParser().transform({"name": "x"}) == {} + + +def test_transform_uses_ocr_as_full_text(): + raw = dict(SAMPLE, ocr=["Первый фрагмент статьи.", "Второй & фрагмент."]) + t = CyberLeninkaParser().transform(raw) + assert t["full_text"] == "Первый фрагмент статьи. Второй & фрагмент." + + +def test_transform_full_text_none_without_ocr(): + assert CyberLeninkaParser().transform(SAMPLE)["full_text"] is None + assert CyberLeninkaParser().transform(dict(SAMPLE, ocr=[]))["full_text"] is None