# bangwen-collectors πŸ•·οΈ > **Lightweight, declarative, and SSRF-hardened edge collection system for public job recruitment aggregation.** --- ## πŸ“– Table of Contents - [✨ Overview](#-overview) - [πŸ“ Project Structure](#-project-structure) - [πŸš€ Quickstart](#-quickstart) - [Prerequisites](#prerequisites) - [Local Setup](#local-setup) - [πŸ› οΈ Configuration & Source Authoring](#️-configuration--source-authoring) - [Declarative TOML Sources](#declarative-toml-sources) - [Custom Python Adapters](#custom-python-adapters) - [βš™οΈ Scheduling & Rate Limiting](#️-scheduling--rate-limiting) - [πŸ§ͺ Testing](#-testing) - [πŸ“œ License](#-license) --- ## ✨ Overview `bangwen-collectors` serves as the decentralized edge data extraction layer for the `bangwen` recruitment aggregation engine: - **Dual-Mode Crawling**: Supports both zero-code declarative TOML site cards (`sources/`) and customized Python scraper adapters (`adapters/`). - **Domain-Grouped Concurrency**: Concurrently processes distinct domains while enforcing strictly serial queues within the same domain. - **WAF-Friendly Polite Delays**: Configurable inter-channel pauses (default 3.0s) and detail extraction throttles (0.3s) preventing IP bans. - **SSRF Defense-in-Depth**: Built-in private IP filtering, strict domain whitelists, and hop-by-hop verification during HTTP redirects. --- ## πŸ“ Project Structure ``` collectors/ β”œβ”€β”€ adapters/ # Specialized scrapers for complex dynamic sites β”‚ └── sc/chengdu_rsks.py # Custom adapter for Chengdu Personnel Exam Net β”œβ”€β”€ sources/ # Zero-code declarative TOML site cards β”‚ β”œβ”€β”€ sc/ # Sichuan Province sources (e.g. myrsks, zgrsks) β”‚ └── yn/ # Yunnan Province sources (e.g. hrss, yuxi) β”œβ”€β”€ common/ # Core modules β”‚ β”œβ”€β”€ content.py # Content truncation and dirty body cleanup β”‚ β”œβ”€β”€ declarative.py # Universal declarative parsing engine β”‚ β”œβ”€β”€ fetcher.py # PoliteFetcher HTTP client with SSRF verification β”‚ β”œβ”€β”€ protocol.py # Pydantic schema matching the bangwen/v1 protocol β”‚ └── security.py # URL validation and private IP guards β”œβ”€β”€ tests/ # Security and parsing regression tests β”œβ”€β”€ config.toml # Active collection and scheduler configuration β”œβ”€β”€ runner.py # Main scheduler and multi-domain orchestrator └── Dockerfile # Dedicated non-root container definition ``` --- ## πŸš€ Quickstart ### Prerequisites - Python 3.12+ - [uv](https://docs.astral.sh/uv/) package manager ### Local Setup 1. **Install dependencies**: ```bash uv sync ``` 2. **List available collectors**: ```bash uv run python runner.py --list ``` 3. **Run a single crawl cycle**: ```bash uv run python runner.py --once ``` --- ## πŸ› οΈ Configuration & Source Authoring ### Declarative TOML Sources Place zero-code TOML configuration cards under `sources/{province}/{site_id}.toml`: ```toml [site] id = "yn.yuxi" name = "ηŽ‰ζΊͺεΈ‚δΊΊζ°‘ζ”ΏεΊœ" province = "δΊ‘ε—ηœ" city = "ηŽ‰ζΊͺεΈ‚" employer = "ηŽ‰ζΊͺ市相关用人单位" employer_type = "δΊ‹δΈšε•δ½" allowed_domains = ["yuxi.gov.cn", "yxrc.cn"] [template] item_selector = "div.news-list a" url_pattern = "/article/\\d+\\.html" title_selector = "h1.article-title" body_selector = "div.article-content" date_regex = "(\\d{4}[-/εΉ΄]\\d{1,2}[-/月]\\d{1,2})" attachment_extensions = [".xlsx", ".xls", ".pdf", ".docx", ".doc", ".zip"] [[channels]] id = "rszk" name = "人事招考" start_url = "https://www.yuxi.gov.cn/rszk.html" keywords = ["ζ‹›θ˜", "考试", "录用", "ε…¬η€Ί"] ``` ### Custom Python Adapters When target sites require complex session handling, cryptographic requests, or specific payload extraction, create a Python scraper under `adapters/{province}/{name}.py`: - Implement a crawl function returning an `IngestPayload`. - Use `common.security.is_safe_url` to enforce domain isolation. - Sanitize extracted text with `common.content.sanitize_body_text`. --- ## βš™οΈ Scheduling & Rate Limiting The runner (`runner.py`) provides fine-grained scheduling controls: | Environment Variable / Flag | Default | Description | |-----------------------------|---------|-------------| | `CORE_INGEST_URL` | `http://core:8080/api/v1/ingest` | Target Rust gateway ingestion endpoint | | `INGEST_TOKEN` | *None* | Bearer authentication token matching core `.env` | | `SCHEDULE_INTERVAL_HOURS` | `6` | Cycle interval between automated sweeps | | `--provinces` | `[]` | Filter target provinces (e.g. `yn,sc`) | | `--concurrency` | `3` | Maximum concurrent distinct domain scrapers | --- ## πŸ§ͺ Testing Comprehensive test suites covering SSRF prevention, HTML sanitization, and URL security: ```bash uv run python tests/test_content_and_security.py # Expected output: All security and content tests passed! ``` --- ## πŸ“œ License [MIT](../LICENSE)

(back to top)