Powrót do strony głównej

Asynchroniczny parser mangi: architektura w Pythonie

Szczegółowy rozbiór architektury asynchronicznego parsera mangi w Pythonie. Omówiono kontrakty poprzez klasy abstrakcyjne, walidacja z Pydantic, ORM z SQLAlchemy i przejście na mikrousługi. Nacisk na skalowalność i utrzymanie bazy kodu.

Parser Python mangi: od kontraktów do mikrousług
Advertisement 728x90

Asynchroniczny parser mangi w Pythonie: od architektury do mikrousług

Tworzenie skalowalnego parsera mangi to zadanie łączące wyzwania programowania asynchronicznego, projektowania kontraktów i zarządzania stanem. Manga-Day to praktyczny przykład przejścia od monolitu do architektury mikrousługowej z wykorzystaniem asyncio, Pydantic i SQLAlchemy. Projekt początkowo był pomyślany jako narzędzie do automatycznego zbierania danych z dziesiątek stron internetowych, ale stał się podręcznikiem do projektowania rozszerzalnych systemów.

Podstawy architektury: kontrakty i asynchroniczność

Jądro systemu jest zbudowane wokół abstrakcyjnej klasy BaseSpider. Każdy konkretny parser (np. dla multi-manga) implementuje jednolity interfejs, co pozwala na dynamiczne podłączanie nowych źródeł bez zmiany rdzenia. Kontrakt obejmuje trzy kluczowe metody:

from abc import ABC, abstractmethod

class BaseSpider(ABC):
    BASE_URL = "https://example-manga.com"

    @abstractmethod
    async def get(self, url: str, **kwargs) -> Optional[MangaSchema]: ...

    @abstractmethod
    async def pages(
        self, start_page: int | None = None
    ) -> AsyncGenerator[list[BaseManga], Any]: ...

Taki podejście zapewnia:

Google AdInline article slot
  • Jednolitość obsługi błędów i timeoutów
  • Możliwość testowania każdego pająka niezależnie
  • Wsparcie dla poziomego skalowania poprzez asynchroniczne generatory

Asynchroniczność jest zaimplementowana na poziomie asyncio, co pozwoliło efektywnie obsługiwać setki jednoczesnych żądań bez blokowania I/O. Szczególnie ważne jest to przy pracy z wolnymi lub niestabilnymi źródłami.

Walidacja danych i modele ORM

Do ścisłej typizacji i walidacji używa się Pydantic. System jest zbudowany na trzech poziomach modeli:

  • BaseManga — minimalny zestaw pól: title, poster, url, sku (hash SHA256)
  • MangaWithGallery — dodaje listę URL obrazów
  • MangaSchema — pełny model z gatunkami, autorem i językiem (opcjonalnie)

W bazie danych zarządzanej przez SQLAlchemy zaimplementowano następujące encje:

Google AdInline article slot
  • Manga, Author, Genre, Language — główne tabele
  • GenreManga — relacja wiele-do-wielu dla gatunków
  • Gallery — oddzielna tabela dla obrazów (aby nie obciążać głównego zapytania)
  • GeneratedPdf — przestarzała tabela, planowana do usunięcia

Podział galerii na oddzielną encję pozwolił uniknąć problemów N+1 przy ładowaniu list mangi — teraz obrazy są ładowane tylko na wyraźne żądanie.

Menedżery: logika ponad danymi

Warstwą logiki biznesowej są menedżery — klasy odpowiedzialne za konkretne operacje:

  • MangaManager: operacje CRUD po SKU, URL, ID
  • RequestManager: żądania HTTP z logiką retry i timeoutami
  • AlertManager: uniwersalny system powiadomień (Telegram, Discord itp.)
  • SpiderManager: centralny orkiestrator wszystkich pająków

SpiderManager jest szczególnie interesujący swoją modularnością. Jest podzielony na cztery pliki:

Google AdInline article slot
  • _load.py — dynamiczne ładowanie pająków przez __all__
  • _status.py — modele statusów i wyliczenia
  • _starter.py — uruchamianie i zarządzanie cyklem życia
  • _spider.py — łączenie komponentów w jednolity interfejs

Taka dekompozycja uprościła testowanie i pozwoliła izolować zmiany — na przykład, dodanie nowego pająka wymaga tylko jego rejestracji w __init__.py, bez zmian w rdzeniu.

Przejście na mikrousługi: punkty bólu i rozwiązania

Początkowo projekt był monolityczny, ale dwa serwisy stały się katalizatorami zmian:

  • PDFService — zużywał zbyt dużo RAM, został wydzielony do oddzielnego kontenera
  • Bot — wymagał WebSocket i uwierzytelniania JWT, również zmigrowany do izolowanego serwisu

Migracja przeszła przez kilka etapów:

  • Frontend odłączony od rdzenia — teraz działa wyłącznie przez REST API, z minimalnym Jinja tylko dla konfiguracji
  • Bot przepisany z użyciem aiohttp dla WebSocket i wdrożeniem modelu ról przez JWT
  • Generowanie PDF teraz uruchamiane jako zadanie tła z ograniczonym zużyciem pamięci

Główne trudności:

  • Serializacja złożonych obiektów między serwisami
  • Spójność danych przy częściowych awariach
  • Konfiguracja health-checków i graceful shutdown

Co ważne

  • Kontrakty ważniejsze od implementacji — abstrakcyjne klasy pozwoliły skalować system bez refaktoryzacji rdzenia
  • Asynchroniczność ≠ automatyczna wydajność — trzeba kontrolować liczbę konkurencyjnych zadań i używać semaforów
  • ORM to nie panaceum — ręczna optymalizacja zapytań i denormalizacja (np. wyodrębnienie galerii) są kluczowe dla wydajności
  • Mikrousługi komplikują, ale dają elastyczność — wyodrębnienie PDFService rozwiązało problem z pamięcią, ale dodało złożoność wdrożenia
  • Testowalność to rezultat architektury — modularność SpiderManager pozwoliła pisać testy jednostkowe bez mocków zewnętrznych zależności

Projekt stał się punktem wyjścia do zrozumienia, jak budować systemy, które można rozwijać latami. Baza kodu urosła do dziesiątek tysięcy linii, ale dzięki jasnemu podziałowi odpowiedzialności pozostaje łatwa w utrzymaniu. Kolejne kroki — wdrożenie message broker dla kolejek zadań i pełne CI/CD z autotesty na każdy pull request.

— Editorial Team

Advertisement 728x90

Czytaj dalej