7. Factory & Object Creation Lifecycle (ะคะฐะฑัะธะบะธ ัะฐ ะถะธัััะฒะธะน ัะธะบะป)¶
๐ ะะผััั¶
- ะะณะปัะด ัะฐะฑัะธะบ ัะฐ DI
- Driver Factory
- Storage Factory
- Registry Pattern
- DependencyRegistry (Singleton)
- ApplicationContainer (DI)
- ะะธัััะฒะธะน ัะธะบะป ะพะฑ'ัะบััะฒ
- Transient Objects
ะะณะปัะด ัะฐะฑัะธะบ ัะฐ DI¶
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ FACTORY & DI ARCHITECTURE โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ FACTORIES (OCP) โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ DriverFactory โ โ StorageFactory โ โ โ
โ โ โ โ โ โ โ โ
โ โ โ _DRIVER_REGISTRY โ โ _STORAGE_REGISTRY โ โ โ
โ โ โ { โ โ { โ โ โ
โ โ โ "http": factory, โ โ "memory": factoryโ โ โ
โ โ โ "async": factory,โ โ "json": factory, โ โ โ
โ โ โ "playwright": ..โ โ "sqlite": factoryโ โ โ
โ โ โ } โ โ } โ โ โ
โ โ โ โ โ โ โ โ
โ โ โ register_driver() โ โ register_storage() โ โ โ
โ โ โ create_driver() โ โ create_storage() โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ DOMAIN REGISTRIES (OCP) โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ CrawlModeRegistry โ โ MergeStrategyRegistryโ โ โ
โ โ โ โ โ โ โ โ
โ โ โ "sequential" โ โ "first" โ โ โ
โ โ โ "multiprocessing" โ โ "last" โ โ โ
โ โ โ "celery" โ โ "merge" โ โ โ
โ โ โ [custom] โ โ "newest" โ โ โ
โ โ โ โ โ "oldest" โ โ โ
โ โ โ โ โ "custom" โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ ChangeDetectionStrategyRegistry โ โ โ
โ โ โ โ โ โ
โ โ โ "hash" - SHA256 ะฒัะด text_content โ โ โ
โ โ โ "metadata" - ะฟะพััะฒะฝัะฝะฝั metadata ะฟะพะปัะฒ โ โ โ
โ โ โ [custom] - ะบะฐััะพะผะฝะฐ ัััะฐัะตะณัั โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ DEPENDENCY INJECTION (DI) โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ DependencyRegistry (Singleton) โ โ โ
โ โ โ โ โ โ
โ โ โ โข plugin_manager_factory โ โ โ
โ โ โ โข tree_parser_factory โ โ โ
โ โ โ โข hash_strategy_factory โ โ โ
โ โ โ โข node_class โ โ โ
โ โ โ โข edge_class โ โ โ
โ โ โ โข default_merge_strategy โ โ โ
โ โ โ โ โ โ
โ โ โ Thread-safe Singleton ะท lazy initialization โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ ApplicationContainer โ โ โ
โ โ โ โ โ โ
โ โ โ ะะฑ'ัะดะฝัั ะฒัั ะทะฐะปะตะถะฝะพััั ะดะปั Spider: โ โ โ
โ โ โ โข driver (IDriver) โ โ โ
โ โ โ โข storage (IStorage) โ โ โ
โ โ โ โข scheduler (Scheduler) โ โ โ
โ โ โ โข event_bus (EventBus) โ โ โ
โ โ โ โข plugins (List[BaseNodePlugin]) โ โ โ
โ โ โ โข config (CrawlerConfig) โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Driver Factory¶
ะ ะพะทัะฐััะฒะฐะฝะฝั¶
ะคะฐะนะป: graph_crawler/application/services/driver_factory.py
ะัั ััะตะบัััะฐ¶
# ะะฝัััััะฝัะน Registry (Dict)
_DRIVER_REGISTRY: Dict[str, DriverFactory] = {}
# ะขะธะฟ ัะฐะฑัะธะบะธ
DriverFactory = Callable[[dict], IDriver]
API¶
# ะ ะตััััะฐััั ะฝะพะฒะพะณะพ ะดัะฐะนะฒะตัะฐ
def register_driver(name: str, factory: DriverFactory) -> None:
"""
ะ ะตัััััั ะฝะพะฒะธะน ัะธะฟ ะดัะฐะนะฒะตัะฐ (OCP).
Args:
name: ะะฐะทะฒะฐ ะดัะฐะนะฒะตัะฐ (lowercase)
factory: ะคัะฝะบััั-ัะฐะฑัะธะบะฐ ัะบะฐ ะฟัะธะนะผะฐั config ั ะฟะพะฒะตััะฐั IDriver
"""
# ะกัะฒะพัะตะฝะฝั ะดัะฐะนะฒะตัะฐ
def create_driver(
driver: DriverType = None,
config: Optional[dict] = None
) -> IDriver:
"""
ะกัะฒะพััั ะดัะฐะนะฒะตั ะท string ะฐะฑะพ ะฟะพะฒะตััะฐั instance.
Args:
driver: "http" | "async" | "playwright" | "stealth" | IDriver instance
config: ะะพะฝััะณััะฐััั ะดัะฐะนะฒะตัะฐ
"""
# ะกะฟะธัะพะบ ะดะพัััะฟะฝะธั
ะดัะฐะนะฒะตััะฒ
def get_available_drivers() -> list[str]:
"""ะะพะฒะตััะฐั ัะฟะธัะพะบ ะทะฐัะตััััะพะฒะฐะฝะธั
ะดัะฐะนะฒะตััะฒ."""
ะะฑัะดะพะฒะฐะฝั ะดัะฐะนะฒะตัะธ¶
| ะะฐะทะฒะฐ | ะะปะฐั | ะัะฑะปัะพัะตะบะฐ | ะะฟะธั |
|---|---|---|---|
http | HTTPDriver | requests | ะกะธะฝั ัะพะฝะฝะธะน HTTP (default) |
async | AsyncDriver | aiohttp | ะัะธะฝั ัะพะฝะฝะธะน HTTP |
playwright | PlaywrightDriver | playwright | JS rendering |
stealth | StealthDriver | playwright | Anti-bot bypass |
ะะฝัััะฐะปัะทะฐััั¶
def _register_builtin_drivers():
"""ะ ะตัััััั ะฒะฑัะดะพะฒะฐะฝั ะดัะฐะนะฒะตัะธ ะฟัะธ ัะผะฟะพััั ะผะพะดัะปั."""
def http_factory(config: dict) -> IDriver:
from graph_crawler.infrastructure.transport import HTTPDriver
return HTTPDriver(config)
def async_factory(config: dict) -> IDriver:
from graph_crawler.infrastructure.transport.async_http import AsyncDriver
return AsyncDriver(config)
def playwright_factory(config: dict) -> IDriver:
from graph_crawler.infrastructure.transport.playwright import PlaywrightDriver
return PlaywrightDriver(config)
def stealth_factory(config: dict) -> IDriver:
from graph_crawler.infrastructure.transport.stealth_driver import StealthDriver
return StealthDriver(config)
_DRIVER_REGISTRY["http"] = http_factory
_DRIVER_REGISTRY["async"] = async_factory
_DRIVER_REGISTRY["playwright"] = playwright_factory
_DRIVER_REGISTRY["stealth"] = stealth_factory
# ะะฒัะพะผะฐัะธัะฝะฐ ัะฝัััะฐะปัะทะฐััั ะฟัะธ ัะผะฟะพััั
_register_builtin_drivers()
Storage Factory¶
ะ ะพะทัะฐััะฒะฐะฝะฝั¶
ะคะฐะนะป: graph_crawler/application/services/storage_factory.py
API¶
# ะ ะตััััะฐััั
def register_storage(name: str, factory: Callable[[dict], IStorage]) -> None:
"""ะ ะตัััััั storage factory (OCP)."""
# ะกัะฒะพัะตะฝะฝั
def create_storage(
storage: StorageType = None,
config: Optional[Dict] = None,
**kwargs
) -> IStorage:
"""ะกัะฒะพััั storage ะท Registry Pattern."""
# ะกะฟะธัะพะบ ัะธะฟัะฒ
def get_available_storage_types() -> list:
"""ะะพะฒะตััะฐั ัะฟะธัะพะบ ะดะพัััะฟะฝะธั
storage ัะธะฟัะฒ."""
ะะฑัะดะพะฒะฐะฝั storage¶
| ะะฐะทะฒะฐ | ะะปะฐั | ะ ะตะบะพะผะตะฝะดะพะฒะฐะฝะพ ะดะปั | ะัะฑะปัะพัะตะบะฐ |
|---|---|---|---|
memory | MemoryStorage | < 1K nodes | built-in |
json | JSONStorage | 1K - 10K nodes | aiofiles |
sqlite | SQLiteStorage | 10K - 100K nodes | aiosqlite |
postgresql | PostgreSQLStorage | 100K+ nodes | asyncpg |
mongodb | MongoDBStorage | 100K+ nodes | motor |
Registry Pattern¶
BaseRegistry¶
ะคะฐะนะป: graph_crawler/domain/entities/registries.py
class BaseRegistry(ABC):
"""ะะฐะทะพะฒะธะน ะบะปะฐั ะดะปั ะฒััั
ัะตัััััะฒ."""
_registry: Dict[str, Any] = {}
@classmethod
def register(cls, name: str, item: Any) -> None:
"""ะ ะตัััััั ะตะปะตะผะตะฝั."""
@classmethod
def unregister(cls, name: str) -> None:
"""ะะธะดะฐะปัั ะตะปะตะผะตะฝั."""
@classmethod
def get(cls, name: str) -> Optional[Any]:
"""ะััะธะผัั ะตะปะตะผะตะฝั."""
@classmethod
def get_all_names(cls) -> List[str]:
"""ะกะฟะธัะพะบ ะฒััั
ะฝะฐะทะฒ."""
@classmethod
def is_registered(cls, name: str) -> bool:
"""ะะตัะตะฒัััั ัะตััััะฐััั."""
@classmethod
def clear(cls) -> None:
"""ะัะธััั ัะตัััั (ะดะปั ัะตัััะฒ)."""
ะะพะฝะบัะตัะฝั Registry¶
class CrawlModeRegistry(BaseRegistry):
"""
ะ ะตัััั ัะตะถะธะผัะฒ ะบัะฐัะปัะฝะณั.
ะะตัะพะปัะฝั ัะตะถะธะผะธ:
- sequential: GraphSpider
- multiprocessing: MultiprocessingSpider
- celery: CeleryBatchSpider
"""
class MergeStrategyRegistry(BaseRegistry):
"""
ะ ะตัััั ัััะฐัะตะณัะน merge ะดะปั Graph.union().
ะะตัะพะปัะฝั ัััะฐัะตะณัั:
- first, last, merge, newest, oldest, custom
"""
class ChangeDetectionStrategyRegistry(BaseRegistry):
"""
ะ ะตัััั ัััะฐัะตะณัะน ะดะตัะตะบััั ะทะผัะฝ.
ะะตัะพะปัะฝั ัััะฐัะตะณัั:
- hash: SHA256 ะฒัะด text_content
- metadata: ะฟะพััะฒะฝัะฝะฝั metadata ะฟะพะปัะฒ
"""
Lazy Factory Pattern¶
ะะปั ัะฝะธะบะฝะตะฝะฝั circular imports ะฒะธะบะพัะธััะพะฒัััััั lazy factory:
def _lazy_import_spider(mode: str):
"""Lazy factory ะดะปั ัะผะฟะพััั Spider ะบะปะฐััะฒ."""
def factory():
if mode == "sequential":
from graph_crawler.application.use_cases.crawling.spider import GraphSpider
return GraphSpider
elif mode == "multiprocessing":
from graph_crawler.application.use_cases.crawling.multiprocessing_spider import (
MultiprocessingSpider,
)
return MultiprocessingSpider
elif mode == "celery":
from graph_crawler.application.use_cases.crawling.celery_batch_spider import (
CeleryBatchSpider,
)
return CeleryBatchSpider
else:
raise ValueError(f"Unknown crawl mode: {mode}")
return factory
# ะ ะตััััะฐััั ะท lazy factory
CrawlModeRegistry.register("sequential", _lazy_import_spider("sequential"))
DependencyRegistry¶
ะัะธะทะฝะฐัะตะฝะฝั¶
ะคะฐะนะป: graph_crawler/application/context/dependency_registry.py
Thread-safe Singleton ะดะปั ัะฟัะฐะฒะปัะฝะฝั ะทะฐะปะตะถะฝะพัััะผะธ, ัะบั ะฝะต ัะตััะฐะปัะทัััััั: - plugin_manager - ัะฟัะฐะฒะปัั ะฟะปะฐะณัะฝะฐะผะธ - tree_parser - ะฟะฐััะตั HTML - hash_strategy - ัััะฐัะตะณัั ะพะฑัะธัะปะตะฝะฝั hash
ะัะพะฑะปะตะผะฐ¶
# Node ะผะฐั ะฟะพะปั ัะบั ะะ ัะตััะฐะปัะทัััััั:
class Node(BaseModel):
plugin_manager: Optional[Any] = Field(default=None, exclude=True)
tree_parser: Optional[Any] = Field(default=None, exclude=True)
hash_strategy: Optional[Any] = Field(default=None, exclude=True)
ะััะปั ะดะตัะตััะฐะปัะทะฐััั ะท JSON/SQLite ัั ะฟะพะปั ะฑัะดััั None.
ะ ััะตะฝะฝั¶
from graph_crawler.application.context import DependencyRegistry
# 1. ะะพะฝััะณััะฐััั ะฟัะธ ััะฐััั ะฟัะพะณัะฐะผะธ
DependencyRegistry.configure(
plugin_manager_factory=lambda: NodePluginManager(),
tree_parser_factory=lambda: BeautifulSoupAdapter(),
hash_strategy_factory=lambda: DefaultHashStrategy(),
default_merge_strategy='merge'
)
# 2. ะััะธะผะฐะฝะฝั ะบะพะฝัะตะบััั ะดะปั ะดะตัะตััะฐะปัะทะฐััั
context = DependencyRegistry.get_context()
graph = GraphMapper.to_domain(graph_dto, context=context)
# 3. Override ะดะปั ะบะพะฝะบัะตัะฝะพะณะพ ะฒะธะฟะฐะดะบั
context = DependencyRegistry.get_context(
plugin_manager=custom_pm, # Override ััะปัะบะธ plugin_manager
)
API¶
class DependencyRegistry:
"""Thread-safe Singleton."""
@classmethod
def configure(
cls,
plugin_manager: Optional[Any] = None,
plugin_manager_factory: Optional[Callable] = None,
tree_parser: Optional[Any] = None,
tree_parser_factory: Optional[Callable] = None,
hash_strategy: Optional[Any] = None,
hash_strategy_factory: Optional[Callable] = None,
node_class: Optional[Type] = None,
edge_class: Optional[Type] = None,
default_merge_strategy: str = "last",
) -> None:
"""ะะพะฝััะณัััั ะดะตัะพะปัะฝั ะทะฐะปะตะถะฝะพััั."""
@classmethod
def get_context(
cls,
plugin_manager: Optional[Any] = None,
tree_parser: Optional[Any] = None,
hash_strategy: Optional[Any] = None,
node_class: Optional[Type] = None,
edge_class: Optional[Type] = None,
default_merge_strategy: Optional[str] = None,
) -> Dict[str, Any]:
"""ะััะธะผัั ะบะพะฝัะตะบัั ะท ะผะพะถะปะธะฒัััั override."""
@classmethod
def reset(cls) -> None:
"""ะกะบะธะดะฐั ะดะพ ะดะตัะพะปััะฒ (ะดะปั ัะตัััะฒ)."""
# Shortcut ะผะตัะพะดะธ
@classmethod
def set_plugin_manager(cls, pm: Any) -> None: ...
@classmethod
def get_plugin_manager(cls) -> Optional[Any]: ...
@classmethod
def set_tree_parser(cls, tp: Any) -> None: ...
@classmethod
def get_tree_parser(cls) -> Optional[Any]: ...
@classmethod
def set_default_merge_strategy(cls, strategy: str) -> None: ...
@classmethod
def get_default_merge_strategy(cls) -> str: ...
ApplicationContainer¶
ะัะธะทะฝะฐัะตะฝะฝั¶
ะคะฐะนะป: graph_crawler/application/services/application_container.py
DI ะบะพะฝัะตะนะฝะตั ัะบะธะน ะพะฑ'ัะดะฝัั ะฒัั ะทะฐะปะตะถะฝะพััั ะดะปั Spider.
ะกัััะบัััะฐ¶
from dataclasses import dataclass
from typing import List, Optional
@dataclass
class ApplicationContainer:
"""
DI ะบะพะฝัะตะนะฝะตั ะดะปั Spider.
ะะฑ'ัะดะฝัั ะฒัั ะทะฐะปะตะถะฝะพััั ะฒ ะพะดะฝะพะผั ะผัััั.
Spider ะพััะธะผัั ัะตะน ะบะพะฝัะตะนะฝะตั ั ะฒะธะบะพัะธััะพะฒัั ะทะฐะปะตะถะฝะพััั.
"""
# ะัะฝะพะฒะฝั ะทะฐะปะตะถะฝะพััั
driver: IDriver
storage: IStorage
scheduler: Scheduler
event_bus: EventBus
# ะะปะฐะณัะฝะธ ัะฐ middleware
plugins: List[BaseNodePlugin]
middleware_chain: MiddlewareChain
# ะะพะฝััะณััะฐััั
config: CrawlerConfig
# ะะฐััะพะผะฝั ะบะปะฐัะธ
node_class: type = Node
edge_class: type = Edge
# Factories
node_factory: Optional[Callable] = None
edge_factory: Optional[Callable] = None
ะกัะฒะพัะตะฝะฝั ะบะพะฝัะตะนะฝะตัะฐ¶
# ะัะตัะตะดะธะฝั API Layer (crawl() ััะฝะบััั)
def _create_container(config: CrawlerConfig) -> ApplicationContainer:
"""
ะกัะฒะพััั DI ะบะพะฝัะตะนะฝะตั ะท ะบะพะฝััะณััะฐััั.
ะะธะบะพัะธััะพะฒัั ัะฐะฑัะธะบะธ ะดะปั ััะฒะพัะตะฝะฝั ะบะพะผะฟะพะฝะตะฝััะฒ.
"""
# ะกัะฒะพัััะผะพ driver ัะตัะตะท factory
driver = create_driver(
config.driver,
config.driver_config
)
# ะกัะฒะพัััะผะพ storage ัะตัะตะท factory
storage = create_storage(
config.storage,
config.storage_config
)
# ะกัะฒะพัััะผะพ event bus
event_bus = EventBus()
# ะกัะฒะพัััะผะพ scheduler
scheduler = Scheduler(
url_rules=config.url_rules,
max_depth=config.max_depth
)
# ะกัะฒะพัััะผะพ plugin manager ะท ะฟะปะฐะณัะฝะฐะผะธ
plugins = config.plugins or get_default_node_plugins()
return ApplicationContainer(
driver=driver,
storage=storage,
scheduler=scheduler,
event_bus=event_bus,
plugins=plugins,
middleware_chain=MiddlewareChain(),
config=config,
node_class=config.node_class or Node,
edge_class=config.edge_class or Edge,
)
ะะธัััะฒะธะน ัะธะบะป ะพะฑ'ัะบััะฒ¶
Node Lifecycle¶
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ NODE LIFECYCLE โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ ะะขะะ 1: URL_STAGE (ะกัะฒะพัะตะฝะฝั) โ โ
โ โ โ โ
โ โ 1. Node.__init__(url="...") โ โ
โ โ โ โ โ
โ โ โผ โ โ
โ โ 2. model_post_init() โ โ
โ โ โ โ โ
โ โ โผ โ โ
โ โ 3. _trigger_node_created_hook() โ โ
โ โ โ โ โ
โ โ โผ โ โ
โ โ 4. ON_NODE_CREATED plugins (sync) โ โ
โ โ โข ะะฝะฐะปัะท URL โ โ
โ โ โข ะััะฐะฝะพะฒะปะตะฝะฝั should_scan, can_create_edges โ โ
โ โ โข ะััะฐะฝะพะฒะปะตะฝะฝั priority โ โ
โ โ โ โ
โ โ ะะพัััะฟะฝะพ: url, depth, should_scan, can_create_edges โ โ
โ โ lifecycle_stage = URL_STAGE โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โ (ัะบัะพ should_scan == True) โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ ะะขะะ 2: HTML_STAGE (ะะฑัะพะฑะบะฐ HTML) โ โ
โ โ โ โ
โ โ 5. await node.process_html(html) โ โ
โ โ โ โ โ
โ โ โผ โ โ
โ โ 6. _parse_html(html) โ (parser, html_tree) โ โ
โ โ โ โ โ
โ โ โผ โ โ
โ โ 7. _execute_plugins() (async) โ โ
โ โ โ โ โ
โ โ โโโโถ ON_BEFORE_SCAN plugins โ โ
โ โ โ โ โ
โ โ โโโโถ ON_HTML_PARSED plugins โ โ
โ โ โ โข MetadataExtractor โ metadata โ โ
โ โ โ โข LinkExtractor โ extracted_links โ โ
โ โ โ โข PhoneExtractor โ user_data['phones'] โ โ
โ โ โ โ โ
โ โ โโโโถ _update_from_context() โ โ
โ โ โ โข metadata = context.metadata โ โ
โ โ โ โข user_data.update(context.user_data) โ โ
โ โ โ โ โ
โ โ โโโโถ ON_AFTER_SCAN plugins โ โ
โ โ โข Vectorization โ โ
โ โ โข ML analysis โ โ
โ โ โ โ โ
โ โ โผ โ โ
โ โ 8. _compute_content_hash() โ โ
โ โ โ โ โ
โ โ โผ โ โ
โ โ 9. _cleanup_memory() โ โ
โ โ โข del html โ โ
โ โ โข del html_tree โ โ
โ โ โข context.html = None โ โ
โ โ โ โ
โ โ lifecycle_stage = HTML_STAGE โ โ
โ โ ะะพัััะฟะฝะพ: metadata, user_data, content_hash โ โ
โ โ HTML ะะะะะะะะ ะท ะฟะฐะผ'ััั! โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ ะะะะ ะะะะะะฏ / ะะะะะะะะะะะฏ โ โ
โ โ โ โ
โ โ 10. storage.save_graph(graph) โ โ
โ โ โข node.model_dump() โ JSON โ โ
โ โ โข plugin_manager, tree_parser EXCLUDED โ โ
โ โ โ โ
โ โ 11. storage.load_graph() โ โ
โ โ โข Node.model_validate(data, context=context) โ โ
โ โ โข node.restore_dependencies(pm, tp, hs) โ โ
โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Spider Lifecycle¶
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SPIDER LIFECYCLE โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ 1. ะกะขะะะ ะะะะฏ โ
โ spider = GraphSpider(container) โ
โ โ
โ 2. ะะะะฆะะะะะะะฆะะฏ โ
โ โ โ
โ โโโโถ scheduler.add_url(start_url, depth=0) โ
โ โ โ
โ โโโโถ event_bus.publish(CRAWL_STARTED) โ
โ โ โ
โ โโโโถ plugin_manager.setup() ะดะปั ะบะพะถะฝะพะณะพ ะฟะปะฐะณัะฝะฐ โ
โ โ
โ 3. ะะะะะะะะ ะฆะะะ โ
โ โ โ
โ โ while not scheduler.is_empty() and pages < max_pages: โ
โ โ โ โ
โ โ โโโโถ url, depth = scheduler.get_next_url() โ
โ โ โ โ
โ โ โโโโถ node = Node(url, depth, plugin_manager) โ
โ โ โ โโโโถ ON_NODE_CREATED plugins โ
โ โ โ โ
โ โ โโโโถ if node.should_scan: โ
โ โ โ โ โ
โ โ โ โโโโถ response = await driver.fetch(url) โ
โ โ โ โ โ
โ โ โ โโโโถ links = await node.process_html(response.html) โ
โ โ โ โ โโโโถ ON_BEFORE_SCAN, ON_HTML_PARSED, ON_AFTER_SCAN โ
โ โ โ โ โ
โ โ โ โโโโถ for link in links: โ
โ โ โ โ scheduler.add_url(link, depth+1) โ
โ โ โ โ โ
โ โ โ โโโโถ graph.add_node(node) โ
โ โ โ โ
โ โ โโโโถ event_bus.publish(NODE_SCANNED) โ
โ โ โ
โ 4. ะะะะะ ะจะะะะฏ โ
โ โ โ
โ โโโโถ await storage.save_graph(graph) โ
โ โ โ
โ โโโโถ event_bus.publish(CRAWL_COMPLETED) โ
โ โ โ
โ โโโโถ plugin_manager.teardown() ะดะปั ะบะพะถะฝะพะณะพ ะฟะปะฐะณัะฝะฐ โ
โ โ โ
โ โโโโถ await driver.close() โ
โ โ โ
โ โโโโถ await storage.close() โ
โ โ
โ 5. ะ ะะะฃะะฌะขะะข โ
โ return graph โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Transient Objects¶
ะฉะพ ััะฒะพััััััั ะฐะฒัะพะผะฐัะธัะฝะพ¶
| ะะฑ'ัะบั | ะกัะฒะพััััััั | ะะฝะธััััััั | ะัะธะผััะบะฐ |
|---|---|---|---|
html | driver.fetch() | _cleanup_memory() | ะะต ะทะฑะตััะณะฐััััั ะฒ Node |
html_tree | _parse_html() | _cleanup_memory() | BeautifulSoup ะดะตัะตะฒะพ |
NodePluginContext | process_html() | ะฟััะปั ะฒะธะบะพะฝะฐะฝะฝั | ะะตัะตะดะฐััััั ะผัะถ ะฟะปะฐะณัะฝะฐะผะธ |
MiddlewareContext | ะฟะตัะตะด fetch | ะฟััะปั fetch | ะะตัะตะดะฐััััั ะผัะถ middleware |
FetchResponse | driver.fetch() | ะฟััะปั process | DTO ะดะปั ะฒัะดะฟะพะฒัะดั |
CrawlerEvent | EventBus | ะฟััะปั notify | ะะฑะตััะณะฐััััั ะฒ history ัะบัะพ enabled |
ะฉะพ ะทะฑะตััะณะฐััััั¶
| ะะฑ'ัะบั | Scope | ะัะธะผััะบะฐ |
|---|---|---|
Node | Graph lifetime | metadata, user_data ะทะฑะตััะณะฐััััั |
Edge | Graph lifetime | anchor_text, link_type |
Graph | Application | nodes, edges, stats |
EventBus.history | Optional | ะฏะบัะพ enabled |
Hidden Factories¶
# 1. Node Factory (ะฒะฝัััััะฝั)
# ะ Spider ะฒะธะบะพัะธััะพะฒัััััั:
node = container.node_class(
url=url,
depth=depth,
plugin_manager=plugin_manager
)
# 2. Edge Factory (ะฒะฝัััััะฝั)
# ะ LinkProcessor:
edge = container.edge_class(
source_node_id=source.node_id,
target_node_id=target.node_id,
anchor_text=anchor,
link_type=types
)
# 3. Plugin Context Factory
# ะ Node.process_html():
context = NodePluginContext(
node=self,
url=self.url,
depth=self.depth,
...
)
๐ ะัะฐะณัะฐะผะฐ ะทะฐะปะตะถะฝะพััะตะน¶
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ DEPENDENCY FLOW โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ crawl(url, ...) โ
โ โ โ
โ โผ โ
โ _create_container() โ
โ โ โ
โ โโโโถ DriverFactory.create_driver() โ
โ โ โ โ
โ โ โโโโถ _DRIVER_REGISTRY["http"]() โ HTTPDriver โ
โ โ โ
โ โโโโถ StorageFactory.create_storage() โ
โ โ โ โ
โ โ โโโโถ _STORAGE_REGISTRY["memory"]() โ MemoryStorage โ
โ โ โ
โ โโโโถ EventBus() โ
โ โ โ
โ โโโโถ Scheduler(url_rules) โ
โ โ โ
โ โโโโถ NodePluginManager(plugins) โ
โ โ โ
โ โโโโถ get_default_node_plugins() โ
โ โข MetadataExtractorPlugin โ
โ โข LinkExtractorPlugin โ
โ โข TextExtractorPlugin โ
โ โ โ
โ โผ โ
โ ApplicationContainer(driver, storage, scheduler, event_bus, plugins) โ
โ โ โ
โ โผ โ
โ Spider(container) โ
โ โ โ
โ โผ โ
โ await spider.crawl(start_url) โ
โ โ โ
โ โผ โ
โ return Graph โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ ะะฐะฒัะณะฐััั¶
- Architecture Overview
- Layer Specification
- Component Catalog
- Communication Channels
- Plugin System
- Extension Points
- Factory & Lifecycle (ะฟะพัะพัะฝะธะน ะดะพะบัะผะตะฝั)