# Naver Blog Scraper (Search + Full Posts) (`frank_conifer_af4/naver-blog-scraper`) Actor

Scrape Naver Blog by keyword, blog ID or post URL. Get clean full post text, images, tags, places, like and comment counts and a sponsored-post hint, or cheap search rows only. Supports incremental monitoring.

- **URL**: https://apify.com/frank_conifer_af4/naver-blog-scraper.md
- **Developed by:** [Juddroid](https://apify.com/frank_conifer_af4) (community)
- **Categories:** Social media, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 full blog posts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Naver Blog Scraper (Search + Full Posts)

Turn Naver Blog into clean, structured data: search by keyword, crawl whole blogs, or paste post URLs, and get full post text with images, tags, places, likes, comments and a sponsored-post hint.

### What does Naver Blog Scraper do?

Naver Blog is where Korean consumers write reviews of restaurants, cafes, cosmetics, apps, hospitals and travel spots, so it is the main source for Korean word-of-mouth research. This Actor searches the Naver Blog tab by keyword (sorted by relevance or newest first, with a date range), reads the newest posts of any public blog, and scrapes individual post URLs in any format. For every post it returns the clean body text with paragraph breaks, plus metadata such as author, KST publish time, category, tags, images, place pins, like and comment counts. If you only need the search listing, switch off full posts and pay a third of the price per row.

### Why use it

- **Three input modes in one run.** Keywords, blog IDs and post URLs can be combined. A post found by several keywords is returned once, with every matching keyword listed in `matchedQueries`.
- **Clean text from every editor generation.** It reads SmartEditor ONE (`se-main-container`), SmartEditor 3 and the legacy 2009-2015 editor, so old posts and tables come out as readable text instead of HTML soup.
- **Sponsored-post detection for marketers.** `isSponsoredHint` flags 협찬, 광고, 소정의 원고료, 제공받아 and similar disclosures. It also catches disclosure banners from review platforms such as Super Members (슈퍼멤버스) and revu.net (레뷰), which other scrapers miss because the disclosure is an image. `ownPurchaseVerified` shows Naver's own 내돈내산 receipt verification.
- **Built for monitoring.** Set `onlyNewSince` to `lastRun` and schedule the Actor. Each keyword and blog remembers what it already delivered, so every run returns only new posts.
- **Place data for local businesses.** Map pins in a post are returned as `places` with name, address, coordinates and the Naver Place link.
- **Fair billing.** Deleted, private, adult (login-only) and empty posts are returned as failed rows and never charged.
- **Polite and resilient.** Low default concurrency, request pacing, backoff on HTTP 429, and a graceful stop with partial results if Naver keeps blocking.

### What data you get

| Field | Description |
|---|---|
| `title`, `url`, `mobileUrl` | Post title and canonical desktop and mobile links |
| `blogId`, `logNo` | Naver blog ID and post number (a stable unique key) |
| `author`, `blogName` | Author nickname and blog name |
| `publishedAt` | Publish time in ISO 8601 with Korea time offset (`+09:00`) |
| `category`, `topic` | The blogger's category and Naver's topic classification (e.g. `맛집>카페`) |
| `text` | Clean body text with paragraph breaks |
| `wordCount`, `charCount` | Word count (space-separated) and character count without spaces |
| `images`, `imageCount` | Image URLs in the post body (stickers and map snapshots excluded) |
| `videos` | Number of embedded videos |
| `links` | Outbound links in the body |
| `tags` | The post's tags |
| `places` | Map pins: name, address, latitude, longitude, Naver Place URL |
| `likeCount`, `commentCount` | Public like (공감) and comment counts |
| `isSponsoredHint`, `sponsoredSignals` | Whether a sponsorship disclosure was found, and which ones |
| `ownPurchaseHint`, `ownPurchaseVerified` | 내돈내산 wording, and Naver's verified receipt badge |
| `searchQuery`, `matchedQueries`, `searchRank` | Keyword that found the post, all matching keywords, position in Naver results |
| `html` | Raw body HTML (only when `includeHtml` is on) |
| `scrapedAt` | When the row was collected |

Search rows (when `scrapeFullPost` is off) contain `title`, `url`, `blogId`, `blogName`, `snippet`, `publishedAt`, `publishedAtText`, `thumbnail`, `rank` and `searchQuery`. Blog feed rows also include `category`, `tags` and, when available, `likeCount` and `commentCount`.

### How to use

1. Enter one or more **search keywords**, **blog IDs** or **post URLs**.
2. For keywords, choose the sort order (relevance or newest first), an optional period, and how many posts you want per keyword.
3. Keep **Scrape full post text** on for full posts, or turn it off for cheaper search rows.
4. For monitoring, set **Only posts published since** to `lastRun` and add a schedule.
5. Click **Start** and download the results as JSON, CSV or Excel, or read them through the API.

```json
{
  "searchQueries": ["성수동 카페", "성수 카페 추천"],
  "sort": "date",
  "period": "1w",
  "maxPostsPerQuery": 50,
  "blogIds": ["csm2469"],
  "maxPostsPerBlog": 20,
  "postUrls": [
    "https://blog.naver.com/baaaba/224441089917",
    "https://m.blog.naver.com/PostView.naver?blogId=gkdudtjr2&logNo=224441926092"
  ],
  "scrapeFullPost": true,
  "onlyNewSince": "lastRun",
  "monitoringKey": "client-a",
  "maxConcurrency": 3
}
```

### Output example

A full post from a local test run (text, images and tags trimmed):

```json
{
  "type": "post",
  "status": "ok",
  "url": "https://blog.naver.com/leesunyuy/224442754984",
  "mobileUrl": "https://m.blog.naver.com/leesunyuy/224442754984",
  "blogId": "leesunyuy",
  "logNo": "224442754984",
  "title": "[ 성수동 카페 디저트 맛집 / 성수 감성카페 ] 비아트 성수",
  "author": "Sunnia",
  "blogName": "써니아의 좌충우돌 신혼라이푸",
  "publishedAt": "2026-10-15T16:50:33+09:00",
  "category": "생활, 노하우, 쇼핑",
  "topic": "맛집>카페",
  "text": "비아트 성수 (서울특별시 성동구 아차산로5길 24-45 2층)\n\n안녕하세요 #소통러 #써니아 입니다\n\n남편과 평일 성수동 데이트로 예쁜\n감성카페 찾아서 비아트성수 왔어요.\n\n성수동의 사람 많고 시끄러운 쪽이\n아닌 다소 한가하고 여유로운 골목길\n위치한 카페로 _ 디저트 맛집 소문난곳! ...",
  "wordCount": 413,
  "charCount": 1441,
  "images": [
    "https://d3i7y4ugnppb9p.cloudfront.net/contract/track/503e0260-c2e0-11f1-9d70-05effd89c856/banner-small.png",
    "https://mblogthumb-phinf.pstatic.net/MjAyNjEwMTVfMjc1/MDAxNzkyMDQ0NjA5ODE3._f4NpfOO92Vpa0-rUH379Zhk8TYeHOCms5esqW8386gg.7HYjzO57E5fvnT5BCFo4DVswzV640CmistvyOnw8yv4g.JPEG/output%EF%BC%BF1833873666.jpg"
  ],
  "imageCount": 23,
  "videos": 0,
  "links": [],
  "tags": ["성수역카페디저트", "성수동카페디저트", "성수감성카페", "성수데이트", "성수카페추천"],
  "places": [
    {
      "name": "비아트 성수",
      "address": "서울특별시 성동구 아차산로5길 24-45 2층",
      "placeId": "1251057335",
      "latitude": 37.5471439,
      "longitude": 127.0542613,
      "placeUrl": "https://m.place.naver.com/place/1251057335"
    }
  ],
  "likeCount": 10,
  "commentCount": 1,
  "isSponsoredHint": true,
  "sponsoredSignals": ["슈퍼멤버스 배너"],
  "ownPurchaseHint": false,
  "ownPurchaseVerified": false,
  "source": "search",
  "searchQuery": "성수동 카페",
  "matchedQueries": ["성수동 카페"],
  "searchRank": 1,
  "editor": "smarteditor-one",
  "scrapedAt": "2026-10-16T08:28:35.425Z"
}
```

A deleted post comes back like this, and is not charged:

```json
{
  "type": "post",
  "status": "failed",
  "url": "https://blog.naver.com/csm2469/224000000001",
  "inputUrl": "https://blog.naver.com/csm2469/224000000001",
  "errorType": "noPost",
  "error": "Post is not available (Naver error type: noPost). It may be deleted, private, or restricted."
}
```

### Pricing

This Actor uses pay-per-event pricing with two events:

| Event | Price | When it is charged |
|---|---|---|
| `blog-post` | $0.0015 per post | A full post saved with non-empty body text |
| `search-result` | $0.0005 per row | A search or blog feed row saved while `scrapeFullPost` is off |

Worked examples:

- The default example (1 keyword, 10 full posts) costs **$0.015**.
- 1,000 full posts for an LLM or RAG dataset cost **$1.50**. 10,000 posts cost **$15**.
- Daily monitoring of 20 keywords in search-row mode with about 30 new posts per keyword per day: 600 rows, or **$0.30 per day**.

Not charged: deleted, private, adult or login-only posts, posts without any text, invalid URLs or blog IDs, duplicates across keywords, posts filtered out by `onlyNewSince`, and the extra requests for search pages and like counts. A post is charged at most once per run. Direct post URLs are always scraped in full and charged as `blog-post`. Set a maximum cost per run in the run options and the Actor stops cleanly when it is reached.

### Integrations and scheduling

- **Schedules:** run hourly or daily with `onlyNewSince: "lastRun"` to receive only new posts. Use a different `monitoringKey` for each client or project.
- **Webhooks:** trigger Slack, email, Zapier, Make or your own endpoint when a run finishes.
- **API:** start runs and download datasets with the Apify API and client libraries for JavaScript and Python.
- **MCP and AI agents:** the Actor works as a tool through the Apify MCP server, so agents can search Naver Blog and read posts in Korean on demand.

### FAQ and limitations

**How many search results can I get per keyword?** Up to 1,000 per keyword. Naver serves 30 results per page, and very broad keywords may stop earlier when Naver ends the listing.

**Why are some search-row dates approximate?** Naver shows recent posts as "3시간 전" or "2주 전". Search rows keep the original text in `publishedAtText` and convert it to an approximate timestamp. Full posts always have an exact `publishedAt`.

**How does "lastRun" work?** The state is stored in the named key-value store `naver-blog-scraper-state`. Each run returns posts that were not delivered before and were published no earlier than one hour before the previous run, to cover search indexing delays. The first run returns everything up to your limits.

**Is the sponsored hint always right?** No. It is a heuristic. It looks for disclosure wording, tags, known review-platform banners and links. A disclosure that exists only as a custom image can be missed, and a post that discusses advertising can be flagged. Use `sponsoredSignals` to review the reason.

**Can it read private, neighbor-only or adult posts?** No. The Actor reads only public posts that anyone can open without logging in. Restricted posts are returned as failed and are not charged.

**I got blocked. What now?** The Actor backs off on HTTP 429 and stops gracefully if blocking continues, keeping everything collected so far. Lower `maxConcurrency`, try again later, or enable Apify Proxy.

**Responsible use.** Only public posts are collected, and the Actor does not log in or bypass access controls. Blog posts are copyrighted by their authors. Analysis, monitoring and research are typical uses, but republishing full posts or images may require the author's permission. Author nicknames and blog IDs are personal data of the bloggers, so handle them in line with PIPA, GDPR and your own data policy, and keep only what you need.

### 한국어 안내

네이버 블로그 검색 결과, 특정 블로그의 최신 글, 개별 포스트 URL을 한 번에 수집해 본문 전체 텍스트와 메타데이터를 JSON, CSV, 엑셀로 받을 수 있는 Actor입니다.

**주요 기능**

- 키워드 검색(관련도순, 최신순, 기간 지정), 블로그 ID별 최신 글 수집, 포스트 URL 직접 입력을 함께 사용할 수 있습니다.
- 스마트에디터 ONE, 스마트에디터 3, 구 에디터 글의 본문을 문단 단위로 깔끔하게 추출합니다.
- 작성자, 발행 시각(KST), 카테고리, 태그, 이미지, 지도 장소, 공감 수, 댓글 수를 함께 제공합니다.
- 협찬, 광고, 소정의 원고료, 제공받아 같은 문구와 슈퍼멤버스, 레뷰 같은 체험단 플랫폼의 배너를 감지해 `isSponsoredHint`로 표시합니다. 내돈내산 영수증 인증 여부도 `ownPurchaseVerified`로 알려 줍니다.
- `onlyNewSince`에 `lastRun`을 넣고 스케줄을 걸면 매번 새 글만 받아 볼 수 있습니다.

**활용 사례**

- **바이럴 모니터링:** 브랜드명, 매장명, 제품명 키워드를 최신순으로 매일 수집해 새 후기를 놓치지 않습니다.
- **체험단/협찬 분석:** 경쟁 브랜드 후기 중 협찬 글의 비율, 협찬 플랫폼, 노출 순위를 비교합니다.
- **리뷰 감성 분석:** 본문 전체 텍스트로 긍정과 부정 의견, 자주 언급되는 메뉴나 기능을 분석합니다.
- **LLM 데이터셋:** 문단 구분이 살아 있는 한국어 본문을 RAG 인덱스나 학습 데이터 구축에 활용합니다. 재배포 시 저작권을 꼭 확인하세요.

**요금:** 본문 포함 포스트 1건당 $0.0015, 검색 결과 행 1건당 $0.0005입니다. 삭제, 비공개, 성인 인증 글과 실패한 항목은 과금되지 않습니다.

**이용 시 유의사항:** 로그인 없이 누구나 볼 수 있는 공개 글만 수집합니다. 글과 이미지의 저작권은 작성자에게 있으므로 분석과 모니터링 용도로 사용하고, 원문을 재게시할 때는 작성자의 허락을 받으세요.

# Actor input Schema

## `searchQueries` (type: `array`):

Korean or English keywords, one per line. Each keyword runs a Naver Blog search (the blog tab on search.naver.com). Posts found by several keywords are returned only once.

## `sort` (type: `string`):

Relevance mirrors Naver's default ranking. Newest first is best for monitoring, because the scraper can stop paging as soon as it reaches posts older than your cutoff.

## `period` (type: `string`):

Limits search results to a publishing period, using Naver's own date filter. Choose 'Custom range' to use the dates below. Applies to keyword search only.

## `dateFrom` (type: `string`):

First day to include (YYYY-MM-DD, Korea time). Used only when 'Published within' is 'Custom range'.

## `dateTo` (type: `string`):

Last day to include (YYYY-MM-DD, Korea time). Leave empty for today. Used only when 'Published within' is 'Custom range'.

## `maxPostsPerQuery` (type: `integer`):

How many results to collect for each keyword. Naver returns 30 results per page, so 30 posts cost one search request. Higher values mean longer runs and higher cost.

## `blogIds` (type: `array`):

Naver blog IDs or blog URLs, one per line (e.g. 'myblog' or 'https://blog.naver.com/myblog'). The scraper reads each blog's newest posts first, via its public RSS feed or the public mobile post list.

## `maxPostsPerBlog` (type: `integer`):

Newest posts to collect from each blog. Up to 50 come from the RSS feed in a single request; more are paged from the mobile post list (24 per request).

## `postUrls` (type: `array`):

Direct links to Naver Blog posts in any format: blog.naver.com/{id}/{logNo}, m.blog.naver.com/..., PostView.naver?blogId=...\&logNo=..., or naver.me short links. Direct URLs are always scraped in full.

## `scrapeFullPost` (type: `boolean`):

On: open every post and return the full body text, images, tags, places, like and comment counts and a sponsorship hint (charged as 'blog-post'). Off: return only the search or feed row (title, snippet, date, thumbnail), which is faster and cheaper (charged as 'search-result').

## `includeLikeCount` (type: `boolean`):

Fetch public like (공감) counts with one batched request per 20 posts. Turn off for slightly faster runs.

## `includeHtml` (type: `boolean`):

Also return the raw post body HTML. Off by default because it makes each item much larger.

## `onlyNewSince` (type: `string`):

Leave empty to get everything. Enter a date such as 2026-10-01 (Korea time) or a full ISO timestamp to skip older posts. Enter 'lastRun' for incremental monitoring: each keyword and blog remembers what it already returned, so scheduled runs only deliver new posts.

## `monitoringKey` (type: `string`):

Used with 'lastRun'. Separate monitors (for example one per client) should use different names. The state is kept in the named key-value store 'naver-blog-scraper-state'.

## `maxConcurrency` (type: `integer`):

How many posts are fetched in parallel. Keep it low (3 is a good default) to stay polite and avoid rate limiting. Higher values are faster but may trigger temporary blocks.

## `proxyConfiguration` (type: `object`):

Optional. Apify Proxy spreads requests over many IPs, which helps large runs. Naver Blog is public, so small runs also work without a proxy.

## Actor input object example

```json
{
  "searchQueries": [
    "성수동 카페"
  ],
  "sort": "relevance",
  "period": "all",
  "maxPostsPerQuery": 10,
  "maxPostsPerBlog": 20,
  "scrapeFullPost": true,
  "includeLikeCount": true,
  "includeHtml": false,
  "monitoringKey": "default",
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQueries": [
        "성수동 카페"
    ],
    "maxPostsPerQuery": 10,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("frank_conifer_af4/naver-blog-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchQueries": ["성수동 카페"],
    "maxPostsPerQuery": 10,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("frank_conifer_af4/naver-blog-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "성수동 카페"
  ],
  "maxPostsPerQuery": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call frank_conifer_af4/naver-blog-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,frank_conifer_af4/naver-blog-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/g1i6ch1YwgkUmA3DW/builds/qNZACHA7KcxBxbIeK/openapi.json
