Base class for Excel storage implementation Provides formatted Excel export with multiple sheets for contents, comments, and creators Uses singleton pattern to maintain state across multiple store calls
| 50 | |
| 51 | |
| 52 | class ExcelStoreBase(AbstractStore): |
| 53 | """ |
| 54 | Base class for Excel storage implementation |
| 55 | Provides formatted Excel export with multiple sheets for contents, comments, and creators |
| 56 | Uses singleton pattern to maintain state across multiple store calls |
| 57 | """ |
| 58 | |
| 59 | # Class-level singleton management |
| 60 | _instances: Dict[str, "ExcelStoreBase"] = {} |
| 61 | _lock = threading.Lock() |
| 62 | |
| 63 | @classmethod |
| 64 | def get_instance(cls, platform: str, crawler_type: str) -> "ExcelStoreBase": |
| 65 | """ |
| 66 | Get or create a singleton instance for the given platform and crawler type |
| 67 | |
| 68 | Args: |
| 69 | platform: Platform name (xhs, dy, ks, etc.) |
| 70 | crawler_type: Type of crawler (search, detail, creator) |
| 71 | |
| 72 | Returns: |
| 73 | ExcelStoreBase instance |
| 74 | """ |
| 75 | key = f"{platform}_{crawler_type}" |
| 76 | with cls._lock: |
| 77 | if key not in cls._instances: |
| 78 | cls._instances[key] = cls(platform, crawler_type) |
| 79 | return cls._instances[key] |
| 80 | |
| 81 | @classmethod |
| 82 | def flush_all(cls): |
| 83 | """ |
| 84 | Flush all Excel store instances and save to files |
| 85 | Should be called at the end of crawler execution |
| 86 | """ |
| 87 | with cls._lock: |
| 88 | for key, instance in cls._instances.items(): |
| 89 | try: |
| 90 | instance.flush() |
| 91 | utils.logger.info(f"[ExcelStoreBase] Flushed instance: {key}") |
| 92 | except Exception as e: |
| 93 | utils.logger.error(f"[ExcelStoreBase] Error flushing {key}: {e}") |
| 94 | cls._instances.clear() |
| 95 | |
| 96 | def __init__(self, platform: str, crawler_type: str = "search"): |
| 97 | """ |
| 98 | Initialize Excel store |
| 99 | |
| 100 | Args: |
| 101 | platform: Platform name (xhs, dy, ks, etc.) |
| 102 | crawler_type: Type of crawler (search, detail, creator) |
| 103 | """ |
| 104 | if not EXCEL_AVAILABLE: |
| 105 | raise ImportError( |
| 106 | "openpyxl is required for Excel export. " |
| 107 | "Install it with: pip install openpyxl" |
| 108 | ) |
| 109 |