페이스북 공개 데이터 스크래핑은 가능하지만, 점점 더 어려워지고 있습니다. 메타(Meta)는 2023년 Meta v. Bright Data 소송에서 스크래핑 업체를 상대로 영구 금지 명령을 받아냈고, 이는 업계에 명확한 신호를 보냈습니다. 인증 없이 접근 가능한 공개 데이터라도, 메타의 서비스 약관(Terms of Service) 위반이 될 수 있으며, 미국에서는 CFAA(Computer Fraud and Abuse Act), EU에서는 GDPR이 적용될 수 있습니다.
⚠️ 법적 고지: 본 가이드는 합법적으로 접근 가능한 공개 정보에 한정합니다. 메타의 서비스 약관을 위반하는 스크래핑은 계정 정지 및 법적 조치의 대상이 될 수 있습니다. CFAA(미국), GDPR(유럽) 등 관할 법역의 법률을 반드시 준수하세요. 로그인 자동화, 비공개 데이터 접근, 대량 수집으로 인한 서비스 장애는 절대 피해야 합니다.
페이스북에서 진정으로 "공개"인 데이터란?
페이스북 공개 데이터 스크래핑을 논하기 전에, 인증 없이 브라우저에서 접근 가능한 데이터가 무엇인지 명확히 해야 합니다. 메타는 점점 더 많은 콘텐츠를 로그인 월(login wall) 뒤로 밀어 넣고 있으며, 과거에 공개였던 데이터가 지금은 아닐 수 있습니다.
인증 없이 접근 가능한 공개 데이터
- 공개 페이지(Public Page) 게시물: 기업, 공공기관, 유명인 페이지의 공개 게시물 및 기본 메타데이터
- 공개 그룹 목록: 그룹 이름, 멤버 수, 공개 설명 (단, 게시물 내용은 로그인 필요한 경우가 많음)
- 공개 이벤트 페이지: 이벤트 이름, 날짜, 장소, 주최자 정보
- Marketplace 목록 (일부 지역): 로그아웃 상태에서도 일부 리스팅이 보이는 지역이 존재
- 공개 프로필의 기본 정보: 이름, 프로필 사진, 커버 사진 (상세 정보는 로그인 필요)
더 이상 공개가 아닌 데이터
- 개인 프로필의 게시물, 친구 목록, 사진 앨범
- 비공개 또는 폐쇄 그룹의 모든 콘텐츠
- Marketplace의 판매자 연락처 및 상세 정보
- 댓글, 반응(좋아요 등), 공유 수의 세부 내역
메타는 2024년부터 공개 페이지 게시물에 대해서도 로그인 월을 점진적으로 확대하고 있습니다. 스크래핑을 기획할 때 현재 로그아웃 상태에서 실제로 무엇이 보이는지 직접 확인하는 것이 첫 번째 단계입니다.
메타의 봇 탐지 스택: 왜 단순 HTTP 요청으로는 불가능한가
페이스북 페이지 스크래핑에 단순 requests 호출이나 curl을 사용하면 거의 확실히 차단됩니다. 메타의 탐지 시스템은 다층적으로 설계되어 있습니다.
Akamai Bot Manager
메타는 Akamai의 봇 관리 솔루션을 사용하여 비인간 트래픽을 식별합니다. Akamai는 다음을 분석합니다:
- HTTP 헤더 순서와 정합성: 브라우저별로 헤더 순서가 다르며, 불일치 시 봇으로 간주
- TLS 핑거프린트(JA3/JA4): TLS 협상 과정의 암호 스위트, 확장 순서를 기반으로 클라이언트 식별
- HTTP/2 프레임 분석: SETTINGS, WINDOW_UPDATE 등 프레임의 기본값과 순서 비교
- 센서 데이터(Akamai Web SDK): 마우스 움직임, 키보드 입력 패턴, 디바이스 특성을 클라이언트 사이드에서 수집
행동 핑거프린팅(Behavioral Fingerprinting)
단순히 페이지를 방문하는 것만으로는 충분하지 않습니다. 메타는 사용자의 상호작용 패턴을 분석합니다:
- 페이지 로드 후 스크롤 패턴 — 선형 다운스크롤은 봇의 전형적 징후
- 클릭 간격의 규칙성 — 균일한 간격은 자동화의 신호
- 뷰포트 체류 시간 — 콘텐츠를 읽지 않고 즉시 이동하는 패턴
- 네트워크 요청의 동시성 — 인간은 한 번에 하나의 페이지에 집중
로그인 월(Login Wall)
가장 직관적인 방어입니다. 메타는 점점 더 많은 콘텐츠를 로그인 뒤에 배치합니다. 로그인 워커를 자동화하는 것은 CFAA 위반의 명백한 위험이며, Meta v. Bright Data 판결에서도 핵심 쟁점이었습니다. 본 가이드에서는 절대 로그인 자동화를 권장하지 않습니다.
왜 주거 프록시 + 브라우저 자동화만이 유일한 접근법인가
페이스북 공개 데이터 스크래핑에 있어서, 프록시와 자동화 도구의 선택은 취미가 아니라 생존 전략입니다.
데이터센터 프록시의 한계
데이터센터 IP는 Akamai의 IP 평판 데이터베이스에 즉시 식별됩니다. ASN(Autonomous System Number) 기반으로 호스팅/클라우드 대역인지 판별하며, 한 번 차단되면 해당 대역 전체가 블랙리스트에 오릅니다. 수천 개의 데이터센터 IP를 교체해도 며칠 안에 모두 차단됩니다.
주거 프록시가 필수인 이유
주거 프록시(residential proxies)는 실제 ISP에서 할당된 IP 주소를 사용하므로, Akamai의 IP 평판 검사를 통과합니다. 핵심 장점:
- ISP ASN: 호스팅 대역이 아닌 실제 통신사 ASN으로 인식
- 지역 다양성: 전 세계 다양한 도시/국가의 IP로 자연스러운 분산
- IP 회전: 요청별 또는 세션별 IP 교체로 속도 제한 우회
- 지역 타겟팅: 특정 국가/도시의 콘텐츠 접근 (예: 지역 한정 Marketplace)
왜 raw HTTP가 아닌 브라우저 자동화인가
Akamai의 센서 데이터 수집과 TLS 핑거프린팅을 통과하려면 실제 브라우저 엔진이 필요합니다. Playwright, Puppeteer와 같은 도구는 Chromium을 구동하므로, TLS 핸드셰이크와 HTTP/2 프레임이 실제 Chrome과 동일합니다. 반면 requests나 axios는 Python/Node.js의 TLS 스택을 사용하므로 즉시 탐지됩니다.
| 접근 방식 | TLS 핑거프린트 | 센서 데이터 | IP 평판 | 차단 확률 | 적합 여부 |
|---|---|---|---|---|---|
| Raw HTTP (requests/curl) | ❌ Python/Go TLS | ❌ 미지원 | ⚠️ 프록시에 따라 | 99%+ | 부적합 |
| 데이터센터 프록시 + 브라우저 | ✅ 실제 브라우저 | ⚠️ 부분 | ❌ 호스팅 ASN | 85%+ | 부적합 |
| 주거 프록시 + 브라우저 자동화 | ✅ 실제 브라우저 | ✅ 자연스러운 패턴 | ✅ ISP ASN | 10–30% | 유일한 실용적 선택 |
| Facebook Graph API | N/A (공식 API) | N/A | N/A | 0% (할당량 내) | ✅ 인증 데이터에 최적 |
Playwright + 주거 프록시로 공개 페이지 스크래핑하기
이제 실제로 작동하는 Playwright 예제를 살펴보겠습니다. 페이스북 주거 프록시 설정과 자연스러운 브라우저 컨텍스트 구성에 집중합니다.
Python Playwright 예제
import asyncio
import random
import json
from playwright.async_api import async_playwright
# ProxyHat 주거 프록시 설정
# 국가 타겟팅: 미국 IP 사용
PROXY_URL = "http://user-country-US:PASSWORD@gate.proxyhat.com:8080"
def random_delay(min_ms=500, max_ms=3000):
"""인간처럼 불규칙한 지연 생성"""
return random.uniform(min_ms / 1000, max_ms / 1000)
async def human_scroll(page, scrolls=3):
"""자연스러운 스크롤 시뮬레이션"""
for i in range(scrolls):
scroll_y = random.randint(300, 800)
await page.evaluate(f"window.scrollBy(0, {scroll_y})")
await asyncio.sleep(random_delay(800, 2500))
async def scrape_public_page(page_url: str):
async with async_playwright() as p:
# 실제 브라우저 컨텍스트 구성
browser = await p.chromium.launch(
headless=True,
proxy={"server": PROXY_URL}
)
# 자연스러운 브라우저 컨텍스트
context = await browser.new_context(
viewport={"width": 1920, "height": 1080},
locale="en-US",
timezone_id="America/New_York",
user_agent=(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/125.0.0.0 Safari/537.36"
),
)
page = await context.new_page()
# 페이지 로드
await page.goto(page_url, wait_until="networkidle")
await asyncio.sleep(random_delay(2000, 4000))
# 자연스러운 스크롤
await human_scroll(page, scrolls=random.randint(2, 4))
# 공개 게시물 추출 (선택자는 변경될 수 있음)
posts = await page.evaluate("""() => {
const results = [];
const postEls = document.querySelectorAll(
'[data-testid="post_message"]'
);
postEls.forEach(el => {
results.push({
text: el.innerText?.trim() || '',
});
});
return results;
}""")
print(json.dumps(posts, ensure_ascii=False, indent=2))
await browser.close()
return posts
# 실행
asyncio.run(scrape_public_page(
"https://www.facebook.com/NASA/"
))
핵심 포인트:
proxy설정에 ProxyHat 주거 프록시를 사용 —gate.proxyhat.com:8080user-country-US플래그로 미국 IP 할당random_delay와human_scroll로 행동 패턴 자연스럽게 구현networkidle대기 후 추가 지연으로 Akamai 센서 데이터 수집 완료 보장
세션 유지(Sticky Session) 예제
여러 페이지를 같은 IP로 순회해야 하는 경우, 세션 플래그를 사용합니다:
# 동일 세션 ID로 여러 요청 시 동일 IP 유지
STICKY_PROXY = "http://user-country-US-session-nasa01:PASSWORD@gate.proxyhat.com:8080"
# 브라우저 실행 시 동일 프록시 사용
browser = await p.chromium.launch(
headless=True,
proxy={"server": STICKY_PROXY}
)
# 동일 브라우저 컨텍스트에서 여러 페이지 순회
pages_to_visit = [
"https://www.facebook.com/NASA/",
"https://www.facebook.com/SpaceX/",
"https://www.facebook.com/esa/",
]
for url in pages_to_visit:
page = await context.new_page()
await page.goto(url, wait_until="networkidle")
await asyncio.sleep(random_delay(3000, 6000))
await human_scroll(page)
# ... 데이터 추출 ...
await page.close()
Node.js Playwright 예제
const { chromium } = require('playwright');
const PROXY_URL = 'http://user-country-US:PASSWORD@gate.proxyhat.com:8080';
function randomDelay(minMs = 500, maxMs = 3000) {
return new Promise(resolve =>
setTimeout(resolve, minMs + Math.random() * (maxMs - minMs))
);
}
async function humanScroll(page, scrolls = 3) {
for (let i = 0; i < scrolls; i++) {
const scrollY = 300 + Math.floor(Math.random() * 500);
await page.evaluate(`window.scrollBy(0, ${scrollY})`);
await randomDelay(800, 2500);
}
}
async function scrapePublicPage(pageUrl) {
const browser = await chromium.launch({
headless: true,
proxy: { server: PROXY_URL },
});
const context = await browser.newContext({
viewport: { width: 1920, height: 1080 },
locale: 'en-US',
timezoneId: 'America/New_York',
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) ' +
'AppleWebKit/537.36 (KHTML, like Gecko) ' +
'Chrome/125.0.0.0 Safari/537.36',
});
const page = await context.newPage();
await page.goto(pageUrl, { waitUntil: 'networkidle' });
await randomDelay(2000, 4000);
await humanScroll(page, 2 + Math.floor(Math.random() * 3));
const posts = await page.$$eval(
'[data-testid="post_message"]',
(els) => els.map((el) => ({ text: el.innerText?.trim() || '' }))
);
console.log(JSON.stringify(posts, null, 2));
await browser.close();
return posts;
}
scrapePublicPage('https://www.facebook.com/NASA/');
curl을 사용한 간단 연결 테스트
본격적인 스크래핑 전에 프록시 연결을 확인합니다:
# ProxyHat 주거 프록시 연결 테스트
curl -x http://user-country-US:PASSWORD@gate.proxyhat.com:8080 \
-s https://httpbin.org/ip
# 독일 IP로 변경 테스트
curl -x http://user-country-DE:PASSWORD@gate.proxyhat.com:8080 \
-s https://httpbin.org/ip
속도 제한과 신뢰성 관리
페이스북 주거 프록시를 사용하더라도, 요청 속도를 관리하지 않으면 차단됩니다.
핵심 전략
- 요청 간 최소 3–8초 지연: 규칙적인 간격이 아닌, 무작위 지연을 사용
- 동일 IP로 시간당 30–50페이지 이하: 이후 속도 제한(soft block) 발생 가능
- IP 회전 전략: 페이지별 새 IP(주거 프록시 회전 모드) 또는 10–15페이지마다 세션 교체
- 429/403 응답 처리: 즉시 해당 세션 중단, 새 IP로 재시도, 지연 시간 증가
- 시간대 분산: 대량 수집은 현지 시간으로 업무 시간에 분산
import time
import random
class RateLimiter:
"""적응형 속도 제한기"""
def __init__(self, base_delay=5.0, max_delay=30.0):
self.base_delay = base_delay
self.max_delay = max_delay
self.current_delay = base_delay
self.consecutive_errors = 0
async def wait(self):
jitter = random.uniform(0.7, 1.3)
delay = min(self.current_delay * jitter, self.max_delay)
await asyncio.sleep(delay)
def on_success(self):
self.consecutive_errors = 0
self.current_delay = max(
self.base_delay,
self.current_delay * 0.8
)
def on_error(self):
self.consecutive_errors += 1
self.current_delay = min(
self.max_delay,
self.current_delay * 2
)
절대 피해야 할 것: 스코프 한계와 명백한 위반
Meta v. Bright Data 판결은 명확합니다: 메타의 서비스 약관을 위반하는 스크래핑은 법적 책임의 대상입니다. 다음은 절대 피해야 할 행위입니다:
🚫 로그인 자동화
계정에 로그인하는 자동화는 CFAA 위반의 핵심 요소입니다. 로그인 월 뒤의 데이터에 접근하려면 Graph API와 액세스 토큰을 사용해야 합니다.
🚫 비공개/제한 그룹 스크래핑
비공개 그룹, 비공개 이벤트, 친구 전용 게시물은 "공개"가 아닙니다. 설령 URL을 알고 있더라도 접근 자체가 약관 위반입니다.
🚫 개인 데이터 대량 수집
공개 프로필의 이름과 사진조차 대량으로 수집하면 GDPR(유럽)과 주 개인정보보호법(미국) 위반이 될 수 있습니다. 개인 식별 정보(PII) 수집은 명확한 법적 근거가 필요합니다.
🚫 서비스 장애 유발
과도한 동시 요청으로 메타 서버에 부하를 주는 것은 단순한 약관 위반을 넘어 범죄가 될 수 있습니다.
Graph API를 사용해야 하는 경우
인증이 필요한 데이터에 접근하려면 Facebook Graph API가 유일한 합법적 경로입니다. 공개 데이터라도 Graph API를 사용하면 더 안정적이고 법적으로 안전합니다.
Graph API가 적합한 경우
- 페이지 인사이트(조회수, 도달수 등) — 액세스 토큰 필요
- 특정 페이지의 전체 게시물 목록 —
/{page-id}/posts엔드포인트 - 광고 성과 데이터 — 전용 액세스 토큰 필요
- 사용자 프로필 정보 — 사용자 동의 기반 액세스 토큰
- 댓글, 반응 데이터 — 페이지 액세스 토큰
Graph API 예제
import requests
# Graph API를 통한 공개 페이지 게시물 조회
# 액세스 토큰은 Facebook 개발자 포털에서 발급
ACCESS_TOKEN = "your-app-access-token"
PAGE_ID = "NASA" # 페이지 사용자명 또는 ID
url = f"https://graph.facebook.com/v19.0/{PAGE_ID}/posts"
params = {
"fields": "id,message,created_time,full_picture",
"limit": 25,
"access_token": ACCESS_TOKEN,
}
response = requests.get(url, params=params)
data = response.json()
for post in data.get("data", []):
print(f"[{post.get('created_time')}] {post.get('message', '')[:80]}...")
Graph API의 장점은 명확합니다: 법적 위험이 없고, 구조화된 JSON 응답, 명확한 할당량 관리. 단점은 액세스 토큰 발급 절차와 할당량 제한이지만, 이는 합법적 데이터 수집의 정당한 대가입니다.
Graph API vs 스크래핑 비교
| 기준 | Graph API | 주거 프록시 + 브라우저 스크래핑 |
|---|---|---|
| 법적 위험 | ✅ 없음 (약관 준수) | ⚠️ 약관 위반 가능성 |
| 데이터 구조 | ✅ 구조화된 JSON | ⚠️ HTML 파싱 필요 |
| 안정성 | ✅ 높음 (버전 관리) | ❌ 낮음 (DOM 변경 시 중단) |
| 할당량 | 시간당 200 calls/사용자 | IP당 30–50 페이지/시간 |
| 비용 | 무료 (할당량 내) | 프록시 비용 발생 |
| 접근 범위 | 토큰 권한에 따라 다름 | 로그아웃 시 공개 데이터만 |
윤리적 스크래핑: 공식 API를 우선하세요
페이스북 공개 데이터 스크래핑을 고려할 때, 다음 우선순위를 따르세요:
- Graph API 사용 가능 여부를 먼저 확인: 대부분의 공개 페이지 데이터는 Graph API로 접근 가능합니다
- 공식 API가 없는 경우에만 스크래핑 고려: 예: Marketplace 리스팅, Graph API에서 제공하지 않는 공개 정보
- 최소한의 데이터 수집: 필요한 데이터만, 필요한 빈도로만
- robots.txt 준수: 메타의 robots.txt를 확인하고 준수하세요
- 수집 목적의 명확성: 저널리즘, 공공 이익, 합법적 브랜드 모니터링 등 정당한 목적이어야 합니다
브랜드 모니터링 팀이라면, Facebook Graph API로 시작하는 것이 장기적으로 더 안정적이고 비용 효율적입니다. 스크래핑은 Graph API가 커버하지 않는 영역에만 보완적으로 사용하세요.
핵심 원칙: 공식 API로 접근 가능한 데이터를 스크래핑으로 수집하는 것은 법적 위험을 감수하면서 얻는 이점이 없습니다. Graph API를 최우선으로, 스크래핑은 최후의 수단으로 사용하세요.
Key Takeaways
- 공개 데이터의 범위는 축소 중: 메타는 로그인 월을 확대하고 있으며, "공개"의 정의가 계속 변합니다
- 단순 HTTP 요청은 불가능: Akamai Bot Manager, TLS 핑거프린팅, 행동 분석으로 즉시 차단됩니다
- 주거 프록시 + 브라우저 자동화만 실용적: ISP ASN, 실제 브라우저 엔진, 자연스러운 상호작용 패턴이 필수
- Meta v. Bright Data를 명심: 약관 위반 스크래핑은 영구 금지 명령과 손해배상의 대상
- Graph API를 최우선으로: 인증이 필요한 데이터는 Graph API만이 합법적 경로
- 절대 금지: 로그인 자동화, 비공개 데이터 접근, 개인정보 대량 수집, 서비스 장애 유발
- 속도 제한 관리: IP당 시간당 30–50페이지, 무작위 지연, 적응형 백오프 전략 적용
합법적이고 윤리적인 공개 데이터 접근은 가능합니다. 하지만 메타의 방어 시스템과 법적 환경을 충분히 이해한 상태에서, 적절한 도구와 전략으로 접근해야 합니다. ProxyHat 주거 프록시로 시작하여, Graph API로 마이그레이션하는 하이브리드 접근이 가장 지속 가능한 전략입니다.
주거 프록시 설정에 대해 더 알아보려면 ProxyHat 요금제를 확인하거나, 지원 국가 목록에서 타겟 지역의 가용성을 확인하세요.






