Python으로 포럼 스크래핑을 하면서 Anti‑Bot Measures를 유발하지 않기

발행: (2025년 12월 23일 오전 10:03 GMT+9)
11 분 소요
원문: Dev.to

Source: Dev.to

위에 제공된 텍스트 외에 번역할 내용이 없습니다. 번역이 필요한 전체 본문을 제공해 주시면 한국어로 번역해 드리겠습니다.

Source:

포럼 스크래핑, 플래그되지 않게

나는 수년간 포럼의 틈새를 뒤져 왔습니다—오래되고 잊힌 포럼도, 살짝만 호기심을 보이면 캡차를 내뱉는 최신 보드도, 아카이브에만 살아 있는 죽은 커뮤니티도, PHPBB 흔적, vBulletin 유령, 그리고 목을 조이는 Cloudflare도. 이들 모두가 공통적으로 원하는 것은 누군가가 들여다보고 있다는 사실을 아는 것입니다, 설령 단순히 읽기 위해서라도.

핵심 아이디어

인간처럼 스크랩하라.
지루하고, 반복적이며, 약간은 산만한. 인간이지만, 아무도 눈치채지 못하는 그런 인간.


1. 먼저 수동 탐색하기

  1. 일반 브라우저로 포럼을 연다.
  2. 클릭하고, 스크롤하고, 페이지를 넘기고, 사용자 프로필을 본다.
  3. DevTools → Network 를 열고 스레드를 새로 고친다.
  4. 관찰한다:
    • 어떤 요청이 발생하고 어떤 요청이 발생하지 않는가?
    • 헤더에 회전 토큰이 있는가?
    • 쿠키가 첫 페이지 이후에만 나타나는가?
    • 숨겨진 POST 요청이 있는가?
  5. 모든 관찰 내용을 적어두라—코드도, 추론도 없이. 안티봇 시스템은 패턴 매처이다; 당신의 임무는 그들이 기대하는 패턴을 피하는 것이다.

2. 지속적인 세션 사용하기

import requests

session = requests.Session()
session.headers.update({
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
    "Accept-Language": "en-US,en;q=0.9",
    "Accept": "text/html,application/xhtml+xml",
    "Connection": "keep-alive"
})

# 첫 번째 요청 = 핸드셰이크 (쿠키 설정 등)
session.get("https://exampleforum.com/")

사용자 에이전트를 정하고 실행 중에 절대 바꾸지 말라. 인간은 몇 분마다 브라우저를 바꾸지 않는다.

3. 인간 같은 일시정지

import time
import random

def human_pause(base: float = 3) -> None:
    """인간처럼 느껴지는 무작위 간격으로 잠시 멈춘다."""
    time.sleep(base + random.uniform(0.5, 2.5))
  • 의미 있는 각 요청 사이에 human_pause() 를 호출한다.
  • 수백 개의 스레드를 스크랩한다면 작업이 몇 분이 아니라 몇 시간 걸릴 것을 예상하라.

4. URL 순서 무작위화

import random

thread_urls = list(collected_threads)   # 미리 수집한 URL 집합
random.shuffle(thread_urls)            # 순차적으로가 아니라 뒤섞는다

인간은 스레드 7 → 스레드 2 → 사용자 프로필 → 인덱스로 돌아가는 식으로 이동한다. 그 행동을 흉내 내라.

5. 파싱 전 지연

response = session.get(url)
human_pause()                     # DOM을 건드리기 **전** 일시정지
soup = BeautifulSoup(response.text, "html.parser")

즉시 파싱하는 것은 명백한 신호다. 짧은 지연은 스크래퍼를 더 “생각하는” 것처럼 보이게 만든다.

6. (선택) Selenium / Playwright

  • 포럼이 JavaScript에 크게 의존할 때만 사용한다.
  • 헤드리스 모드를 비활성화하고, 현실적인 창 크기를 설정하고, 동작 사이에 human_pause() 를 추가한다.
  • 대부분의 고전 포럼은 순수 HTML이므로 requests + BeautifulSoup 만으로 충분하다.

7. robots.txt 존중하기

법은 아니지만, 사이트가 어느 페이지를 천천히 크롤링하길 기대하는지 알려준다.

  • 관대함 → “느리고 지루한 사용자”처럼 대한다.
  • 제한적 → 더 엄격한 모니터링을 가정하고, 더욱 조심한다.

8. 소프트 블록 감지

전형적인 징후:

  • 빈 응답
  • 로그인 리다이렉트
  • 숨겨진 캡차 HTML
  • HTTP 200이지만 의심스러울 정도로 짧은 본문
if "captcha" in response.text.lower():
    raise RuntimeError("Soft blocked")

블록에 걸렸을 때:

  1. 일시정지 (몇 분에서 몇 시간).
  2. IP나 사용자 에이전트를 공격적으로 회전시키지 않는다.
  3. 나중에 더 느린 속도로 재개한다.

9. 무거운 엔드포인트 피하기

  • 검색 엔드포인트는 강력히 감시되므로 “인간 전용”으로 취급한다.
  • 카테고리 페이지, 인덱스, 최신 스레드 목록에 머무른다.

10. 데이터베이스에 상태 지속하기

import sqlite3

conn = sqlite3.connect("forum.db")
c = conn.cursor()
c.execute('''
CREATE TABLE IF NOT EXISTS posts (
    thread_id TEXT,
    post_id   TEXT,
    content   TEXT,
    timestamp TEXT
)
''')
conn.commit()
  • 각 포스트를 저장하고…

Source:

11. Vary Your Schedule

  • 크론 작업처럼 매일 새벽 02:00 AM에 스크래핑하지 마세요.
  • 무작위로 며칠을 건너뛰고, 시작 시간을 바꾸며, 추가 일시 정지를 삽입하세요.
  • 사람들은 불규칙한 브라우징 패턴을 가지고 있습니다.

12. Trim the Payload

  • 아바타, 서명, 배지는 필요하지 않은 한 무시하세요.
  • 이미지 다운로드를 피하세요 – 대역폭을 늘리고 의심을 불러일으킵니다.
  • 일반적으로 스레드의 처음 몇 페이지만 유용한 토론을 포함합니다.

13. Authenticated Scraping (If Needed)

  • 하나의 로그인 세션만 사용하세요; 인증된 상태에서는 IP나 User‑Agent를 회전시키지 마세요.
  • 계정의 속도 제한을 준수하세요 – 게스트보다 훨씬 느리게 동작해야 합니다.

14. A Minimal Thread Scraper Example

from bs4 import BeautifulSoup

def scrape_thread(url: str):
    response = session.get(url)
    human_pause()

    soup = BeautifulSoup(response.text, "html.parser")
    posts = soup.select(".post")          # adjust selector to the forum's markup

    data = []
    for post in posts:
        post_id   = post.get("data-post-id")
        content   = post.select_one(".content").get_text(strip=True)
        timestamp = post.select_one(".date").get_text(strip=True)

        data.append((url, post_id, content, timestamp))

        # Store immediately
        c.execute(
            "INSERT INTO posts (thread_id, post_id, content, timestamp) VALUES (?,?,?,?)",
            (url, post_id, content, timestamp)
        )
    conn.commit()
    return data

Source:

TL;DR

  1. 코드를 작성하기 전에 직접 관찰하세요.
  2. 안정적인 사용자‑에이전트를 사용해 단일 지속 세션을 유지하세요.
  3. 인간처럼 일시정지를 곳곳에 삽입하세요.
  4. URL 순서와 탐색 패턴을 무작위화하세요.
  5. 진행하면서 SQLite(또는 다른 DB)로 데이터를 지속하세요.
  6. robots.txt를 준수하고, 검색을 피하며, 스케줄을 다양하게 하세요.
  7. 차단당하면 속도를 늦추세요; 즉시 확대하지 마세요.
content = post.select_one(".content")
if content:
    data.append(content.get_text(strip=True))

return data

빠진 점을 보세요: 동시성, 재시도, 속도 해킹이 없습니다. 그것들은 나중에, 어쩌면 절대 나오지 않을 수도 있습니다.
모든 것을 제대로 해도 이런 일은 일어납니다.

즉시 확대하지 마세요. 타이밍 외에는 아무 것도 바꾸지 마세요. 더 오래 기다리고, 범위를 줄이며, 완전히 일시정지하세요. IP나 에이전트를 회전시키면 더 눈에 띕니다. 때때로 올바른 선택은 지루함입니다.

안티‑봇 시스템은 똑똑하지 않습니다—오히려 불안합니다. 속도, 규칙성, 양, 지속성을 찾습니다. 그 신호들을 없애면, 당신은 인간들의 무한 스크롤 소음 속에 사라집니다.

목표는 숨김이 아니라 무시당함입니다. 조용히, 천천히, 누구에게도 귀찮게 하지 않게.

포럼을 스크래핑하는 것은 기술적 장벽을 깨는 것이 아니라, 관심 없는 척하는 시스템에 대한 사회공학입니다. 중요하지 않은 사람처럼 움직이면 혼자 남게 됩니다. 관찰하고, 멈추고, 섞고, 읽고, 기다리고, 반복하세요.

이것이 파이썬으로 포럼을 스크래핑하면서 안티‑봇 조치를 절대 트리거하지 않는 방법입니다. 천천히. 조용히. 인내심 있게. 결코 끝내지 못할 것을 알면서도, 그 여정 자체가 목적이기에 개의치 않는 사람의 인내심으로.

Back to Blog

관련 글

더 보기 »