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 __name__ == "__main__":
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2 or "vwts.ru" not in sys.argv[1]:
|
||||||
print("❌ Укажи URL раздела. Пример:")
|
print("❌ Укажи URL раздела vwts.ru. Пример:")
|
||||||
print(" python -m vwts_scraper "
|
print(" python -m vwts_scraper "
|
||||||
"https://vwts.ru/forum/vag/benzinovye-dvigateli/")
|
"https://vwts.ru/forum/vag/benzinovye-dvigateli/")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
@@ -39,3 +39,11 @@ def save_topic(section_slug: str, section_name: str, topic: dict,
|
|||||||
st["topic_count"] += 1
|
st["topic_count"] += 1
|
||||||
log.info(f" 💾 part_{st['file_index']:04d}.jsonl "
|
log.info(f" 💾 part_{st['file_index']:04d}.jsonl "
|
||||||
f"({fpath.stat().st_size/1024/1024:.1f} MB)")
|
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:
|
except ValueError:
|
||||||
pass # "…", "Назад", "Вперёд"
|
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:
|
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
|
return claimed
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import requests
|
status = _head_or_get(session, f"{section_url}page-{claimed}")
|
||||||
r = session.head(f"{section_url}page-{claimed}",
|
if status < 400:
|
||||||
timeout=(10, 30), allow_redirects=True)
|
|
||||||
if r.status_code < 400:
|
|
||||||
return claimed
|
return claimed
|
||||||
|
|
||||||
# Бинарный поиск
|
# Бинарный поиск последней существующей страницы
|
||||||
lo, hi = 1, claimed
|
lo, hi = 1, claimed
|
||||||
while lo < hi:
|
while lo < hi:
|
||||||
mid = (lo + hi + 1) // 2
|
mid = (lo + hi + 1) // 2
|
||||||
hr = session.head(f"{section_url}page-{mid}",
|
hr = _head_or_get(session, f"{section_url}page-{mid}")
|
||||||
timeout=(10, 30), allow_redirects=True)
|
if hr >= 400:
|
||||||
if hr.status_code >= 400:
|
|
||||||
hi = mid - 1
|
hi = mid - 1
|
||||||
else:
|
else:
|
||||||
lo = mid
|
lo = mid
|
||||||
@@ -78,4 +88,4 @@ def verify_last(section_url: str, claimed: int, session) -> int:
|
|||||||
return lo
|
return lo
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
return claimed # не смогли — верим сайту
|
return claimed
|
||||||
|
|||||||
+2
-1
@@ -48,7 +48,8 @@ def run(section_url: str):
|
|||||||
consecutive_404 = 0
|
consecutive_404 = 0
|
||||||
continue
|
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}]")
|
log.info(f"📄 [{pg}/{total_pages}]")
|
||||||
|
|
||||||
page_soup = get(url)
|
page_soup = get(url)
|
||||||
|
|||||||
Reference in New Issue
Block a user