2026년 Pinterest 핀 및 보드 스크래핑 방법: 프록시 활용 개발자 가이드

Pinterest 핀, 보드 피드, 검색 결과를 스크래핑하는 방법을 Resource API 엔드포인트, 회전 프록시, 페이지네이션 전략과 함께 다룹니다. 공개 데이터 접근에 초점을 맞춘 실용 가이드입니다.

How to Scrape Pinterest Pins and Boards in 2026: A Developer's Guide
이 글의 목차

면책 조항: 이 가이드는 Pinterest의 공개적으로 접근 가능한 핀 및 보드 데이터에 대한 합법적인 접근만을 다룹니다. Pinterest 이용약관(ToS)을 준수해야 하며, 미국에서는 Computer Fraud and Abuse Act (CFAA), 유럽연합에서는 GDPR 등 관련 법률을 반드시 확인해야 합니다. 로그인이 필요한 비공개 데이터나 개인정보를 수집하지 마십시오. 상업적 용도라면 공식 Pinterest API v5를 우선적으로 사용하십시오.

2026년에 Pinterest 핀 및 보드 스크래핑을 시도하는 개발자라면, 단순히 HTML을 가져오는 것만으로는 부족하다는 것을 금방 알게 됩니다. Pinterest는 React 기반 SPA(Single Page Application)이며, 대부분의 데이터가 내부 Resource API를 통해 JSON 형태로 비동기 로딩됩니다. 이 가이드에서는 Pinterest의 Resource API 구조, 회전 프록시 활용 방법, 페이지네이션 처리, 그리고 운영 환경에서의 안정성 확보 방법을 코드 예제와 함께 설명합니다.

Pinterest 핀 및 보드 스크래핑이란? — 공개 표면과 접근 가능 데이터

Pinterest 핀 및 보드 스크래핑은 Pinterest 플랫폼에서 공개적으로 접근 가능한 핀 데이터(이미지 URL, 제목, 설명, 링크, 보드 정보)를 프로그래밍 방식으로 수집하는 작업입니다. 시각적 콘텐츠 트렌드 분석, 이커머스 가격 모니터링, 머신러닝 학습 데이터 구축 등 다양한 목적에 활용됩니다.

공개 표면 vs 로그인 필요 표면

표면 유형접근 방식데이터 예시
개별 핀 페이지공개 (로그인 불필요)핀 ID, 이미지 URL, 제목, 설명, 링크
공개 보드 피드공개 (로그인 불필요)보드 내 핀 목록, 보드 메타데이터
검색 결과공개 (로그인 불필요)검색어별 핀, 추천 보드
홈 피드 (Home Feed)로그인 필요개인화된 추천 핀
비공개 보드로그인 필요 (권한)비공개 핀 데이터

이 가이드에서는 공개 핀, 공개 보드 피드, 검색 결과만을 다룹니다. 홈 피드와 비공개 보드는 로그인이 필요하며, 이를 스크래핑하는 것은 ToS 위반 및 법적 위험이 따릅니다.

공식 Pinterest API v5의 한계

Pinterest는 공식 Pinterest API v5를 제공하지만, 상업적 액세스가 제한적이고 핀 생성/관리에 중점을 맞추고 있습니다. 대량의 핀 데이터를 읽어오는 데는 엔드포인트가 제한적이며, rate limit이 분당 10~100 요청 수준으로 설정되어 있어 대규모 데이터 수집에는 부적합합니다. 따라서 개발자들은 공개 웹 표면을 통해 데이터에 접근하는 방식을 고려하게 됩니다.

Pinterest 내부 Resource API 구조 이해하기

Pinterest의 프론트엔드는 /resource/<ResourceName>/get/ 형태의 내부 API 엔드포인트를 사용합니다. 이 엔드포인트들은 브라우저에서 AJAX 요청으로 호출되며, ?source_url= 파라미터와 URL 인코딩된 JSON data 파라미터를 통해 요청 데이터를 전달합니다.

주요 Resource 엔드포인트

  • PinResource: /resource/PinResource/get/ — 개별 핀의 상세 정보를 반환합니다.
  • BoardFeedResource: /resource/BoardFeedResource/get/ — 특정 보드의 핀 목록을 페이지네이션과 함께 반환합니다.
  • SearchResource: /resource/SearchResource/get/ — 검색어 기반 핀 및 보드 결과를 반환합니다.
  • BoardResource: /resource/BoardResource/get/ — 보드 메타데이터(이름, 설명, 팔로워 수 등)를 반환합니다.

예를 들어, BoardFeedResource 요청은 다음과 같은 형태를 가집니다:

GET https://www.pinterest.com/resource/BoardFeedResource/get/?source_url=/board_slug/&data=%7B%22options%22%3A%7B%22board_id%22%3A%22123456789%22%2C%22page_size%22%3A25%2C%22bookmarks%22%3A%5B%22bookmark_token%22%5D%7D%7D

필수 헤더

Resource API 요청이 성공하려면 다음 헤더들이 필요합니다:

  • X-Pinterest-PWS-Handler — Pinterest 웹 서비스 핸들러 식별자
  • X-APP-VERSION — 프론트엔드 앱 버전 (예: abc123 형태의 해시)
  • csrftoken — CSRF 보호 토큰 (쿠키에서 획득)
  • User-Agent — 실제 브라우저와 일치하는 UA 문자열
  • Acceptapplication/json

X-APP-VERSION 값은 Pinterest 프론트엔드 배포마다 변경되므로, 스크래핑 전에 브라우저 개발자 도구에서 현재 값을 확인해야 합니다. 이 값을 주기적으로 갱신하지 않으면 요청이 실패할 수 있습니다.

안티봇 현실: IP별 rate limit과 봇 점수

Pinterest는 Cloudflare 및 자체 안티봇 시스템을 통해 요청 패턴을 분석합니다. 단일 IP에서 짧은 시간에 과도한 요청이 발생하면 다음과 같은 대응이 발생합니다:

  • Rate limiting: IP당 분당 약 50~100 요청 수준에서 429 응답 반환
  • 봇 점수: 요청 패턴, 헤더 일관성, TLS 지문, 행동 패턴을 기반으로 봇 점수 산정
  • CAPTCHA: 의심스러운 요청에 대해 hCaptcha 챌린지 반환
  • IP 차단: 반복 위반 시 IP를 블랙리스트에 등록

특히 Pinterest의 검색 결과와 추천 핀은 지역화되어 있습니다. 미국 사용자에게 보이는 검색 결과와 한국 사용자에게 보이는 결과가 다릅니다. 따라서 일관된 지역의 데이터를 수집하려면 지역 타겟팅이 가능한 회전 주거용 프록시(rotating residential proxy)가 필요합니다. 데이터센터 IP는 Pinterest의 안티봇 시스템에서 높은 봇 점수를 받아 차단될 확률이 높습니다.

RFC 7231에 따르면 HTTP 429 응답은 rate limit 도달을 의미하며, Retry-After 헤더를 통해 재시도 시점을 알려줍니다. 이를 존중하여 백오프 전략을 구현해야 합니다.

ProxyHat 프록시 설정 및 연결

ProxyHat 주거용 프록시를 사용하면 실제 ISP IP에서 요청을 보내는 것과 같은 효과를 얻을 수 있습니다. 지역 타겟팅, 세션 고정(sticky session), 회전(rotation) 기능을 모두 사용자 이름 필드에서 제어합니다.

연결 형식

# HTTP 게이트웨이 (기본 포트 8080)
http://user-country-US:pass@gate.proxyhat.com:8080

# 미국 특정 도시
http://user-country-US-city-new_york:pass@gate.proxyhat.com:8080

# 세션 고정 (sticky session)
http://user-session-abc123:pass@gate.proxyhat.com:8080

# SOCKS5 게이트웨이 (포트 1080)
socks5://user-country-US:pass@gate.proxyhat.com:1080

자세한 프록시 설정은 ProxyHat 문서를 참조하십시오. 가격 정보는 ProxyHat 가격 페이지에서 확인할 수 있으며, 지원 지역 목록은 위치 페이지에서 확인 가능합니다.

Python 예제: BoardFeedResource 페이지네이션

다음은 ProxyHat 주거용 프록시를 통해 Pinterest BoardFeedResource를 페이지네이션하며 핀 데이터를 수집하는 Python 예제입니다. requests 라이브러리와 세션 고정(sticky session)을 사용하여 csrftoken 연속성을 유지합니다.

import requests
import json
import urllib.parse
import time
import random

# ProxyHat 주거용 프록시 (미국 지역, sticky session)
proxy_url = "http://user-country-US-session-pinterest01:pass@gate.proxyhat.com:8080"

proxies = {
    "http": proxy_url,
    "https": proxy_url,
}

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
    "Accept": "application/json, text/javascript, */*; q=0.01",
    "Accept-Language": "en-US,en;q=0.9",
    "X-Pinterest-PWS-Handler": "www/[username]/[slug]/[slug].js",
    "X-APP-VERSION": "a1b2c3d",  # 브라우저 개발자 도구에서 확인 필요
    "X-Requested-With": "XMLHttpRequest",
}

session = requests.Session()
session.proxies = proxies
session.headers.update(headers)

# 1단계: 보드 페이지 방문하여 csrftoken 획득
board_url = "https://www.pinterest.com/exampleuser/example-board/"
resp = session.get(board_url, timeout=30)
csrftoken = session.cookies.get("csrftoken", "")
session.headers.update({"X-CSRFToken": csrftoken})

# 2단계: BoardFeedResource 호출로 핀 목록 수집
board_id = "123456789012345678"  # 보드 페이지 HTML에서 추출
bookmarks = [""]
all_pins = []

for page in range(5):  # 최대 5페이지 (약 125개 핀)
    data_param = json.dumps({
        "options": {
            "board_id": board_id,
            "page_size": 25,
            "bookmarks": bookmarks,
            "current_url": board_url,
        },
        "context": {}
    })

    api_url = (
        "https://www.pinterest.com/resource/BoardFeedResource/get/"
        f"?source_url={urllib.parse.quote(board_url, safe='')}"
        f"&data={urllib.parse.quote(data_param, safe='')}"
    )

    resp = session.get(api_url, timeout=30)
    if resp.status_code == 429:
        retry_after = int(resp.headers.get("Retry-After", "60"))
        print(f"Rate limited. Waiting {retry_after}s...")
        time.sleep(retry_after)
        continue

    if resp.status_code != 200:
        print(f"Error: {resp.status_code}")
        break

    result = resp.json()
    pins = result.get("resource_response", {}).get("data", [])

    for pin in pins:
        # truncated pin object 파싱
        pin_id = pin.get("id")
        title = pin.get("title") or pin.get("description", "")[:100]
        image_url = (
            pin.get("images", {})
            .get("orig", {})
            .get("url", "")
        )
        link = pin.get("link") or pin.get("repin", {}).get("link", "")

        all_pins.append({
            "id": pin_id,
            "title": title,
            "image_url": image_url,
            "link": link,
        })

    # 다음 페이지 북마크 추출
    bookmarks = result.get("resource", {}).get("options", {}).get("bookmarks", [])
    if not bookmarks or bookmarks == [""]:
        print("No more pages.")
        break

    # 요청 간 지연 (pacing)
    time.sleep(random.uniform(2.0, 4.0))

print(f"Collected {len(all_pins)} pins")
for p in all_pins[:3]:
    print(json.dumps(p, indent=2, ensure_ascii=False))

이 예제에서는 user-session-pinterest01 세션 식별자를 사용하여 동일한 IP를 유지합니다. 이는 csrftoken이 IP와 연관되어 있기 때문에 중요합니다. 세션을 변경하면 토큰이 무효화될 수 있습니다.

Node.js 예제: HTTP 게이트웨이를 통한 핀 검색

다음은 Node.js에서 ProxyHat HTTP 게이트웨이(포트 8080)를 통해 SearchResource를 호출하는 예제입니다.

const https = require('https');
const { HttpsProxyAgent } = require('https-proxy-agent');

const proxyUrl = "http://user-country-US-session-pinterest02:pass@gate.proxyhat.com:8080";
const agent = new HttpsProxyAgent(proxyUrl);

const query = "minimalist home decor";
const dataParam = JSON.stringify({
  options: {
    query: query,
    scope: "pins",
    page_size: 25,
    bookmarks: [""],
  },
  context: {},
});

const sourceUrl = `/search/pins/?q=${encodeURIComponent(query)}`;
const apiUrl = `https://www.pinterest.com/resource/SearchResource/get/?source_url=${encodeURIComponent(sourceUrl)}&data=${encodeURIComponent(dataParam)}`;

const options = {
  hostname: "www.pinterest.com",
  path: apiUrl.replace("https://www.pinterest.com", ""),
  method: "GET",
  agent: agent,
  headers: {
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
    "Accept": "application/json, text/javascript, */*; q=0.01",
    "Accept-Language": "en-US,en;q=0.9",
    "X-Pinterest-PWS-Handler": "www/search/[scope].js",
    "X-APP-VERSION": "a1b2c3d",
    "X-Requested-With": "XMLHttpRequest",
  },
};

https.get(options, (res) => {
  let body = "";
  res.on("data", (chunk) => (body += chunk));
  res.on("end", () => {
    if (res.statusCode === 200) {
      const result = JSON.parse(body);
      const pins = result.resource_response?.data || [];
      pins.slice(0, 5).forEach((pin) => {
        console.log({
          id: pin.id,
          title: pin.title || pin.description?.substring(0, 100),
          image_url: pin.images?.orig?.url,
          link: pin.link,
        });
      });
      console.log(`Total results on page: ${pins.length}`);
    } else {
      console.error(`Status: ${res.statusCode}`);
    }
  });
}).on("error", (e) => console.error(e));

북마크/커서 페이지네이션과 안정성 전략

북마크 기반 페이지네이션

Pinterest Resource API는 전통적인 offset 기반 페이지네이션이 아닌 북마크(커서) 기반 페이지네이션을 사용합니다. 각 응답에 포함된 bookmarks 배열을 다음 요청의 options.bookmarks에 전달하여 다음 페이지를 가져옵니다. 빈 배열이 반환되면 더 이상 데이터가 없음을 의미합니다.

세션 고정(sticky session)과 csrftoken

csrftoken은 쿠키로 발급되며, 특정 IP와 연관됩니다. ProxyHat에서 user-session-abc123 형태로 세션을 고정하면 동일한 IP를 유지하여 토큰 연속성을 보장할 수 있습니다. 세션을 너무 오래 유지하면 단일 IP에 요청이 집중되어 rate limit에 걸릴 수 있으므로, 보드당 하나의 세션을 사용하고 보드 간에 세션을 교체하는 전략이 효과적입니다.

페이싱(Pacing) 전략

  • 요청 간 2~5초 무작위 지연
  • 페이지당 25개 핀 (Pinterest 기본 page_size)
  • 단일 세션당 분당 10~15 요청 이하 유지
  • 429 응답 시 Retry-After 헤더 존중
  • 지수 백오프: 30초 → 60초 → 120초

User-Agent 위생

요청마다 다른 User-Agent를 사용하는 것은 오히려 의심을 유발할 수 있습니다. 단일 세션 내에서는 일관된 UA를 유지하고, 세션 교체 시 다른 브라우저 UA로 전환하는 것이 자연스럽습니다. Accept-Language 헤더도 지역과 일치시켜야 합니다. 예를 들어 user-country-DE 프록시를 사용할 때는 Accept-Language: de-DE,de;q=0.9,en;q=0.7를 사용하는 것이 자연스럽습니다.

흔한 실수와 엣지 케이스

  • X-APP-VERSION 만료: Pinterest 배포가 업데이트되면 이전 버전 값이 거부됩니다. 주기적으로 브라우저에서 새 값을 확인해야 합니다.
  • 북마크 인코딩 오류: 북마크 문자열에 특수문자가 포함될 수 있으므로 URL 인코딩을 정확히 처리해야 합니다.
  • 핀 타입 혼재: BoardFeedResource 응답에는 일반 핀 외에도 스토리 핀, 비디오 핀, 프로모션 핀이 섞여 있을 수 있습니다. pin.type 필드로 필터링하십시오.
  • 빈 응답: 일부 보드는 로그인한 사용자에게만 공개되어 있어 공개 접근 시 빈 응답이 반환됩니다.
  • 지역별 결과 차이: 검색 결과는 IP 지역에 따라 다릅니다. 일관된 데이터를 원하면 user-country-US와 같이 지역을 고정하십시오.

윤리적 스크래핑과 공식 API 사용

Pinterest 스크래핑을 수행할 때 다음 원칙을 지켜야 합니다:

  • 공개, 비개인 핀 데이터만 수집: 사용자 프로필 정보, 이메일, 비공개 보드 데이터는 수집하지 마십시오.
  • robots.txt 존중: Pinterest robots.txt를 확인하고 허용된 경로만 접근하십시오.
  • 수집 목적 명확화: 수집한 데이터의 용도를 명확히 하고, GDPR이 적용되는 EU 사용자 데이터를 처리할 때는 법적 근거가 필요합니다.
  • 서버 부하 최소화: 적절한 페이싱과 캐싱을 통해 Pinterest 서버에 부담을 주지 않는 수준에서 수집해야 합니다.
  • 공식 API 우선: 상업적 서비스를 구축하는 경우 Pinterest API v5를 우선적으로 고려하십시오. 공식 API는 안정성과 법적 보호를 제공합니다.

스크래핑은 공식 API가 제공하지 않는 데이터에 대한 'last resort'로 접근해야 합니다. 공식 API가 요구를 충족한다면, 그것이 항상 더 안전하고 지속 가능한 선택입니다.

웹 스크래핑 사용 사례에 대한 자세한 내용은 ProxyHat 웹 스크래핑 사용 사례를 참조하십시오. SERP 추적과 관련된 작업은 SERP 추적 사용 사례에서 확인할 수 있습니다.

핵심 요약 (Key Takeaways)

  • Pinterest는 React SPA이며, 데이터는 /resource/<Name>/get/ 형태의 내부 Resource API를 통해 JSON으로 제공됩니다.
  • 공개 핀, 공개 보드 피드, 검색 결과는 로그인 없이 접근 가능하지만, 홈 피드와 비공개 보드는 로그인이 필요합니다.
  • X-Pinterest-PWS-Handler, X-APP-VERSION, csrftoken 헤더가 필수이며, X-APP-VERSION은 주기적으로 갱신해야 합니다.
  • 회전 주거용 프록시와 지역 타겟팅(user-country-US)이 안티봇 회피와 일관된 지역 데이터 수집에 필수적입니다.
  • 북마크 기반 페이지네이션, 세션 고정, 2~5초 페이싱, 일관된 User-Agent로 안정성을 확보할 수 있습니다.
  • 상업적 용도라면 공식 Pinterest API v5를 우선적으로 고려하고, 공개 비개인 데이터만 윤리적으로 수집하십시오.

자주 묻는 질문

Pinterest 핀 및 보드 스크래핑이란 무엇인가요?

Pinterest 핀 및 보드 스크래핑은 Pinterest 플랫폼에서 공개적으로 접근 가능한 핀 데이터(이미지 URL, 제목, 설명, 링크 등)를 프로그래밍 방식으로 수집하는 작업입니다. Pinterest는 React 기반 SPA이므로 HTML 대신 내부 Resource API 엔드포인트를 통해 JSON 데이터를 수집해야 합니다. 공개 핀, 공개 보드 피드, 검색 결과는 로그인 없이 접근할 수 있습니다.

Pinterest 스크래핑에 프록시가 왜 필요한가요?

Pinterest는 IP별 rate limit과 봇 점수 시스템을 운영합니다. 단일 IP에서 과도한 요청을 보내면 429 응답이 반환되거나 IP가 차단됩니다. 또한 검색 결과와 추천 핀이 지역화되어 있어 일관된 데이터를 수집하려면 지역 타겟팅이 가능한 회전 주거용 프록시가 필요합니다. 데이터센터 IP는 높은 봇 점수를 받아 차단 확률이 높습니다.

Pinterest 스크래핑에 어떤 프록시 유형이 가장 적합한가요?

주거용 프록시(residential proxy)가 가장 적합합니다. 실제 ISP IP를 사용하므로 Pinterest의 안티봇 시스템에서 일반 사용자와 구별하기 어렵습니다. 지역 타겟팅(user-country-US 등)으로 일관된 지역의 데이터를 수집할 수 있고, 세션 고정(user-session-abc123)으로 csrftoken 연속성을 유지할 수 있습니다. 데이터센터 프록시는 빠르지만 봇 감지 확률이 높습니다.

Pinterest 스크래핑 시 차단을 피하려면 어떻게 해야 하나요?

요청 간 2~5초 무작위 지연, 분당 10~15 요청 이하 유지, 일관된 User-Agent 사용, X-APP-VERSION 주기적 갱신, 세션 고정을 통한 csrftoken 연속성 유지가 필요합니다. 429 응답 시 Retry-After 헤더를 존중하고 지수 백오프를 적용해야 합니다. 보드 간 세션을 교체하여 단일 IP에 요청이 집중되지 않도록 관리해야 합니다.

Pinterest 공식 API v5 대신 스크래핑을 사용해야 하는 경우는?

Pinterest API v5는 상업적 액세스가 제한적이고 핀 생성/관리에 중점을 맞추고 있어 대량 핀 데이터 읽기에 부적합합니다. 분당 10~100 요청 수준의 rate limit으로 대규모 데이터 수집이 어렵습니다. 공식 API가 요구를 충족하지 못할 때 공개 데이터에 한해 스크래핑을 last resort로 고려할 수 있지만, 상업적 서비스라면 공식 API를 우선적으로 사용해야 법적 보호를 받을 수 있습니다.

시작할 준비가 되셨나요?

AI 필터링으로 148개국 이상에서 5천만 개 이상의 레지덴셜 IP에 액세스하세요.

가격 보기레지덴셜 프록시
← 블로그로 돌아가기