Python Scraping Pinterest: "Pins, Boards & Cuộn Vô Tận"

Cập nhật lần cuối vào April 17, 2026
Python Scraping Pinterest: "Pins, Boards & Cuộn Vô Tận"

Vài tháng trước, một kỹ sư trong team đã mở cho tôi xem một đoạn Python script mà anh ấy viết vào cuối tuần. Mục tiêu của nó là lấy ảnh cảm hứng sản phẩm từ Pinterest cho một dự án nghiên cứu thị trường. Anh ấy chạy thử, và kết quả là… 16 pin. Trong khi board đó có hơn 2.000 pin. Anh ấy nhìn màn hình, rồi nhìn tôi, và nói: “Tôi nghĩ Pinterest đang troll tôi.”

Anh ấy không hề đơn độc. Đây là nỗi bực mình phổ biến nhất mà tôi thấy ở các developer khi cố scrape Pinterest bằng Python. Bạn mở requests và BeautifulSoup, truy cập một URL Pinterest, rồi chỉ nhận về vài mục lẻ tẻ hoặc một khung HTML trống rỗng. Vì sao lại vậy? Pinterest là một ứng dụng một trang (SPA) được render hoàn toàn bằng JavaScript — nên request HTTP tĩnh của bạn không bao giờ nhìn thấy được nội dung thật. Trong hướng dẫn này, tôi sẽ giải thích vì sao chuyện đó xảy ra, những cách thật sự hiệu quả (Playwright, intercept API nội bộ, và các công cụ no-code như Thunderbit), đồng thời cung cấp code từng bước để scrape pin, board, hồ sơ người dùng, cuộn vô tận, và ảnh độ phân giải cao. Dù bạn muốn build một scraper chuẩn production hay chỉ cần lấy nhanh dữ liệu, bài viết này sẽ giúp bạn.

Scraping Pinterest Là Gì?

Scraping Pinterest là quá trình trích xuất dữ liệu từ Pinterest bằng chương trình — chẳng hạn như ảnh pin, tiêu đề, mô tả, tên board, số follower và URL. Thay vì tự tay mở từng pin rồi lưu lại, bạn dùng code (hoặc một công cụ) để thu thập dữ liệu có cấu trúc từ kết quả tìm kiếm, board hoặc hồ sơ người dùng ở quy mô lớn.

Với hơn 240 tỷ Pin trên nền tảng và 619 triệu người dùng hoạt động hàng tháng tính đến cuối năm 2025, Pinterest là một trong những nguồn dữ liệu hình ảnh phong phú nhất trên web. Với doanh nghiệp, dữ liệu này cực kỳ giá trị — dù bạn đang theo dõi xu hướng sản phẩm, so sánh nội dung đối thủ, hay xây dựng danh sách tiếp cận influencer.

Vì Sao Nên Scrape Pinterest Bằng Python?

Pinterest không còn chỉ là nơi lưu mood board cho wedding planner nữa. Nó đã trở thành một nền tảng phân tích kinh doanh nghiêm túc — 85% người dùng Pinterest hằng tuần đã mua một thứ gì đó sau khi xem Pin của thương hiệu, và 96–97% lượt tìm kiếm hàng đầu là không gắn thương hiệu, nghĩa là người dùng đến với ý định rõ ràng nhưng không bị ràng buộc bởi lòng trung thành thương hiệu. Đây là một cơ hội rất lớn để khai thác insight — và cũng là lý do nhiều team muốn có dữ liệu Pinterest ở dạng cấu trúc.

Dưới đây là cách dữ liệu này mang lại giá trị cho từng nhóm:

NhómDữ liệu cầnGiá trị kinh doanh
Vận hành EcommerceẢnh sản phẩm, giá, thẩm mỹ đang lênĐịnh giá cạnh tranh, tồn kho theo xu hướng
MarketingHiệu suất board, mức độ tương tác của pin, nội dung đối thủChiến lược nội dung, so sánh chiến dịch
Sales / Lead GenHồ sơ creator, số follower, thông tin liên hệTiếp cận influencer, nhắm mục tiêu hợp tác
Bất động sảnPin về staging nhà, xu hướng trang trí, bố cục phòngẢnh listing, gợi ý staging
Content CreatorsChủ đề đang hot, định dạng phổ biến, chủ đề theo mùaLịch nội dung, nghiên cứu phong cách hình ảnh

Và đây là điểm đáng chú ý: API chính thức của Pinterest khá hạn chế. Nó yêu cầu tài khoản doanh nghiệp, phải được phê duyệt (bao gồm cả video demo ứng dụng của bạn), và chỉ cho phép truy cập dữ liệu của chính tài khoản bạn. Nếu bạn muốn xem board công khai, kết quả tìm kiếm hoặc hồ sơ đối thủ, scraping là lựa chọn thực tế hơn. Đó là lý do nhiều team chuyển sang Python — hoặc dùng công cụ no-code như Thunderbit khi họ muốn có kết quả mà không phải lo phần thiết lập.

Vì Sao Chỉ Dùng BeautifulSoup Không Hoạt Động Với Pinterest (Và Cái Gì Mới Thực Sự Hiệu Quả)

Nếu bạn từng thử scrape Pinterest bằng requests + BeautifulSoup rồi chỉ nhận được 16 mục hoặc một trang trống, thì không phải bạn tưởng tượng đâu. Pinterest được xây bằng React và toàn bộ nội dung được render bằng JavaScript. Khi bạn truy cập một URL Pinterest bằng request HTTP bình thường, server chỉ trả về một khung HTML rất tối giản — vài thẻ <link><script>, cùng một <div> trống nơi React sẽ mount ứng dụng. Tất cả pin card, ảnh, tiêu đề và bố cục lưới chỉ được chèn vào sau khi JavaScript chạy trong trình duyệt.

Không chạy JavaScript = không có pin.

Vậy cái gì hoạt động? Dưới đây là so sánh giữa các cách tiếp cận chính:

Cách tiếp cậnXử lý JS?Lấy đủ dữ liệu?Độ phức tạpPhù hợp nhất cho
requests + BeautifulSoupKhông~0–16 mụcThấpKhông phù hợp với Pinterest
Selenium / PlaywrightCó, nếu xử lý cuộnTrung bìnhKiểm soát đầy đủ, pipeline Python
Intercept API nội bộ PinterestCó, JSON phân trangCaoTối đa dữ liệu, không cần browser
Third-party Scraper APITùyThấpScale mà không cần hạ tầng
Công cụ no-code (Thunderbit)Có cấu trúc bằng AIRất thấpNgười không rành kỹ thuật, kết quả nhanh

Trong hướng dẫn này, tôi khuyên dùng Playwright cho hướng Python. Nó render JavaScript, hỗ trợ giả lập cuộn trang, được duy trì tốt (78.600+ sao trên GitHub, số job posting tăng 180% YoY), và nhanh hơn 35–45% so với Selenium trong các benchmark. Nếu bạn muốn đi theo hướng no-code, tôi cũng sẽ nói đến.

Pinterest Official API vs. Python Scraping vs. No-Code: Nên Chọn Hướng Nào?

Trước khi bắt đầu viết code, đáng để tự hỏi: bạn có thật sự cần viết code không? Đây là khung quyết định:

Tiêu chíPinterest APIPython ScrapingThunderbit (No-Code)
Cần phê duyệtTài khoản doanh nghiệp + video demoKhôngKhông
Truy cập pin/board công khaiHạn chế (chỉ dữ liệu của mình)Đầy đủĐầy đủ
Tải ảnh full-resTùyCó, thông qua xử lý URLCó, bằng trích xuất ảnh
Hỗ trợ infinite scrollKhông áp dụngCó, bằng codeTự động
Cần bảo trìThấpCao (selector dễ hỏng)Không (AI tự thích ứng)
Xuất sang Sheets/AirtableThủ côngCode tuỳ chỉnhCó sẵn
Thời gian thiết lậpVài giờ–vài ngày30–60 phút2 phút

Nếu bạn là marketer, người làm vận hành ecommerce, hoặc đơn giản là muốn có dữ liệu Pinterest trong spreadsheet mà không phải viết hay bảo trì Python script, Thunderbit's AI Web Scraper là con đường nhanh nhất. Bạn chỉ cần mở một trang Pinterest, bấm "AI Suggest Fields", nhấn "Scrape", rồi xuất thẳng sang Google Sheets, Excel, Airtable hoặc Notion. Tính năng scrape subpage của nó thậm chí còn có thể đi theo từng link pin để tự động làm giàu dữ liệu. Tôi đã thấy những người chưa từng viết một dòng code kéo hơn 500 pin vào Google Sheet trong chưa đầy ba phút.

Với những ai muốn toàn quyền kiểm soát, muốn tích hợp scraping vào pipeline Python, hoặc đơn giản thích tự tay build — hãy đọc tiếp.

Thiết Lập Môi Trường Python Cho Pinterest Scraping

  • Độ khó: Trung bình
  • Thời gian cần: khoảng 30–60 phút (bao gồm viết code và kiểm thử)
  • Bạn cần: Python 3.9+, trình duyệt Chrome (để test), quyền truy cập terminal/command line

Cài Playwright Và Các Phụ Thuộc

Trước tiên, tạo thư mục dự án và thiết lập virtual environment:

mkdir pinterest-scraper
cd pinterest-scraper
python -m venv venv
source venv/bin/activate  # Trên Windows: venv\Scripts\activate

Cài Playwright và tải binary của trình duyệt Chromium:

pip install playwright
playwright install chromium

Bạn cũng sẽ dùng các module có sẵn của Python là json, os, và csv để xuất dữ liệu. Không cần cài thêm cho những module này.

Cấu Trúc Thư Mục Dự Án

Tôi khuyên nên sắp xếp ngay từ đầu cho gọn gàng:

pinterest-scraper/
├── scraper.py
├── config.py
├── output/
│   ├── pins.json
│   └── pins.csv
└── images/
    ├── board-name-1/
    └── board-name-2/

Trong config.py, hãy đặt user agent string của bạn. Pinterest chặn dấu hiệu từ headless browser mặc định, nên bạn cần dùng một chuỗi giống trình duyệt thật:

USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"

Bước 1: Tạo URL Tìm Kiếm Pinterest

Tạo URL tìm kiếm bằng cách chèn từ khóa của bạn vào mẫu:

query = "mid century modern furniture"
url = f"https://www.pinterest.com/search/pins/?q={query.replace(' ', '%20')}&rs=typed"

Bạn có thể tham số hoá cho bất kỳ cụm tìm kiếm nào. Tham số rs=typed cho Pinterest biết truy vấn là do người dùng gõ vào (không phải gợi ý), đôi khi sẽ ảnh hưởng đến độ liên quan của kết quả.

Bước 2: Mở Trình Duyệt Headless Và Tải Trang

Đây là phần cốt lõi của Playwright. Hãy chú ý user agent tuỳ chỉnh — nếu không có nó, Pinterest rất có thể sẽ chặn bạn hoặc hiện trang yêu cầu đăng nhập.

import asyncio
from playwright.async_api import async_playwright
from config import USER_AGENT

async def scrape_search(query, max_pins=100):
    url = f"https://www.pinterest.com/search/pins/?q={query.replace(' ', '%20')}&rs=typed"
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        page = await browser.new_page(
            user_agent=USER_AGENT,
            viewport={"width": 1920, "height": 1080}
        )
        await page.goto(url)
        await asyncio.sleep(3)  # Chờ JS render pin ban đầu

Sau khi chạy xong, trang sẽ có lô pin đầu tiên được load — thường là 25–50 pin.

Bước 3: Trích Xuất Dữ Liệu Pin Từ Trang

Pinterest bọc mỗi pin trong một divdata-test-id='pinWrapper'. Bên trong, bạn sẽ thấy một link (<a>) chứa URL và tiêu đề của pin (qua aria-label), và một thẻ <img> chứa URL thumbnail.

        results = []
        pins = await page.query_selector_all("div[data-test-id='pinWrapper']")
        for pin in pins:
            link = await pin.query_selector("a")
            if not link:
                continue
            title = await link.get_attribute("aria-label") or ""
            href = await link.get_attribute("href") or ""
            img = await pin.query_selector("img")
            src = await img.get_attribute("src") if img else ""
            results.append({
                "title": title,
                "url": f"https://www.pinterest.com{href}" if href.startswith("/") else href,
                "image_url": src
            })

Lúc này, results chỉ chứa những pin đang hiển thị trong vùng nhìn thấy ban đầu. Để lấy thêm, bạn phải cuộn trang — và đó là phần quan trọng nhất.

Bước 4: Lưu Kết Quả Sang JSON Hoặc CSV

Sau khi trích xuất, hãy ghi dữ liệu ra file để tiện sử dụng:

import json
import csv

def save_json(data, filepath="output/pins.json"):
    with open(filepath, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)

def save_csv(data, filepath="output/pins.csv"):
    if not data:
        return
    with open(filepath, "w", newline="", encoding="utf-8-sig") as f:
        writer = csv.DictWriter(f, fieldnames=data[0].keys())
        writer.writeheader()
        writer.writerows(data)

Hãy dùng mã hoá utf-8-sig cho CSV nếu bạn định mở nó bằng Excel — cách này sẽ tránh lỗi ký tự bị loạn.

Scrape Toàn Bộ Board Và Hồ Sơ Người Dùng Pinterest

Đây là một khoảng trống lớn trong các tutorial hiện có. Tôi không tìm thấy một hướng dẫn cạnh tranh nào đi sâu vào scraping board hoặc profile — nhưng đây lại là một trong những tính năng được hỏi nhiều nhất trên các diễn đàn. Người dùng muốn tải toàn bộ pin của một board, sắp xếp ảnh vào từng thư mục riêng theo board, và lấy dữ liệu cấp hồ sơ như số follower cùng danh sách board.

Scrape Tất Cả Pin Từ Một URL Board

URL board thường có dạng https://www.pinterest.com/{username}/{board-name}/. Cấu trúc DOM tương tự kết quả tìm kiếm — pin được bọc trong div[data-test-id='pinWrapper'] — nhưng bạn cần cuộn để load hết.

async def scrape_board(board_url, max_pins=500):
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        page = await browser.new_page(user_agent=USER_AGENT, viewport={"width": 1920, "height": 1080})
        await page.goto(board_url)
        await asyncio.sleep(3)

        seen_ids = set()
        all_pins = []

        for scroll_round in range(100):  # Giới hạn an toàn
            pins = await page.query_selector_all("div[data-test-id='pinWrapper']")
            new_count = 0
            for pin in pins:
                link = await pin.query_selector("a")
                if not link:
                    continue
                href = await link.get_attribute("href") or ""
                if href in seen_ids:
                    continue
                seen_ids.add(href)
                new_count += 1
                title = await link.get_attribute("aria-label") or ""
                img = await link.query_selector("img")
                src = await img.get_attribute("src") if img else ""
                all_pins.append({
                    "title": title,
                    "url": f"https://www.pinterest.com{href}" if href.startswith("/") else href,
                    "image_url": src
                })

            print(f"Lần cuộn {scroll_round + 1}: đã thu thập {len(all_pins)} pin duy nhất")
            if new_count == 0 or len(all_pins) >= max_pins:
                break

            prev_height = await page.evaluate("document.body.scrollHeight")
            await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
            await asyncio.sleep(2.5)
            curr_height = await page.evaluate("document.body.scrollHeight")
            if curr_height == prev_height:
                break  # Không còn nội dung mới

        await browser.close()
        return all_pins

Một điểm cần lưu ý: các trang board đôi khi có tab "More Ideas" tách pin đã lưu khỏi phần gợi ý do thuật toán. Nếu bạn chỉ muốn các pin mà người dùng đã lưu, hãy dừng cuộn khi gặp phần chia tách đó.

Scrape Hồ Sơ Người Dùng: Board, Số Follower Và Pin

URL hồ sơ có dạng https://www.pinterest.com/{username}/. Từ trang profile, bạn có thể trích xuất:

  • Số follower/following: Tìm div[data-test-id='follower-count']
  • Danh sách board: Mỗi board là một thẻ card dẫn đến /{username}/{board-name}/
  • Tổng số pin: Đôi khi hiển thị trong phần đầu trang profile
async def scrape_profile(username):
    url = f"https://www.pinterest.com/{username}/"
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        page = await browser.new_page(user_agent=USER_AGENT, viewport={"width": 1920, "height": 1080})
        await page.goto(url)
        await asyncio.sleep(3)

        # Lấy số follower
        follower_el = await page.query_selector("div[data-test-id='follower-count']")
        followers = await follower_el.inner_text() if follower_el else "N/A"

        # Lấy link các board
        board_links = await page.query_selector_all("a[href*='/" + username + "/']")
        boards = []
        for bl in board_links:
            href = await bl.get_attribute("href") or ""
            name = await bl.get_attribute("aria-label") or href.split("/")[-2]
            if href.count("/") >= 3 and href != f"/{username}/":
                boards.append({"name": name, "url": f"https://www.pinterest.com{href}"})

        await browser.close()
        return {"username": username, "followers": followers, "boards": boards}

Để scrape toàn bộ pin trên mọi board của một hồ sơ, hãy lặp qua danh sách board và gọi scrape_board() cho từng board. Bạn cũng có thể tự động sắp xếp ảnh đã tải xuống vào từng thư mục riêng theo board.

Xây Dựng Bộ Xử Lý Infinite Scroll Chuẩn Production

Đây là phần phân biệt giữa một scraper chơi thử và một scraper thật sự dùng được. Vấn đề số 1 — và tôi đã thấy nó trong ít nhất một tá thread trên forum — là scraper chỉ trả về 16–25 mục vì nó không cuộn đủ xa, hoặc dùng số lần cuộn cố định kiểu for i in range(5): scroll() rồi cầu may.

Cách đó không ổn định. Pinterest tải nội dung mới theo từng đợt khoảng 25 pin, kích hoạt bởi sự kiện cuộn. Nếu bạn cuộn 5 lần, có thể bạn lấy được 125 pin — hoặc chỉ 75 nếu mạng chậm, hoặc 150 nếu các đợt nhỏ hơn. Bạn cần một pattern thông minh hơn.

Pattern Cuộn Đến Khi Không Còn Nội Dung Mới

Đây là một hàm scroll vững hơn, theo dõi ID pin duy nhất, dùng timeout có thể cấu hình, có retry logic, và in tiến trình:

import time
import random

async def scroll_and_collect(page, max_pins=1000, max_scrolls=200, scroll_pause=2.5):
    seen_ids = set()
    all_pins = []
    no_new_count = 0

    for i in range(max_scrolls):
        pins = await page.query_selector_all("div[data-test-id='pinWrapper']")
        new_this_round = 0

        for pin in pins:
            link = await pin.query_selector("a")
            if not link:
                continue
            href = await link.get_attribute("href") or ""
            if href in seen_ids:
                continue
            seen_ids.add(href)
            new_this_round += 1
            title = await link.get_attribute("aria-label") or ""
            img = await link.query_selector("img")
            src = await img.get_attribute("src") if img else ""
            all_pins.append({
                "title": title,
                "url": f"https://www.pinterest.com{href}" if href.startswith("/") else href,
                "image_url": src
            })

        print(f"  Cuộn {i+1}: {new_this_round} pin mới | {len(all_pins)} pin duy nhất tổng cộng")

        if len(all_pins) >= max_pins:
            print(f"  Đã đạt giới hạn max_pins ({max_pins}). Dừng lại.")
            break

        if new_this_round == 0:
            no_new_count += 1
            if no_new_count >= 3:
                print("  Không có pin mới sau 3 lần cuộn liên tiếp. Hết nội dung.")
                break
        else:
            no_new_count = 0

        prev_height = await page.evaluate("document.body.scrollHeight")
        await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
        await asyncio.sleep(scroll_pause + random.uniform(0.5, 1.5))
        curr_height = await page.evaluate("document.body.scrollHeight")

        if curr_height == prev_height and new_this_round == 0:
            print("  Chiều cao trang không đổi và không có pin mới. Có thể đã hết feed.")
            break

    return all_pins

Vì sao thiết kế này hiệu quả:

  • Khử trùng lặp bằng href: URL của mỗi pin là duy nhất, nên ta dùng nó làm ID. Điều này giúp tránh đếm trùng khi DOM render lại trong lúc cuộn.
  • Luật ba lần không có dữ liệu mới: Nếu ba lần cuộn liên tiếp không có pin mới, ta dừng. Cách này xử lý tình huống trang vẫn đang tải nhưng không còn nội dung mới.
  • Thêm độ trễ ngẫu nhiên: Chèn 0.5–1.5 giây delay ngẫu nhiên giữa các lần cuộn làm hành vi giống người thật hơn và giảm nguy cơ bị anti-bot phát hiện.
  • Giới hạn max scrolls: Ngăn vòng lặp vô hạn nếu có sự cố.

Xử Lý Các Trường Hợp Đặc Biệt

  • Ngắt bởi "More Ideas": Trên trang board, Pinterest đôi khi chèn một phần "More Ideas". Nếu bạn chỉ muốn các pin thực sự thuộc board, hãy kiểm tra phần tử này và dừng cuộn khi nó xuất hiện.
  • Bị giới hạn tốc độ trong phiên dài: Nếu bạn đang cuộn một board có hàng nghìn pin, Pinterest có thể bắt đầu làm chậm phản hồi. Nếu thấy thỉnh thoảng các lần cuộn không ra pin mới (nhưng chưa đến 3 lần liên tiếp), hãy tăng thời gian pause lên 5 giây hoặc hơn.

Lấy Ảnh Pinterest Độ Phân Giải Cao (Không Phải Thumbnail)

Phần này thường khiến mọi người phát điên. Bạn scrape được một loạt pin, tải ảnh xuống, và tất cả đều là thumbnail 236px bé tí. Trên forum, người dùng mô tả nó kiểu “chất lượng rác, bé quá mức”. Cách sửa là hiểu cấu trúc URL ảnh của Pinterest.

Hiểu Đường Dẫn URL Ảnh Pinterest

Tất cả ảnh Pinterest đều được phục vụ từ https://i.pinimg.com/{size}/{hash}.jpg. Phần {size} quyết định độ phân giải:

Đường dẫn kích thướcKích thướcCách dùng
/236x/rộng 236pxDạng lưới mặc định (thứ bạn nhận được ban đầu)
/474x/rộng 474pxĐộ phân giải trung bình
/736x/rộng 736pxXem chi tiết pin / ảnh mở rộng
/originals/Kích thước gốc khi uploadĐộ phân giải đầy đủ

Hàm Tiện Ích: Nâng Bất Kỳ URL Ảnh Pinterest Nào Lên Chất Lượng Cao Nhất

Đây là một hàm rewrite URL ảnh Pinterest sang chất lượng cao nhất có thể, kèm logic dự phòng:

import requests as req

def upgrade_image_url(url, preferred_size="originals"):
    """Viết lại URL ảnh Pinterest sang độ phân giải cao nhất có thể."""
    sizes = ["originals", "736x", "474x", "236x"]
    if preferred_size not in sizes:
        preferred_size = "originals"

    for size in sizes[sizes.index(preferred_size):]:
        upgraded = url
        for s in sizes:
            upgraded = upgraded.replace(f"/{s}/", f"/{size}/")
        try:
            resp = req.head(upgraded, timeout=5, allow_redirects=True)
            if resp.status_code == 200:
                return upgraded
        except Exception:
            continue
    return url  # Trả lại URL gốc nếu tất cả đều thất bại

Lưu ý quan trọng (tính đến năm 2025): Đường dẫn /originals/ ngày càng thường trả về lỗi HTTP 403 Forbidden. Một issue đã được ghi nhận trong gallery-dl xác nhận hành vi này vào giữa năm 2025. Mức tối đa đáng tin cậy hiện nay là /736x/. Hàm của tôi sẽ thử /originals/ trước, rồi tự động lùi về /736x/.

Tải Ảnh Về Các Thư Mục Được Sắp Xếp Gọn Gàng

import os
import time

def download_images(pins, folder="images/default", delay=1.5):
    os.makedirs(folder, exist_ok=True)
    for i, pin in enumerate(pins):
        img_url = upgrade_image_url(pin.get("image_url", ""), preferred_size="736x")
        if not img_url:
            continue
        filename = f"pin_{i+1}.jpg"
        filepath = os.path.join(folder, filename)
        try:
            resp = req.get(img_url, timeout=15)
            if resp.status_code == 200:
                with open(filepath, "wb") as f:
                    f.write(resp.content)
                print(f"  Đã tải {filename} ({len(resp.content) // 1024} KB)")
            else:
                print(f"  Tải thất bại {filename}: HTTP {resp.status_code}")
        except Exception as e:
            print(f"  Lỗi khi tải {filename}: {e}")
        time.sleep(delay + random.uniform(0.3, 0.8))

Hãy thêm độ trễ giới hạn tốc độ giữa các lần tải. Tôi thường dùng 1.5–2.3 giây kèm jitter. Nếu không, Pinterest sẽ chặn IP của bạn sau vài trăm request.

Xuất Dữ Liệu Pinterest Đã Scrape

Xuất Sang CSV Hoặc JSON

Phần cơ bản chúng ta đã nói ở trên. Với bộ dữ liệu lớn hơn (10.000+ pin), bạn nên cân nhắc định dạng JSON Lines — một object JSON trên mỗi dòng — vì dễ stream và xử lý hơn:

def save_jsonl(data, filepath="output/pins.jsonl"):
    with open(filepath, "w", encoding="utf-8") as f:
        for item in data:
            f.write(json.dumps(item, ensure_ascii=False) + "\n")

Xuất Sang Google Sheets, Airtable Hoặc Notion

Nếu bạn muốn đẩy dữ liệu trực tiếp từ Python sang Google Sheets, bạn sẽ cần thư viện gspread và một Google Cloud service account. Với Airtable, dùng pyairtable. Với Notion, dùng notion-client. Mỗi lựa chọn đều cần thiết lập API key và làm pipeline phức tạp hơn đáng kể.

Hoặc — và tôi có phần thiên vị ở đây, nhưng đây thực sự là đường nhanh nhất — bạn có thể dùng Thunderbit để scrape Pinterest và xuất sang bất kỳ đích nào trong số này chỉ với một cú nhấp. Không cần API key, không cần service account, không cần code thêm. Thunderbit Chrome Extension xử lý phần xuất dữ liệu sẵn ngay trong công cụ.

Mẹo Để Tránh Bị Chặn Khi Scrape Pinterest

Hệ thống chống bot của Pinterest được ScrapeOps chấm mức 6/10 về độ khó vượt qua — không hề đơn giản, nhưng cũng chưa phải mục tiêu khó nhất. Nó dùng fingerprint trình duyệt, phân tích hành vi và giới hạn tốc độ theo IP. Những gì hiệu quả là:

  • Luân phiên user agent: Dùng một pool user agent Chrome thật và chọn ngẫu nhiên cho mỗi phiên.
  • Thêm độ trễ ngẫu nhiên: 2–5 giây giữa các lần cuộn và request, kèm jitter. Nếu không dùng proxy, hãy tăng lên 10–15 giây.
  • Dùng viewport hợp lý: Đặt viewport={"width": 1920, "height": 1080} — đừng dùng kích thước quá nhỏ hoặc quá lạ.
  • Cân nhắc proxy khi scale lớn: Nếu bạn scrape hàng nghìn pin, hãy luân phiên residential proxy. Nếu không, rất có thể IP sẽ bị chặn sau vài trăm request.
  • Tôn trọng robots.txt: robots.txt của Pinterest chặn phần lớn crawler tự động và có khoảng 180 rule disallow. Hãy để ý điều này để đảm bảo tuân thủ.
  • Tránh scrape khi đã đăng nhập: Hãy chỉ lấy nội dung công khai khi đang đăng xuất. Scrape phía sau đăng nhập sẽ làm tăng cả rủi ro pháp lý lẫn kỹ thuật.

Thunderbit tự động xử lý anti-bot và CAPTCHA bằng AI engine của nó — đỡ cho bạn một thứ phải bảo trì nếu đi theo hướng no-code.

Cân Nhắc Pháp Lý Và Đạo Đức Khi Scrape Pinterest

Tôi sẽ nói ngắn gọn vì đây không phải trọng tâm chính của bài viết, nhưng nó rất quan trọng.

Điều khoản sử dụng của Pinterest (Mục 2a) nêu rằng bạn đồng ý không được "scrape, collect, search, copy hoặc truy cập dữ liệu/nội dung từ Pinterest theo cách không được phép, chẳng hạn bằng phương tiện tự động (khi chưa có sự cho phép rõ ràng từ trước của chúng tôi)." Tuy nhiên, các toà án nhìn chung đã cho rằng scraping dữ liệu công khai không vi phạm Computer Fraud and Abuse Act — xem vụ hiQ v. LinkedIn và Meta v. Bright Data (tháng 1/2024), nơi toà án phán quyết rằng việc scrape dữ liệu công khai khi đăng xuất là hợp pháp.

Một vài nguyên tắc cơ bản:

  • Chỉ scrape nội dung công khai khi đang đăng xuất
  • Không dùng dữ liệu scrape được để spam hoặc giả mạo người dùng
  • Tôn trọng bản quyền hình ảnh — ưu tiên trích xuất metadata khi có thể, và tránh tái phân phối ảnh có bản quyền cho mục đích thương mại nếu không có quyền
  • Nếu bạn định dùng dữ liệu scrape cho mục đích thương mại, hãy trao đổi với luật sư

Để tìm hiểu kỹ hơn về bối cảnh pháp lý, xem hướng dẫn về tác động pháp lý của web scraping của chúng tôi.

Kết Luận: Bạn Đã Học Được Gì Và Nên Làm Gì Tiếp Theo

Bây giờ bạn đã hiểu vì sao cách scrape tĩnh không hoạt động trên Pinterest (đây là một React SPA — không có JavaScript thì không có dữ liệu), cách dùng Playwright để scrape kết quả tìm kiếm, board và hồ sơ người dùng, cách xây dựng bộ xử lý infinite scroll chuẩn production không bị kẹt ở 16 pin, và cách lấy ảnh độ phân giải cao thay vì thumbnail nhỏ xíu.

Tóm tắt nhanh những điều quan trọng:

  • requests + BeautifulSoup không hoạt động trên Pinterest. Đừng mất thời gian.
  • Playwright là công cụ Python phù hợp nhất cho bài toán này — nhanh, được hỗ trợ tốt, và render JS rất tự nhiên.
  • Infinite scroll cần một vòng lặp cuộn dựa trên khử trùng lặp, không phải số lần cuộn cố định.
  • Ảnh full-res cần viết lại đường dẫn URL — ưu tiên /736x/ (vì /originals/ thường trả về 403).
  • Scrape board và profile thường bị các tutorial bỏ sót, nhưng thực ra khá thẳng nếu dùng đúng selector.
  • Với người không viết code hoặc team cần tốc độ, Thunderbit cho phép bạn scrape Pinterest chỉ trong 2 cú nhấp và xuất sang Google Sheets, Excel, Airtable hoặc Notion — không cần Python. Hãy thử miễn phí qua Chrome Extension.

Nếu bạn đang build pipeline Python, code trong hướng dẫn này sẽ cho bạn một nền tảng vững chắc. Nếu bạn chỉ cần dữ liệu, Thunderbit là lối tắt. Dù chọn cách nào, bạn sẽ không còn bị kẹt với 16 pin và một ánh nhìn trống rỗng nữa.

Để tìm hiểu thêm về scraping và trích xuất dữ liệu, hãy xem các hướng dẫn của chúng tôi về cách web scrape bằng Python, công cụ web scraping tự động tốt nhất, và scrape dữ liệu từ website sang Excel. Bạn cũng có thể khám phá bảng giá Thunderbit hoặc xem video hướng dẫn trên kênh YouTube của Thunderbit.

Câu Hỏi Thường Gặp

1. Có thể scrape Pinterest bằng BeautifulSoup không?

Không hiệu quả nếu chỉ dùng một mình. Pinterest render toàn bộ nội dung bằng JavaScript, nên requests + BeautifulSoup chỉ nhìn thấy một khung HTML rỗng. Bạn cần một trình duyệt headless như Playwright hoặc Selenium để render trang trước, hoặc dùng công cụ no-code như Thunderbit để xử lý JS tự động.

2. Một phiên scrape Pinterest có thể lấy được bao nhiêu pin?

Điều đó phụ thuộc vào logic cuộn và cách xử lý anti-bot của bạn. Với bộ xử lý infinite scroll chuẩn production trong hướng dẫn này (khử trùng lặp, timeout, retry logic), bạn có thể scrape ổn định từ hàng trăm đến hàng nghìn pin cho mỗi board hoặc truy vấn tìm kiếm. Với board rất lớn, hãy chuẩn bị cuộn và thu thập trong vài phút.

3. Vì sao ảnh Pinterest scrape được lại nhỏ xíu?

Mặc định, Pinterest phục vụ thumbnail /236x/ ở chế độ lưới. Để lấy độ phân giải cao hơn, hãy viết lại đường dẫn ảnh sang /736x/ hoặc /originals/. Lưu ý rằng từ năm 2025, /originals/ ngày càng thường trả về lỗi 403, nên /736x/ là mức tối đa đáng tin cậy.

4. Scrape Pinterest có hợp pháp không?

Scraping dữ liệu công khai nhìn chung được chấp nhận theo các phán quyết gần đây của toà án (ví dụ: hiQ v. LinkedIn, Meta v. Bright Data), nhưng Điều khoản sử dụng của Pinterest lại cấm truy cập tự động không được phép. Hãy chỉ làm với nội dung công khai, không dùng dữ liệu scrape được để spam, tôn trọng bản quyền và nên hỏi ý kiến pháp lý nếu dùng cho mục đích thương mại.

5. Đâu là giải pháp no-code tốt nhất để scrape Pinterest?

Thunderbit's AI Web Scraper có thể trích xuất dữ liệu pin Pinterest — tiêu đề, ảnh, URL, mô tả — chỉ trong 2 cú nhấp, với tính năng xuất sẵn sang Google Sheets, Excel, Airtable hoặc Notion. Công cụ này xử lý JavaScript rendering, infinite scroll và cả thách thức anti-bot tự động, nên bạn không cần viết hay bảo trì code.

Tìm hiểu thêm

Shuai Guan
Shuai Guan
CEO tại Thunderbit | Chuyên gia Tự động hóa Dữ liệu AI Shuai Guan là CEO của Thunderbit và là cựu sinh viên ngành Kỹ thuật của University of Michigan. Với gần một thập kỷ kinh nghiệm trong lĩnh vực công nghệ và kiến trúc SaaS, anh chuyên biến các mô hình AI phức tạp thành những công cụ trích xuất dữ liệu thực tiễn, không cần viết mã. Trên blog này, anh chia sẻ những góc nhìn thẳng thắn, đã được kiểm chứng qua thực chiến về web scraping và các chiến lược tự động hóa, giúp bạn xây dựng quy trình làm việc thông minh hơn, dựa trên dữ liệu. Khi không tối ưu hóa quy trình dữ liệu, anh áp dụng sự tỉ mỉ đó vào niềm đam mê nhiếp ảnh.
Mục lục

Cào một trang web chỉ bằng cách hỏi

Nói bạn cần gì bằng tiếng Anh đơn giản. Hoặc tốt hơn, không cần nói gì cả.

Dùng Thunderbit ngay miễn phí
Trích xuất dữ liệu bằng AI
Dễ dàng chuyển dữ liệu sang Google Sheets, Airtable hoặc Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week