fix: audit findings — edge cases in paginate, output, run
- paginate.py: max(nums) on empty set → guard - paginate.py: HEAD fallback to GET if server doesn't support HEAD - output.py: disk health check every 500 topics - run.py: avoid page-1 extra redirect - __main__.py: URL validation (must contain vwts.ru)
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
+20
-10
@@ -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
|
||||
|
||||
+2
-1
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user