feat(parsers): CyberLeninka full_text из OCR-фрагментов поиска (без доп. запросов)

Поисковый API КиберЛенинки отдаёт поле "ocr" — список OCR-фрагментов текста
статьи (начало + фрагмент с ключевыми словами), прямо в ответе search. Раньше
transform() игнорировал его (full_text всегда None), и Winnowing-отпечатки (L1)
считались только по короткой annotation (~150 симв.) либо не считались вовсе,
если аннотации не было.

Теперь ocr (список) склеивается, чистится (<b>/сущности) и идёт в 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 <noreply@anthropic.com>
This commit is contained in:
jze9
2026-08-24 14:37:12 +05:00
parent 92e23b5209
commit cc87a11f07
2 changed files with 19 additions and 1 deletions

View File

@@ -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]:

View File

@@ -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=["Первый <b>фрагмент</b> статьи.", "Второй &amp; фрагмент."])
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