원클릭으로
data-collection
Parallel data collection from web sources, APIs, and documentation sites
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Parallel data collection from web sources, APIs, and documentation sites
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Delegate coding to OpenAI Codex CLI (features, PRs).
Configure, extend, or contribute to Hermes Agent.
Manage multiple remote servers from Hermes via SSH — deploy services, configure firewalls, transfer files, run commands across servers
Clone/create/fork repos; manage remotes, releases.
Deploy static sites to GitHub Pages via API — create repo, push, enable Pages, all from CLI
Build knowledge bases from collected data — clean raw files, generate search indexes, deploy GitHub Pages websites, and package as Hermes skills. Covers the full pipeline from raw READMEs/docs to searchable open-source knowledge bases.
| name | data-collection |
| description | Parallel data collection from web sources, APIs, and documentation sites |
| version | 1.0.0 |
| author | Hermes Agent |
| license | MIT |
| platforms | ["linux"] |
| metadata | {"hermes":{"tags":["data-collection","web-scraping","parallel-processing","python","multiprocessing"],"related_skills":["web-tool-builder","flask-web-tools","coding-principles"]}} |
| triggers | ["data collection","web scraping","crawl","采集","爬虫","parallel collection"] |
Reusable patterns for collecting data from web sources, APIs, and documentation sites. Optimized for multi-core servers.
When to use: Collecting from multiple independent sources simultaneously.
Key insight: Use multiprocessing.Process (not threads) for true parallelism on multi-core servers. Each process maintains its own state file, with a shared counter for progress tracking.
from multiprocessing import Process, Manager, Lock, Value
import signal, sys, json
from pathlib import Path
# Shared counter (atomic via Lock)
shared_counter = Value('i', 0)
lock = Lock()
stop_event = multiprocessing.Event()
# Each process has its own seen-state file
def init_process_seen(process_name):
seen_file = BASE_DIR / f"seen_{process_name}.json"
if seen_file.exists():
data = json.loads(seen_file.read_text())
return set(data.get("urls", [])), set(data.get("contents", []))
return set(), set()
def save_process_seen(process_name, seen_urls, seen_contents):
seen_file = BASE_DIR / f"seen_{process_name}.json"
data = {"urls": list(seen_urls), "contents": list(seen_contents)}
seen_file.write_text(json.dumps(data, indent=2))
# Process function
def process_source(shared_counter, lock, stop_event):
process_name = "source_name"
seen_urls, seen_contents = init_process_seen(process_name)
session = create_session()
while not stop_event.is_set():
# Collect data...
with lock:
shared_counter.value += 1
# Periodic save
if shared_counter.value % 50 == 0:
save_process_seen(process_name, seen_urls, seen_contents)
# Sleep with interrupt check
for _ in range(interval):
if stop_event.is_set():
break
time.sleep(1)
# Signal handling for graceful shutdown
def signal_handler(signum, frame):
stop_event.set()
for p in processes:
p.join(timeout=30)
if p.is_alive():
p.terminate()
sys.exit(0)
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
Manager().list() returns ListProxy which doesn't have .add() methodManager().set() works but adds IPC overhead for every operationrequests + gzip: Don't Manually DecompressProblem: Manual gzip.decompress(resp.content) fails when server sends plain JSON with Content-Encoding: gzip header.
Solution: Use resp.text — requests handles decompression automatically.
# ❌ WRONG - crashes on plain JSON responses
if resp.headers.get("Content-Encoding") == "gzip":
content = gzip.decompress(resp.content).decode("utf-8")
else:
content = resp.text
# ✅ CORRECT - requests handles it
content = resp.text
Why it happens: Some servers (like GitHub API) send Content-Encoding: gzip header but the actual content is plain JSON. requests transparently decompresses when using .text, but .content returns raw bytes.
multiprocessing.Manager().list() is ListProxyProblem: Manager().list() returns a ListProxy object, not a real list.
# ❌ WRONG - ListProxy has no .add()
shared_urls = manager.list()
shared_urls.add(url) # AttributeError!
# ✅ CORRECT - use per-process state or Manager().dict()
# Option A: Per-process state (recommended for high-throughput)
# Option B: Manager().dict() with manual set operations
shared_urls = manager.dict()
shared_urls[url_hash] = True
Problem: Fixed URL list + hash-based dedup = first round collects everything, subsequent rounds do nothing.
Solution:
# Extract repo links from trending page
repos = re.findall(r'href=["\'](/[^/]+/[^"\']+)["\']', content)
repos = [r for r in repos if r.count("/") == 2 and not r.startswith("/trending")]
# Then fetch README from raw.githubusercontent.com
# Search repositories by topic
url = f"https://api.github.com/search/repositories?q={topic}&sort=stars&per_page=10"
# Rate limit: 10 requests/minute for unauthenticated, 30 for authenticated
# Extract article links with date patterns
article_patterns = [
r'/\d{4}/\d{2}/', # Date format
r'/blog/', r'/article/', r'/post/', r'/tutorial/',
]
# Scan collected files for new links
all_links = set()
for doc_file in collected_dir.glob("*.md"):
content = doc_file.read_text()
links = extract_links(content, "")
all_links.update(links)
multi-process-scraper — More complete reference with watchdog, size monitoring, scaling guide, and pitfalls