- 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-тем при многопоточном парсинге
111 lines
3.5 KiB
Python
111 lines
3.5 KiB
Python
"""Пагинация phpBB.
|
||
|
||
phpBB использует параметр ?start=N для пагинации, где:
|
||
N = (page - 1) * items_per_page
|
||
|
||
Страницы раздела:
|
||
viewforum.php?f=22&start=25 ← страница 2
|
||
viewforum.php?f=22&start=50 ← страница 3
|
||
...
|
||
|
||
Страницы темы:
|
||
viewtopic.php?f=22&t=123&start=10 ← страница 2
|
||
viewtopic.php?f=22&t=123&start=20 ← страница 3
|
||
...
|
||
|
||
HTML-структура пагинации:
|
||
<ul class="pagination">
|
||
<li><a href="./viewforum.php?f=22&start=25">2</a></li>
|
||
<li><a href="./viewforum.php?f=22&start=50">3</a></li>
|
||
...
|
||
<li><a href="./viewforum.php?f=22&start=1800">72</a></li>
|
||
</ul>
|
||
"""
|
||
|
||
import re
|
||
import logging
|
||
from bs4 import BeautifulSoup
|
||
|
||
log = logging.getLogger(__name__)
|
||
|
||
|
||
def count_pages(soup: BeautifulSoup, per_page: int) -> int:
|
||
"""Определить количество страниц в пагинации phpBB.
|
||
|
||
Алгоритм:
|
||
1. Ищем ul.pagination (или ul.page-nav).
|
||
2. Собираем все start=N из href.
|
||
3. Находим max_start.
|
||
4. Вычисляем: pages = max_start / per_page + 1.
|
||
|
||
Args:
|
||
soup: BeautifulSoup страницы раздела или темы.
|
||
per_page: Количество элементов на странице (темы=25, посты=10).
|
||
|
||
Returns:
|
||
int: количество страниц (минимум 1).
|
||
"""
|
||
# Ищем контейнер пагинации (два варианта класса)
|
||
nav = soup.find("ul", class_="pagination")
|
||
if nav is None:
|
||
nav = soup.find("ul", class_="page-nav")
|
||
if nav is None:
|
||
return 1
|
||
|
||
# Собираем все start=N из ссылок
|
||
starts = set()
|
||
for link in nav.select("a"):
|
||
href = link.get("href", "")
|
||
match = re.search(r"[?&]start=(\d+)", href)
|
||
if match:
|
||
starts.add(int(match.group(1)))
|
||
|
||
if not starts:
|
||
return 1
|
||
|
||
max_start = max(starts)
|
||
pages = (max_start // per_page) + 1
|
||
return pages
|
||
|
||
|
||
def page_url(base_url: str, page_num: int, per_page: int) -> str:
|
||
"""Сформировать URL страницы раздела phpBB.
|
||
|
||
Формат:
|
||
page=1 → base_url (без ?start=)
|
||
page>1 → base_url + &start=N
|
||
|
||
Args:
|
||
base_url: URL страницы без параметра start (напр. viewforum.php?f=22).
|
||
page_num: Номер страницы (1-based).
|
||
per_page: Количество элементов на страницу.
|
||
|
||
Returns:
|
||
str: URL страницы с ?start= (если page>1).
|
||
"""
|
||
if page_num <= 1:
|
||
return base_url
|
||
|
||
start = (page_num - 1) * per_page
|
||
separator = "&" if "?" in base_url else "?"
|
||
return f"{base_url}{separator}start={start}"
|
||
|
||
|
||
def topic_page_url(topic_url: str, page_num: int, per_page: int) -> str:
|
||
"""Сформировать URL страницы темы phpBB.
|
||
|
||
Args:
|
||
topic_url: Полный URL темы.
|
||
page_num: Номер страницы (1-based, начиная с 2).
|
||
per_page: Количество постов на страницу.
|
||
|
||
Returns:
|
||
str: URL страницы темы с ?start=.
|
||
"""
|
||
if page_num <= 1:
|
||
return topic_url
|
||
|
||
start = (page_num - 1) * per_page
|
||
separator = "&" if "?" in topic_url else "?"
|
||
return f"{topic_url}{separator}start={start}"
|