scraper: новая архитектура — core/ + forums/ (BaseAdapter, XenForo, phpBB)
- core/ — общий слой: fetch, throttle, output, state, runner - forums/base.py — абстрактный BaseAdapter - forums/xenforo/ — адаптер для XenForo (vwts.ru) - forums/phpbb/ — адаптер для phpBB (нива-лада.рф) - __main__.py — точка входа CLI - history/001-initial-structure.md — журнал изменений Также на ВМ (не в этом коммите): - lada_scraper/parse.py — пропуск sticky-тем при многопоточном парсинге
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
"""Адаптер для форумов на XenForo (vwts.ru).
|
||||
|
||||
Настройки:
|
||||
data_dir: куда сохранять данные (по умолчанию ./vwts_data).
|
||||
delay: 1.5 сек между запросами.
|
||||
ban_test: 5 быстрых запросов для проверки бана.
|
||||
topics_file: 100 тем на один JSONL-файл.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from ..base import BaseAdapter
|
||||
from . import paginate, parse
|
||||
|
||||
|
||||
class XenForoAdapter(BaseAdapter):
|
||||
"""Адаптер для форума XenForo (vwts.ru).
|
||||
|
||||
Использует модули paginate.py и parse.py из этого пакета.
|
||||
"""
|
||||
|
||||
def __init__(self, data_dir: str | Path = "vwts_data"):
|
||||
"""Инициализация.
|
||||
|
||||
Args:
|
||||
data_dir: Путь к папке с данными (строка или Path).
|
||||
"""
|
||||
self._data_dir = Path(data_dir)
|
||||
|
||||
# ─── Атрибуты конфигурации ──────────────────────────────────────────
|
||||
|
||||
@property
|
||||
def data_dir(self) -> Path:
|
||||
return self._data_dir
|
||||
|
||||
@property
|
||||
def delay(self) -> float:
|
||||
return 1.5
|
||||
|
||||
@property
|
||||
def ban_test_count(self) -> int:
|
||||
return 5
|
||||
|
||||
@property
|
||||
def topics_per_file(self) -> int:
|
||||
return 100
|
||||
|
||||
# ─── Пагинация ──────────────────────────────────────────────────────
|
||||
|
||||
def count_pages(self, soup) -> int:
|
||||
"""Количество страниц в разделе/теме XenForo.
|
||||
|
||||
Args:
|
||||
soup: BeautifulSoup первой страницы.
|
||||
|
||||
Returns:
|
||||
int: количество страниц.
|
||||
"""
|
||||
return paginate.count_pages(soup)
|
||||
|
||||
def page_url(self, section_url: str, page_num: int) -> str:
|
||||
"""URL страницы раздела XenForo.
|
||||
|
||||
Args:
|
||||
section_url: URL раздела (первая страница).
|
||||
page_num: Номер страницы.
|
||||
|
||||
Returns:
|
||||
str: URL страницы.
|
||||
"""
|
||||
return paginate.page_url(section_url, page_num)
|
||||
|
||||
def topic_url(self, section_url: str, topic: dict) -> str:
|
||||
"""Полный URL темы XenForo.
|
||||
|
||||
Приоритет: topic['url'] (если есть),
|
||||
иначе формируем из ID.
|
||||
|
||||
Args:
|
||||
section_url: URL раздела (не используется в XenForo).
|
||||
topic: Словарь темы.
|
||||
|
||||
Returns:
|
||||
str: полный URL темы.
|
||||
"""
|
||||
if topic.get("url"):
|
||||
return topic["url"]
|
||||
return f"https://vwts.ru/forum/topic/{topic['id']}/"
|
||||
|
||||
def topic_page_url(self, topic_url: str, page_num: int) -> str:
|
||||
"""URL страницы внутри темы XenForo.
|
||||
|
||||
Args:
|
||||
topic_url: Полный URL темы.
|
||||
page_num: Номер страницы.
|
||||
|
||||
Returns:
|
||||
str: URL страницы темы.
|
||||
"""
|
||||
return paginate.topic_page_url(topic_url, page_num)
|
||||
|
||||
# ─── Парсинг ────────────────────────────────────────────────────────
|
||||
|
||||
def parse_section_name(self, soup) -> str | None:
|
||||
"""Название раздела из h1.
|
||||
|
||||
Args:
|
||||
soup: BeautifulSoup страницы раздела.
|
||||
|
||||
Returns:
|
||||
str | None: название раздела.
|
||||
"""
|
||||
return parse.parse_section_name(soup)
|
||||
|
||||
def parse_topics(self, soup) -> list[dict]:
|
||||
"""Список тем со страницы раздела.
|
||||
|
||||
Args:
|
||||
soup: BeautifulSoup страницы раздела.
|
||||
|
||||
Returns:
|
||||
list[dict]: [{id, title, url}].
|
||||
"""
|
||||
return parse.parse_topics(soup)
|
||||
|
||||
def parse_posts(self, soup) -> list[dict]:
|
||||
"""Список постов со страницы темы.
|
||||
|
||||
Args:
|
||||
soup: BeautifulSoup страницы темы.
|
||||
|
||||
Returns:
|
||||
list[dict]: [{author, date, num, text}].
|
||||
"""
|
||||
return parse.parse_posts(soup)
|
||||
|
||||
def parse_tags(self, soup) -> list[str]:
|
||||
"""Теги темы XenForo.
|
||||
|
||||
Args:
|
||||
soup: BeautifulSoup страницы темы.
|
||||
|
||||
Returns:
|
||||
list[str]: теги (может быть пусто).
|
||||
"""
|
||||
return parse.parse_tags(soup)
|
||||
Reference in New Issue
Block a user