Files
naeel 5c142096de 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-тем при многопоточном парсинге
2026-06-06 11:46:57 +04:00

143 lines
4.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Парсинг HTML форума phpBB (subsilver2, нива-лада.рф).
Структуры:
Список тем в разделе (subsilver2):
<a href="./viewtopic.php?f=22&t=123" class="topictitle">Заголовок</a>
Пост в теме (табличная вёрстка subsilver2):
<tr class="row1"> ← строка: автор + дата
<td><b class="postauthor">Автор</b></td>
<td width="100%">
<div style="float: right;">
<b>Добавлено:</b> 12 авг 2025, 20:00
</div>
</td>
</tr>
<tr class="row1"> ← строка: аватар + текст
<td class="profile">...</td>
<td><div class="postbody">текст поста</div></td>
</tr>
"""
import re
import logging
from bs4 import BeautifulSoup
log = logging.getLogger(__name__)
def parse_topics(soup: BeautifulSoup) -> list[dict]:
"""Распарсить список тем со страницы раздела phpBB.
Ищем все a.topictitle → из href вытаскиваем t=ID.
Args:
soup: BeautifulSoup страницы раздела.
Returns:
list[dict]: [{id, title, url}].
url может быть None (будет сформирован адаптером).
"""
items = []
for link in soup.select("a.topictitle"):
href = link.get("href", "")
# Из href вытаскиваем t=ID
match = re.search(r"[?&]t=(\d+)", href)
if match is None:
continue
topic_id = int(match.group(1))
title = link.text.strip()
items.append({
"id": topic_id,
"title": title,
"url": None, # адаптер сформирует сам
})
return items
def parse_posts(soup: BeautifulSoup) -> list[dict]:
"""Распарсить посты со страницы темы phpBB (subsilver2).
Каждый пост — это 3 <tr> в таблице. Ищем b.postauthor
как маркер начала поста, затем:
- Тот же <tr>: дата из "Добавлено:"
- Следующий <tr>: текст из div.postbody
Args:
soup: BeautifulSoup страницы темы.
Returns:
list[dict]: [{author, date, num, text}, ...].
"""
posts = []
post_num = 0
for author_tag in soup.select("b.postauthor"):
post_num += 1
author = author_tag.text.strip()
# ── Дата: ищем "Добавлено:" в том же <tr> ───────────────────────
date = ""
header_row = author_tag.find_parent("tr")
if header_row is not None:
date_cell = header_row.select_one("td[width='100%']")
if date_cell is not None:
date_text = date_cell.get_text(" ", strip=True)
date_match = re.search(r"Добавлено:\s*(.+)", date_text)
if date_match:
date = date_match.group(1).strip()
# ── Текст: ищем div.postbody в следующем <tr> ───────────────────
text = ""
if header_row is not None:
body_row = header_row.find_next_sibling("tr")
if body_row is not None:
body_div = body_row.select_one("div.postbody")
if body_div is not None:
text = body_div.get_text("\n", strip=True)
posts.append({
"author": author,
"date": date,
"num": f"#{post_num}",
"text": text,
})
return posts
def parse_section_name(soup: BeautifulSoup) -> str | None:
"""Извлечь название раздела из заголовка phpBB.
phpBB обычно показывает название в h2 или в breadcrumbs.
Args:
soup: BeautifulSoup страницы раздела.
Returns:
str | None: название раздела.
"""
# Пробуем h2 (основной заголовок страницы)
h2 = soup.select_one("h2")
if h2 is not None:
text = h2.text.strip()
if text:
return text
# Пробуем title страницы
title_tag = soup.select_one("title")
if title_tag is not None:
text = title_tag.text.strip()
# Чистим от названия сайта
text = re.sub(r"\s*\s*.*$", "", text).strip()
if text:
return text
return None