Skip to main content
Glama
BaektotheFuture98

Python Crawling MCP

Python Crawling MCP

Python Crawling MCP는 MCP Client가 요청한 공개 페이지와 사전에 등록된 인증 사이트를 안전하게 수집하는 STDIO 서버입니다. 임의 사이트의 로그인 방법을 추측하지 않으며, 지원 사이트마다 로그인·세션 검증·페이지 분류·추출 규칙을 Adapter로 등록합니다.

잠금 파일 기준 주요 버전은 Python 3.12, MCP Python SDK 1.29.0, Crawlee 1.9.0, Playwright 1.62.0, Pydantic 2.13.4입니다.

아키텍처

FastMCP Adapter
    ↓ 입력 검증, Application Service 호출, 응답 직렬화
CrawlService ── AuthService ── MonitoringService ── ArticlePersistenceService
    ↓ Protocol ports
CrawlerEngine / PageExtractor / ArticleExtractor / focused Repository ports
    ↑
Crawlee HTTP / shared Playwright browser / validated egress proxy / PostgreSQL

MCP Tool은 Playwright와 Crawlee를 직접 사용하지 않습니다. CrawlService는 Protocol에만 의존하므로 같은 로직을 CLI, REST API 또는 Worker Adapter에서 재사용할 수 있습니다. 서버 lifespan이 Playwright와 Chromium을 한 번 시작하고, 작업별 BrowserContext로 세션을 격리합니다.

지속 크롤링은 Hermes가 MCP를 polling하는 대신 별도 Python Worker가 담당합니다.

Hermes -> MCP query/command tools -> MonitoringQueryService -> PostgreSQL

Crawler Worker -> MonitoringService -> CrawlService -> Adaptive Engine
                                      -> HTTP / shared Playwright
               -> ArticleExtractor -> ArticleCandidate
               -> ArticlePersistenceService -> ARTICLE + crawler state

반복 가능한 수집·비교·저장은 Python 코드에서 수행하고, Hermes는 변경이 발생한 뒤 분석·요약·판단이 필요할 때만 호출합니다.

Related MCP server: PlayMCP Browser Automation Server

디렉터리

src/crawling_mcp/
├── domain/           # 모델, enum, 오류, URL·링크 정책
├── application/      # CrawlService, AuthService, Monitoring/ArticlePersistence service
├── ports/            # crawler/auth/extractor/repository/browser/network/robots Protocol
├── adapters/
│   ├── mcp/          # 네 MCP Tool
│   ├── worker/       # polling scheduler와 graceful runner
│   ├── crawlee/      # HTTP/browser/adaptive engine, factory, router
│   ├── auth/         # registry, no-auth, saved session, example login
│   ├── extractors/   # 범용 PageItem extractor와 registry
│   ├── article_extractors/ # 기사 전용 structured/site adapter
│   └── storage/      # memory/file 및 PostgreSQL repository
├── infrastructure/   # 설정, SSRF, egress proxy, robots, logging, browser, artifacts
├── bootstrap.py      # dependency composition root
├── server.py         # FastMCP lifespan
└── test_site.py      # 개발 전용 로그인 사이트

최종 기사는 기존 PostgreSQL ARTICLE에만 저장하고, target/state/run은 별도 테이블에 저장합니다. PageItem은 범용 DTO이며 기사 필드를 포함하지 않습니다. FileRepository는 기존 수동 크롤링 호환성을 위해 유지하지만 Worker는 범용 CrawlResult 저장을 끄므로 기사 본문이 JSON에 중복되지 않습니다.

로컬 설치와 실행

Python 3.12 이상과 uv가 필요합니다.

uv sync
uv run playwright install chromium
cp .env.example .env
uv run alembic upgrade head
uv run python -m crawling_mcp

Worker는 별도 프로세스로 실행합니다.

uv run python -m crawling_mcp.worker

Worker는 MCP를 호출하지 않고 MonitoringService -> CrawlService를 직접 호출합니다. 지속 크롤링 Worker는 CRAWLING_MCP_REPOSITORY=postgres에서만 시작됩니다.

서버는 STDIO transport를 사용합니다. stdout은 MCP protocol 전용이고 구조화 로그는 stderr로 출력됩니다.

개발용 로그인 사이트는 별도 터미널에서 실행합니다.

uv run python -m crawling_mcp.test_site

개발 계정은 test-user / test-password입니다. 이 값은 로컬 테스트 전용이며 운영 계정으로 사용하면 안 됩니다.

Docker 실행

.env를 만든 후 다음을 실행합니다.

cp .env.example .env
docker compose up -d

기본 Compose는 개발용 PostgreSQL 18, 기존 ARTICLE 계약을 만드는 초기화 SQL, Alembic migration, MCP Server, Crawler Worker를 순서대로 시작합니다. 운영에서는 기존 PostgreSQL의 DATABASE_URL 또는 CRAWLING_MCP_POSTGRES_DSN을 사용합니다. Alembic migration 자체는 ARTICLE을 생성·삭제하지 않으며 MinIO도 사용하지 않습니다.

기본 Compose는 사설망 접근을 차단하며 테스트 사이트를 시작하지 않습니다. 로컬 Docker 인증 예제는 명시적인 test override와 profile로 실행합니다.

docker compose -f docker-compose.yml -f docker-compose.test.yml --profile test up -d test-site
docker compose -f docker-compose.yml -f docker-compose.test.yml --profile test run --rm -T mcp-server

test override는 private-network 허용과 domain allowlist를 test-site 하나로 함께 제한합니다. 인터넷 대상 운영 배포에서는 기본 CRAWLING_MCP_ALLOW_PRIVATE_NETWORKS=false를 유지하십시오. 인증, 결과, 실패, 스크린샷 디렉터리는 각각 별도 named volume입니다. 이미지는 비-root crawling 사용자로 실행됩니다.

MCP Client 연결

Codex, Claude Desktop 등 STDIO MCP Client에서는 절대 경로를 사용합니다.

{
  "mcpServers": {
    "python-crawling-mcp": {
      "command": "uv",
      "args": [
        "--directory",
        "/absolute/path/python-crawling-mcp",
        "run",
        "python",
        "-m",
        "crawling_mcp"
      ]
    }
  }
}

MCP Tools

MCP에는 기존 수동 크롤링 Tool과 지속 크롤링용 고수준 Tool이 함께 등록됩니다.

scrape_page

{
  "url": "https://example.com/page",
  "crawl_mode": "auto",
  "auth_profile": null
}

성공 결과에는 url, title, content, metadata, meta_description, canonical_url, language, http_status_code, collected_at이 포함됩니다.

crawl_site

{
  "start_url": "https://example.com",
  "crawl_mode": "auto",
  "auth_profile": null,
  "max_pages": 20,
  "max_depth": 2,
  "include_patterns": [],
  "exclude_patterns": [],
  "same_domain_only": true,
  "max_request_retries": 2,
  "request_timeout_seconds": 30,
  "job_timeout_seconds": 300,
  "max_concurrency": 3,
  "respect_robots_txt": true,
  "request_delay_seconds": 0.5,
  "remove_tracking_parameters": true
}

결과에는 job ID, 방문/성공/실패 수, items, failures, 시작/완료 시각이 포함됩니다. URL fragment와 선택한 tracking parameter를 제거한 URL이 중복 키로 사용됩니다.

validate_session

{"auth_profile": "example-reader"}

저장된 Playwright storage state를 격리된 context에 로드한 뒤 사이트 Adapter의 실제 인증 표식을 확인합니다. 만료 세션을 자동 갱신하지는 않으며 scrape_page 또는 crawl_site 호출 때 재로그인합니다.

list_supported_sites

등록된 domain, authentication Adapter 이름, extractor 이름만 반환합니다. 환경변수 이름, storage path와 secret은 반환하지 않습니다.

configure_crawl_target

target ID 없이 호출하면 생성하고, ID가 있으면 전달한 필드만 수정합니다.

{
  "url": "https://example.com/news",
  "interval_seconds": 300,
  "enabled": true,
  "crawl_mode": "auto",
  "max_pages": 20,
  "max_depth": 2
}

list_crawl_targets

{"enabled": true, "limit": 50}

설정과 스케줄 상태만 반환하며 기사 본문은 반환하지 않습니다.

run_crawl_target

{"target_id": "0198..."}

등록 target을 즉시 한 번 실행하고 visited/new/updated/unchanged/failed 집계만 반환합니다.

get_crawl_status

{"target_id": "0198...", "limit": 20}

최근 Worker job 집계를 반환합니다. target ID를 생략하면 여러 target의 최신 상태를 반환합니다.

get_recent_article_changes

{"target_id": "0198...", "limit": 20}

article_id, URL, change type, 제목, publisher, 작성시간, 마지막 변경시간만 반환합니다. ar_content는 포함하지 않습니다.

get_article

{"article_id": "0198..."}

Hermes가 실제 분석 대상으로 선택한 단일 ARTICLE의 ar_content를 반환합니다.

지속 크롤링 흐름

  1. Worker가 FOR UPDATE SKIP LOCKED로 due target을 한 건씩 실행 직전에 claim하고, 실행 중 lease를 주기의 1/3 간격으로 갱신합니다.

  2. MonitoringService가 ARTICLE_CRAWL_STATE의 ETag/Last-Modified와 알려진 URL을 준비합니다.

  3. 기존 CrawlService가 HTTP 우선, 필요한 경우에만 Browser fallback으로 수집합니다.

  4. HTTP 304는 parsing, extractor, hash, Browser fallback을 모두 생략합니다.

  5. ArticleExtractor가 각 페이지 콜백에서 JSON-LD → OpenGraph → semantic article 순서로 ArticleCandidate를 만들고 즉시 저장합니다. 모니터링 경로는 전체 페이지 본문이나 CrawlResult.items를 메모리에 누적하지 않습니다.

  6. 제목·본문·기자·발행사·작성시간·canonical URL을 정규화하고 SHA-256으로 비교합니다.

  7. canonical URL이 같은 기사는 target과 무관하게 하나의 ARTICLE을 재사용합니다. NEW는 동시 실행에도 멱등 INSERT, UPDATED는 ARTICLE UPDATE, UNCHANGED는 ARTICLE을 수정하지 않습니다.

  8. 모든 관찰은 target별 (target_id, article_id) ARTICLE_CRAWL_STATE의 last seen/validator를 독립적으로 갱신합니다.

  9. 현재 lease owner만 target과 CRAWL_RUN을 완료할 수 있습니다. Worker가 중단돼 남은 RUNNING 기록은 다음 claim에서 lease_expired 실패로 복구합니다.

  10. 성공 시 다음 실행시간을, 실패 시 retry/backoff를 저장합니다.

한 target의 timeout이나 오류는 다른 target 실행을 중단하지 않습니다. lease가 만료되거나 다른 Worker에 넘어간 오래된 실행은 새 소유자의 상태를 덮어쓸 수 없습니다. Worker 종료 시 SIGINT/SIGTERM을 받아 현재 polling loop를 정리하고 Browser/PostgreSQL lifecycle을 닫습니다.

PostgreSQL schema

  • ARTICLE: 기존 계약 그대로 id, ar_title, ar_content, reporter, publisher, url, published_at

  • CRAWL_TARGET: URL, interval, enabled, mode/auth/crawl 옵션, 실행·retry·lease 상태

  • ARTICLE_CRAWL_STATE: (target_id, article_id) 복합 기본 키, target별 canonical URL·content hash·HTTP validator·first/last seen·마지막 의미 있는 변경

  • CRAWL_RUN: target별 실행 상태와 visited/new/updated/unchanged/failed 집계

ARTICLE, target, run ID는 PostgreSQL 18 DEFAULT uuidv7()가 생성합니다. Adapter INSERT는 ID를 전달하지 않고 RETURNING으로 결과만 받습니다. Migration은 기존 ARTICLE의 컬럼/default를 검증하고 중복 URL이 있으면 삭제하지 않은 채 해결 방법을 알리는 오류로 중단합니다. ARTICLE에 crawler 전용 컬럼을 추가하지 않으며 snapshot/history/raw article 테이블도 만들지 않습니다.

DELETED는 첫 버전에서 저장하지 않습니다. bounded crawl이나 부분 실패로 방문하지 못한 페이지를 실제 삭제로 오판할 수 있기 때문입니다.

Hermes 사용 방식

권장 흐름은 다음과 같습니다.

  1. 최초 한 번 configure_crawl_target으로 감시 대상을 등록합니다.

  2. 일반적인 주기 실행은 Worker에 맡기고 Hermes가 crawl_site를 polling하지 않습니다.

  3. 분석이 필요할 때 get_crawl_status 또는 get_recent_article_changes를 호출합니다.

  4. 관심 있는 article만 get_article로 가져와 요약·판단합니다.

  5. 긴급 재수집이 필요할 때만 run_crawl_target을 호출합니다.

기존 방식은 매 주기마다 Agent 실행, MCP 호출, 전체 CrawlResult context 전송이 발생했습니다. 새 방식은 변경이 없어도 Python Worker와 HTTP 304/작은 DB update만 수행합니다. LLM context에는 작은 change summary만 들어가고 선택한 본문만 상세 조회하므로 호출 횟수와 token 사용량이 함께 감소합니다.

향후 분산 확장

각 Worker의 scheduler는 단일 polling loop지만 여러 Worker가 동시에 실행돼도 PostgreSQL lease, heartbeat, owner fencing과 SKIP LOCKED로 target을 안전하게 분배합니다. 규모가 커지면 다음 경계만 교체합니다.

  • adapters/worker/scheduler.py: PostgreSQL polling을 Kafka/queue consumer로 교체

  • TargetRepository.claim_due: scheduler producer 또는 dispatcher로 이동

  • lease 컬럼: queue visibility timeout과 기존 heartbeat/fencing 계약을 결합

  • MonitoringService: 기존 target-scoped idempotency와 lease owner 계약을 유지

  • MonitoringUnitOfWork: outbox table을 추가해 ARTICLE/state commit과 Kafka publish를 원자화

CrawlerEngine, CrawlService, ArticleExtractor, ArticlePersistenceService, MCP query adapter는 분산 Worker 전환 시 변경하지 않습니다.

인증 프로필 등록

config/auth_profiles.yaml에는 secret이 아닌 참조만 저장합니다.

profiles:
  example-reader:
    domain: example.com
    adapter: example_login
    username_env: EXAMPLE_USERNAME
    password_env: EXAMPLE_PASSWORD
    storage_state_path: data/auth/example-reader.json

.env 또는 Secret Provider가 참조된 환경변수를 제공합니다.

EXAMPLE_USERNAME=reader
EXAMPLE_PASSWORD=replace-me

MCP 요청으로 사용자명이나 비밀번호를 전달하지 마십시오. storage state 경로는 data/auth 아래 JSON 파일만 허용하며 path traversal과 root 탈출을 거부합니다.

새 Login Adapter 추가

  1. AuthenticationAdapter Protocol의 is_authenticatedauthenticate를 구현합니다.

  2. role, label, placeholder, 안정된 name/id/data-testid, CSS 순으로 Locator 후보를 둡니다.

  3. 후보는 count 1, visible, enabled 조건을 모두 확인합니다.

  4. 클릭 완료가 아니라 보호 URL, 로그아웃 버튼, 프로필 또는 인증 API로 성공을 검증합니다.

  5. bootstrap.pyAuthRegistry에 exact domain과 Adapter를 등록합니다.

  6. profile YAML에는 secret 값 대신 환경변수 이름만 추가합니다.

  7. 로그인, 저장 세션 재사용, 만료 후 재로그인, locator 변경 실패 테스트를 추가합니다.

ExampleLoginAdapter가 이 흐름의 실행 가능한 예제입니다.

새 Extractor 추가

  1. PageExtractor Protocol의 name과 비동기 extract를 구현합니다.

  2. engine-neutral PageSnapshot에서 PageItem 목록을 반환합니다.

  3. bootstrap.pyExtractorRegistry에 domain과 함께 등록합니다.

  4. 필요하면 PageRouter 또는 사이트 classifier로 START, LIST, DETAIL을 구분합니다.

  5. 실제 HTML fixture로 제거 태그, 메타데이터, 구조화 필드를 테스트합니다.

미등록 공개 사이트는 GenericExtractor를 사용합니다. 인증 profile이 지정된 미등록 사이트는 명확한 UNSUPPORTED_SITE 오류를 반환합니다.

테스트와 품질 검사

uv run ruff check .
uv run ruff format --check .
uv run mypy src
uv run pytest
uv run pytest -m integration
CRAWLING_MCP_RUN_STORAGE_INTEGRATION=1 uv run pytest -m integration tests/integration/test_postgres_monitoring.py

기본 pytest는 빠른 단위 테스트만 실행합니다. integration marker는 로컬 FastAPI 포트와 Chromium을 사용하며 공개 수집, 인증, storage-state 재사용, 만료 후 재로그인, 목록·상세 탐색, 실패 처리를 검증합니다.

실패 파일

브라우저 navigation 또는 extraction 실패 시 가능한 범위에서 다음을 저장합니다.

data/failures/{job_id}/
├── error.json
├── page.html
├── screenshot.png
└── accessibility_snapshot.txt

error.json은 traceback을 포함하지 않으며 민감 key를 재귀적으로 마스킹합니다. HTML의 password/token input value도 저장 전에 제거합니다. 아티팩트 저장 실패는 원래 도메인 오류를 덮어쓰지 않습니다.

보안

  • HTTP/HTTPS만 허용하며 URL user-info, file, ftp, data scheme을 거부합니다.

  • hostname을 IDNA로 정규화한 뒤 모든 A/AAAA 응답을 검사합니다.

  • loopback, private, link-local, unspecified, multicast, reserved 및 metadata IP를 기본 차단합니다.

  • DNS 응답 중 하나라도 차단 주소면 전체 요청을 거부합니다.

  • 응답 HTML은 운영자 설정 CRAWLING_MCP_MAX_CONTENT_BYTES 상한을 넘으면 파싱·추출을 중단합니다.

  • 페이지별 발견 링크 수, DNS 답변 수, egress 연결 시간과 연결별 수신 byte에도 운영자 상한을 적용합니다.

  • 최초 URL, 발견 링크, navigation 직전과 redirect 최종 URL을 다시 검증합니다.

  • HTTP와 Chromium 트래픽은 loopback egress proxy를 통과하며, proxy가 검증된 정확한 IP로 연결합니다. redirect와 iframe·이미지·스크립트 같은 하위 리소스도 같은 정책을 적용받습니다.

  • 선택적 domain allowlist는 private-IP 차단을 우회하지 않습니다.

  • password, cookie, Authorization, token, storage state는 로그에서 마스킹됩니다.

  • robots.txt 준수는 기본 활성화지만 사이트 이용약관과 법적 권한 검토를 대신하지 않습니다.

현재 한계

  • CAPTCHA, MFA, SSO, device approval을 우회하지 않습니다.

  • 임의 사이트 로그인 form을 자동 추측하지 않습니다.

  • Adaptive HTTP→browser 전환은 본문 길이·JS shell·login redirect 휴리스틱입니다.

  • Browser traversal은 작업별 context 안에서 bounded worker queue를 사용하며 max_concurrency, 사이트별 요청 간격, robots.txt를 함께 적용합니다.

  • FileRepository는 기존 수동 크롤링용 단일 호스트 Adapter입니다. 지속 Worker는 PostgreSQL을 요구합니다.

  • scheduler는 현재 단일 polling 프로세스이며 Kafka/outbox는 아직 포함하지 않습니다.

  • 운영 사설망 crawling은 기본 제공하지 않습니다. allow_private_networks는 로컬 통합 테스트 또는 격리된 테스트 Compose에서만 사용하십시오.

F
license - not found
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    B
    quality
    Not graded
    maintenance
    A MCP server that provides browser automation tools, allowing users to navigate websites, take screenshots, click elements, fill forms, and execute JavaScript through Playwright.
    8
    2
  • A
    license
    Not graded
    quality
    D
    maintenance
    An MCP server that enables AI-powered browser automation, web scraping, and testing using Playwright across Chromium, Firefox, and WebKit. It allows users to perform actions like navigation, clicking, typing, and taking screenshots through natural language interfaces.
    10
    MIT
  • F
    license
    B
    quality
    B
    maintenance
    An MCP server for generic browser automation using Playwright. Enables MCP clients to navigate pages, inspect elements, execute JavaScript, capture screenshots, and monitor console logs and network traffic via a headless Chromium instance.
    7

View all related MCP servers

Related MCP Connectors

  • All HasData scraping tools in one MCP server: Google, TikTok, Instagram, maps, e-commerce and more.

  • Crawlbase MCP — wraps the Crawlbase Crawling API (crawlbase.com, formerly

  • Free remote MCP server for fetching public web pages through a rotating proxy pool.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/BaektotheFuture98/Crawl_MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server