diff --git a/vwts_scraper/__main__.py b/vwts_scraper/__main__.py index 542bf36..3771ff0 100644 --- a/vwts_scraper/__main__.py +++ b/vwts_scraper/__main__.py @@ -25,8 +25,8 @@ logging.basicConfig( ) if __name__ == "__main__": - if len(sys.argv) < 2: - print("❌ Укажи URL раздела. Пример:") + if len(sys.argv) < 2 or "vwts.ru" not in sys.argv[1]: + print("❌ Укажи URL раздела vwts.ru. Пример:") print(" python -m vwts_scraper " "https://vwts.ru/forum/vag/benzinovye-dvigateli/") sys.exit(1) diff --git a/vwts_scraper/output.py b/vwts_scraper/output.py index 691e63e..d94ef7c 100644 --- a/vwts_scraper/output.py +++ b/vwts_scraper/output.py @@ -39,3 +39,11 @@ def save_topic(section_slug: str, section_name: str, topic: dict, st["topic_count"] += 1 log.info(f" 💾 part_{st['file_index']:04d}.jsonl " f"({fpath.stat().st_size/1024/1024:.1f} MB)") + + if st["topic_count"] % 500 == 0: + # Каждые 500 тем — проверяем что диск ещё жив + try: + fpath.stat() + except OSError as e: + log.critical(f"❌ Ошибка диска: {e}") + raise diff --git a/vwts_scraper/paginate.py b/vwts_scraper/paginate.py index 7fc51e2..59e2a71 100644 --- a/vwts_scraper/paginate.py +++ b/vwts_scraper/paginate.py @@ -44,7 +44,20 @@ def count(soup: BeautifulSoup) -> int: except ValueError: pass # "…", "Назад", "Вперёд" - return max(nums) if nums else 1 + return max(nums) if nums else 1 # защита от пустого pageNav + + +def _head_or_get(session, url: str, timeout=(10, 30)) -> int: + """HEAD-запрос с fallback на GET (не все серверы поддерживают HEAD).""" + try: + r = session.head(url, timeout=timeout, allow_redirects=True) + if r.status_code in (405, 501): # HEAD не поддерживается + r = session.get(url, timeout=timeout, + allow_redirects=True, stream=True) + r.close() + return r.status_code + except Exception: + return 500 def verify_last(section_url: str, claimed: int, session) -> int: @@ -57,19 +70,16 @@ def verify_last(section_url: str, claimed: int, session) -> int: return claimed try: - import requests - r = session.head(f"{section_url}page-{claimed}", - timeout=(10, 30), allow_redirects=True) - if r.status_code < 400: + status = _head_or_get(session, f"{section_url}page-{claimed}") + if status < 400: return claimed - # Бинарный поиск + # Бинарный поиск последней существующей страницы lo, hi = 1, claimed while lo < hi: mid = (lo + hi + 1) // 2 - hr = session.head(f"{section_url}page-{mid}", - timeout=(10, 30), allow_redirects=True) - if hr.status_code >= 400: + hr = _head_or_get(session, f"{section_url}page-{mid}") + if hr >= 400: hi = mid - 1 else: lo = mid @@ -78,4 +88,4 @@ def verify_last(section_url: str, claimed: int, session) -> int: return lo except Exception: - return claimed # не смогли — верим сайту + return claimed diff --git a/vwts_scraper/run.py b/vwts_scraper/run.py index 0cb285c..208c040 100644 --- a/vwts_scraper/run.py +++ b/vwts_scraper/run.py @@ -48,7 +48,8 @@ def run(section_url: str): consecutive_404 = 0 continue - url = f"{section_url}page-{pg}" + # page-1 = базовый URL (чтобы не было лишнего редиректа) + url = section_url if pg == 1 else f"{section_url}page-{pg}" log.info(f"📄 [{pg}/{total_pages}]") page_soup = get(url)