
Naver Blog Research
Fetch and parse Naver blog pages with shared Python HTTP helpers for Korean-market content and competitor research.
Overview
naver-blog-research is an agent skill most often used in Idea → research (also Grow → content) that provides Python HTTP utilities for safe Naver blog fetching and URL validation.
Install
npx skills add https://github.com/nomadamas/k-skill --skill naver-blog-researchWhat is this skill?
- Shared Python utilities for Naver blog scripts with explicit SSL secure vs insecure contexts for Naver domains
- URL validation restricted to Naver host patterns (.naver.com, .naver.net, .pstatic.net)
- stderr warnings when SSL verification is skipped on Naver targets so callers stay aware of risk
- HTML tag-stripping helper and urlopen wrapper suited to lightweight blog content extraction pipelines
- Domain allowlist covers .naver.com, .naver.net, and .pstatic.net host patterns
Adoption & trust: 1.9k installs on skills.sh; 5.4k GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
You need to research Naver blogs programmatically but keep redoing SSL, domain checks, and fetch boilerplate for every script.
Who is it for?
Solo builders doing Korean-market blog or SEO research who extend Python scripts with Naver-aware HTTP utilities.
Skip if: Non-Naver sources, no-code-only workflows, or users who need a complete blog analytics product without writing script glue.
When should I use this skill?
You are building or running Python workflows that read Naver blogs and need shared SSL, domain validation, and HTTP fetch helpers.
What do I get? / Deliverables
Your agent reuses vetted Naver-domain HTTP helpers so research scripts start from consistent SSL and URL rules instead of one-off scrapers.
- Reusable HTTP/urlopen patterns for Naver blog scripts
- Validated Naver-only URL checks before fetch
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Blog reconnaissance starts in Idea research before you commit to positioning or content calendars. Research subphase fits Naver-specific discovery and scraping utilities rather than product implementation.
Where it fits
Scan top Naver blogs in a niche to see posting frequency and topic clusters before picking a product angle.
Pull recent posts from rival blogs on Naver to compare positioning language and calls to action.
Harvest headings and themes from high-ranking Naver articles to draft a Korean content calendar.
Estimate content competition depth on Naver before committing to a localized landing page scope.
How it compares
Foundation-layer research helpers, not a full MCP server or turnkey competitive-intelligence dashboard.
Common Questions / FAQ
Who is naver-blog-research for?
Indie developers and growth-focused solo builders researching Korean Naver blogs who want reusable Python fetch utilities in agent-driven scripts.
When should I use naver-blog-research?
In Idea → research for niche and competitor scans on Naver; in Grow → content when mining posts for topics, angles, or localization patterns before publishing.
Is naver-blog-research safe to install?
It can disable SSL verification for Naver domains when insecure mode is used; review the Security Audits panel on this Prism page and audit any script before production use.
SKILL.md
READMESKILL.md - Naver Blog Research
__pycache__/ *.pyc naver-images/ """Shared HTTP utilities for Naver blog scripts (SSL handling, URL validation, urlopen wrapper).""" from __future__ import annotations import re import ssl import sys import urllib.error import urllib.parse import urllib.request TAG_RE = re.compile(r"<[^>]+>") _ssl_ctx_secure: ssl.SSLContext | None = None _ssl_ctx_insecure: ssl.SSLContext | None = None def _get_ssl_context(*, insecure: bool = False) -> ssl.SSLContext: global _ssl_ctx_secure, _ssl_ctx_insecure if insecure: if _ssl_ctx_insecure is None: ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE _ssl_ctx_insecure = ctx return _ssl_ctx_insecure if _ssl_ctx_secure is None: _ssl_ctx_secure = ssl.create_default_context() return _ssl_ctx_secure _NAVER_DOMAINS = (".naver.com", ".naver.net", ".pstatic.net") def is_naver_url(url: str) -> bool: host = urllib.parse.urlparse(url).hostname or "" return any(host == d.lstrip(".") or host.endswith(d) for d in _NAVER_DOMAINS) def urlopen(request: urllib.request.Request, timeout: int, *, insecure: bool = False): """urlopen with explicit SSL insecure mode for Naver domains. When *insecure* is True and the target is a Naver domain, SSL certificate verification is skipped. A warning is printed to stderr on every call so the caller is always aware. """ if insecure: if not is_naver_url(request.full_url): raise ValueError("insecure 모드는 네이버 도메인에만 사용할 수 있습니다.") print( "[warn] SSL 인증서 검증이 비활성화되었습니다. 연결이 안전하지 않을 수 있습니다.", file=sys.stderr, ) return urllib.request.urlopen( request, timeout=timeout, context=_get_ssl_context(insecure=True), ) return urllib.request.urlopen(request, timeout=timeout, context=_get_ssl_context()) from __future__ import annotations import argparse import json import os import sys import urllib.error import urllib.request from concurrent.futures import ThreadPoolExecutor, as_completed sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from _naver_http import is_naver_url, urlopen DEFAULT_OUTPUT_DIR = "./naver-images" DEFAULT_MAX = 10 DEFAULT_TIMEOUT = 15 DEFAULT_HEADERS = { "Accept": "image/webp,image/apng,image/*,*/*;q=0.8", "Accept-Language": "ko,en-US;q=0.9,en;q=0.8", "Referer": "https://m.blog.naver.com/", "User-Agent": ( "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36" ), } CONTENT_TYPE_TO_EXT = { "image/jpeg": ".jpg", "image/png": ".png", "image/gif": ".gif", "image/webp": ".webp", "image/bmp": ".bmp", "image/svg+xml": ".svg", } _MAGIC_BYTES = ( (b"\x89PNG\r\n\x1a\n", ".png"), (b"GIF87a", ".gif"), (b"GIF89a", ".gif"), (b"RIFF", ".webp"), # WebP: RIFF....WEBP (check first 4 bytes) (b"BM", ".bmp"), ) def guess_extension(url: str, content_type: str | None = None, data: bytes | None = None) -> str: if content_type: ct = content_type.split(";")[0].strip().lower() if ct in CONTENT_TYPE_TO_EXT: return CONTENT_TYPE_TO_EXT[ct] lower_url = url.lower().split("?")[0] for ext in (".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg"): if lower_url.endswith(ext): return ".jpg" if ext == ".jpeg" else ext if data: for magic, ext in _MAGIC_BYTES: if data[:len(magic)] == magic: if ext == ".webp" and data[8:12] != b"WEBP": continue return ext if data[:2] in (b"\xff\xd8",): return ".jpg" return ".jpg" def download_image(url: str, output_path: str, output_dir: str, timeout: int = DEFAULT_TIMEOUT, *, insecure: bool = False) -> dict: """Download a single image from a Naver CDN URL. *output_dir*