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,200 @@
|
||||
"""Точка входа CLI для scraper.
|
||||
|
||||
Использование:
|
||||
python -m scraper xenforo URL [--workers N] [--no-delay]
|
||||
python -m scraper phpbb ID [--base URL] [--workers N]
|
||||
|
||||
Примеры:
|
||||
python -m scraper xenforo https://vwts.ru/forum/vag/benzinovye-dvigateli/
|
||||
python -m scraper xenforo URL --workers 8
|
||||
python -m scraper phpbb 22
|
||||
python -m scraper phpbb 22 --base https://нива-лада.рф/n/
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
# ─── Инициализация логирования ──────────────────────────────────────────
|
||||
# Должна быть первой, чтобы все логи писались сразу
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||
datefmt="%H:%M:%S",
|
||||
handlers=[
|
||||
logging.StreamHandler(sys.stdout),
|
||||
],
|
||||
)
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _print_usage():
|
||||
"""Вывести справку по использованию."""
|
||||
print("Использование:")
|
||||
print(" python -m scraper xenforo URL [опции]")
|
||||
print(" python -m scraper phpbb FORUM_ID [опции]")
|
||||
print()
|
||||
print("XenForo (vwts.ru):")
|
||||
print(" python -m scraper xenforo https://vwts.ru/forum/vag/.../")
|
||||
print()
|
||||
print("phpBB (нива-лада.рф):")
|
||||
print(" python -m scraper phpbb 22")
|
||||
print(" python -m scraper phpbb 22 --base https://нива-лада.рф/n/")
|
||||
print()
|
||||
print("Опции:")
|
||||
print(" --workers N количество потоков (по умолчанию 4)")
|
||||
print(" --no-delay без координации задержек")
|
||||
print(" --no-ban-test не проверять бан")
|
||||
print(" --data-dir DIR папка для данных (по умолчанию vwts_data / lada_data)")
|
||||
print(" --base URL базовый URL форума (для phpBB)")
|
||||
|
||||
|
||||
def main():
|
||||
"""Точка входа: разбор аргументов → запуск нужного адаптера."""
|
||||
args = sys.argv[1:]
|
||||
|
||||
if not args or args[0] in ("-h", "--help"):
|
||||
_print_usage()
|
||||
sys.exit(0)
|
||||
|
||||
# ── Определяем движок ───────────────────────────────────────────────
|
||||
engine = args.pop(0)
|
||||
|
||||
if engine == "xenforo":
|
||||
_run_xenforo(args)
|
||||
elif engine == "phpbb":
|
||||
_run_phpbb(args)
|
||||
else:
|
||||
print(f"❌ Неизвестный движок: {engine}")
|
||||
print(" Доступно: xenforo, phpbb")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# XenForo
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
def _run_xenforo(args: list[str]):
|
||||
"""Запуск парсинга для XenForo (vwts.ru).
|
||||
|
||||
Args:
|
||||
args: список аргументов после "xenforo".
|
||||
"""
|
||||
if not args or args[0].startswith("-"):
|
||||
print("❌ Укажи URL раздела. Пример:")
|
||||
print(" python -m scraper xenforo https://vwts.ru/forum/vag/.../")
|
||||
sys.exit(1)
|
||||
|
||||
section_url = args.pop(0).rstrip("/") + "/"
|
||||
|
||||
# Парсим опции
|
||||
workers = 4
|
||||
no_delay = False
|
||||
ban_test = True
|
||||
data_dir = "vwts_data"
|
||||
|
||||
i = 0
|
||||
while i < len(args):
|
||||
if args[i] == "--workers" and i + 1 < len(args):
|
||||
workers = int(args[i + 1])
|
||||
i += 2
|
||||
elif args[i] == "--no-delay":
|
||||
no_delay = True
|
||||
i += 1
|
||||
elif args[i] == "--no-ban-test":
|
||||
ban_test = False
|
||||
i += 1
|
||||
elif args[i] == "--data-dir" and i + 1 < len(args):
|
||||
data_dir = args[i + 1]
|
||||
i += 2
|
||||
else:
|
||||
i += 1
|
||||
|
||||
# Импортируем адаптер
|
||||
from .forums.xenforo.adapter import XenForoAdapter
|
||||
from .core.runner import run
|
||||
|
||||
adapter = XenForoAdapter(data_dir=data_dir)
|
||||
|
||||
log.info("=" * 60)
|
||||
log.info("🚀 scraper — XenForo: %s", section_url.split("/")[-2])
|
||||
log.info("📂 %s", Path(data_dir) / section_url.split("/")[-2] / "part_*.jsonl")
|
||||
log.info("🛑 Ctrl+C = пауза | Повторить команду = продолжить")
|
||||
log.info("=" * 60)
|
||||
|
||||
try:
|
||||
run(adapter, section_url,
|
||||
workers=workers, no_delay=no_delay, ban_test=ban_test)
|
||||
except KeyboardInterrupt:
|
||||
log.info("\n🛑 ПАУЗА. Продолжить: та же команда.")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# phpBB
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
def _run_phpbb(args: list[str]):
|
||||
"""Запуск парсинга для phpBB (нива-лада.рф).
|
||||
|
||||
Args:
|
||||
args: список аргументов после "phpbb".
|
||||
"""
|
||||
if not args or args[0].startswith("-"):
|
||||
print("❌ Укажи ID раздела. Пример:")
|
||||
print(" python -m scraper phpbb 22")
|
||||
sys.exit(1)
|
||||
|
||||
forum_id = args.pop(0)
|
||||
|
||||
# Парсим опции
|
||||
workers = 4
|
||||
no_delay = False
|
||||
ban_test = True
|
||||
data_dir = "lada_data"
|
||||
forum_base = "https://нива-лада.рф/n/"
|
||||
|
||||
i = 0
|
||||
while i < len(args):
|
||||
if args[i] == "--workers" and i + 1 < len(args):
|
||||
workers = int(args[i + 1])
|
||||
i += 2
|
||||
elif args[i] == "--no-delay":
|
||||
no_delay = True
|
||||
i += 1
|
||||
elif args[i] == "--no-ban-test":
|
||||
ban_test = False
|
||||
i += 1
|
||||
elif args[i] == "--data-dir" and i + 1 < len(args):
|
||||
data_dir = args[i + 1]
|
||||
i += 2
|
||||
elif args[i] == "--base" and i + 1 < len(args):
|
||||
forum_base = args[i + 1]
|
||||
i += 2
|
||||
else:
|
||||
i += 1
|
||||
|
||||
# Формируем URL раздела
|
||||
section_url = f"{forum_base}viewforum.php?f={forum_id}"
|
||||
|
||||
from .forums.phpbb.adapter import PhpBBAdapter
|
||||
from .core.runner import run
|
||||
|
||||
adapter = PhpBBAdapter(data_dir=data_dir, forum_base=forum_base)
|
||||
|
||||
log.info("=" * 60)
|
||||
log.info("🚀 scraper — phpBB: f=%s", forum_id)
|
||||
log.info("📂 %s", Path(data_dir) / f"f{forum_id}" / "part_*.jsonl")
|
||||
log.info("🛑 Ctrl+C = пауза | Повторить команду = продолжить")
|
||||
log.info("=" * 60)
|
||||
|
||||
try:
|
||||
run(adapter, section_url, section_slug=f"f{forum_id}",
|
||||
workers=workers, no_delay=no_delay, ban_test=ban_test)
|
||||
except KeyboardInterrupt:
|
||||
log.info("\n🛑 ПАУЗА. Продолжить: та же команда.")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user