박카스
박카스
🎻 루키 파트너
🚀 SNS 챌린지 달성자

이제 감으로 쓰지 말자! 데이터로 골라주는 네이버 키워드 자동 추천기 만들기

🟦 소개

블로그를 운영하다 보면
“오늘은 무슨 키워드로 글을 써야 사람들은 많이 보고, 나는 상위노출이 될까…?”
이 고민이 하루도 빠지지 않고 따라옵니다.

검색량은 높은데 경쟁이 너무 치열한 키워드를 잡으면
글을 10개, 20개 써도 노출이 묻혀버리죠.

그래서 저는 결심했습니다.

“네이버는 이미 검색량과 문서수(경쟁량) 데이터를 모두 가지고 있다.
그럼 이걸 자동으로 뽑아서 ‘좋은 키워드’만 골라주면 최고 아닌가?”

그래서 만들어진 것이 바로…

🔥 ‘블로그 자동 키워드 분석 시스템’

  • 네이버 검색광고 API → 검색량(시장 수요)

  • 네이버 검색 API → 문서수(경쟁 정도)

  • 파이썬으로 자동 계산 → 경쟁률이 낮은 키워드 자동 선별

이 시스템이면 이제:

✔ 어떤 키워드를 먼저 써야 하는지
✔ 어떤 키워드는 피해야 하는지
✔ 어디에 ‘숨은 황금 키워드’가 있는지

모두 자동으로 눈앞에 펼쳐집니다.


🟦 진행 방법

🌳 전체 구조를 비유로 설명해볼게요

이 시스템을 만들기 위해서는
네이버 본사 건물 두 곳을 방문해야 합니다.

  • 1번 건물: 네이버 검색광고 API — ‘시장 정보 센터’
    → 사람들이 매달 얼마나 검색하는지 데이터가 있음

  • 2번 건물: 네이버 검색 API — ‘경쟁자 데이터 센터’
    → 해당 키워드로 이미 작성된 블로그 글의 개수가 있음

우리는 이 두 건물에 들어가기 위해
출입카드 3종 세트(API 키) 를 발급받아야 해요.


🧭 1단계 — 네이버 검색광고 API 키 발급 (검색량 담당)

📍 사이트

https://searchad.naver.com/

✔ 해야 할 일

  1. 광고 계정 생성

    공개 옹호
    한국사이트 스크린샷

  2. 검색 광고 클릭 후 상단 메뉴 → API 관리

  3. Access License Key 복사

  4. Secret Key 복사

  5. Customer ID 확인

✔ 목적

이 건물에서는 “월간 검색량(PC·모바일)” 데이터를 제공합니다.

즉,
“사람들이 이 키워드를 얼마나 찾는가?”

수요를 알려주는 핵심 키입니다.


🧭 2단계 — 네이버 검색 API 키 발급 (문서수 담당)

📍 사이트

https://developers.naver.com

✔ 해야 할 일

  1. 로그인 → 상단 Application → ‘애플리케이션 등록’

  2. 한국 웹사이트의 신청 페이지 스크린샷
  3. API 선택: 검색(Search)

  4. Client ID, Client Secret 발급

  5. 메모해두기

✔ 목적

이 건물에서는 “블로그 문서수(total)” 데이터를 줍니다.

즉,
“이 키워드로 이미 글을 몇 개나 써놨는가?”

경쟁량을 알려주는 핵심 키입니다.


🧭 3단계 — API 키값을 파이썬 환경에 등록

보통 .env 로 넣지만 이번 실습은 코드 안에 바로 넣는 형태로 구성되어 있습니다.

NAVER_SEARCH_ACCESS_LICENSE_KEY = "발급받은 Access Key 입력"
NAVER_SEARCH_SECRET_KEY = "발급받은 Secret Key 입력"
NAVER_SEARCH_CUSTOMER_ID = "광고센터 Customer ID"
NAVER_BLOG_CLIENT_ID = "네이버 검색 API Client ID"
NAVER_BLOG_CLIENT_SECRET = "네이버 검색 API Secret"

이건 마치
“출입카드 번호를 내 프로그램에게 알려주는 과정”
이라고 생각하시면 됩니다.


🧭 4단계 — 파이썬 프로그램 실행

✔ 초보자도 한 번에 이해할 수 있게 전체 코드는 아래에 그대로 제공합니다.

import requests
import hmac
import hashlib
import base64
import time
import json
import urllib.parse
from collections import deque
import openpyxl
from openpyxl.utils import get_column_letter

# 네이버 검색광고 API 정보
NAVER_SEARCH_ACCESS_LICENSE_KEY = "01000000001f114edd83ad29eceaf97acf89cbcbc27283150884065a5467a4cb19efff08ee"
NAVER_SEARCH_SECRET_KEY = "AQAAAAAfEU7dg60p7Or5es+Jy8vC48WR8I2Y9hX4SYlEpORx2g=="
NAVER_SEARCH_CUSTOMER_ID = "4180063"
NAVER_API_BASE_URL = "https://api.naver.com"

# 네이버 검색 API (블로그 검색) 정보
NAVER_BLOG_CLIENT_ID = "O6AVg7D5tucjFH72rYJQ"
NAVER_BLOG_CLIENT_SECRET = "_TWdNmG_aN"

def generate_signature(secret_key: str, timestamp: str, method: str, request_uri: str) -> str:
    """
    네이버 검색광고 API 요청에 필요한 시그니처를 생성합니다.
    """
    message = f"{timestamp}.{method}.{request_uri}"
    h = hmac.new(secret_key.encode('utf-8'), message.encode('utf-8'), hashlib.sha256)
    return base64.b64encode(h.digest()).decode('utf-8')

def get_keyword_stats(access_key: str, secret_key: str, customer_id: str, hint_keywords: list) -> dict:
    """
    지정된 키워드에 대한 통계 정보를 네이버 검색광고 API로부터 가져옵니다.
    """
    request_uri = "/keywordstool"
    method = "GET"
    timestamp = str(int(time.time() * 1000))

    signature = generate_signature(secret_key, timestamp, method, request_uri)

    headers = {
        "X-Timestamp": timestamp,
        "X-API-KEY": access_key,
        "X-Customer": customer_id,
        "X-Signature": signature
    }

    params = {
        "hintKeywords": ",".join(hint_keywords),
        "showDetail": "1"  # 상세 통계 정보 포함
    }

    url = f"{NAVER_API_BASE_URL}{request_uri}"

    try:
        response = requests.get(url, headers=headers, params=params)
        response.raise_for_status()  # HTTP 오류 발생 시 예외 발생
        return response.json()
    except requests.exceptions.HTTPError as err:
        print(f"HTTP 오류 발생: {err}")
        print(f"응답 내용: {response.text}")
        return {}
    except requests.exceptions.RequestException as err:
        print(f"요청 오류 발생: {err}")
        return {}

def get_document_count(keyword: str, client_id: str, client_secret: str) -> int:
    """
    네이버 블로그 검색 API를 사용하여 특정 키워드에 대한 문서 수를 가져옵니다.
    """
    encText = urllib.parse.quote(keyword)
    url = f"https://openapi.naver.com/v1/search/blog?query={encText}&display=1"

    headers = {
        "X-Naver-Client-Id": client_id,
        "X-Naver-Client-Secret": client_secret
    }

    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        data = response.json()
        return data.get("total", 0)
    except requests.exceptions.HTTPError as err:
        print(f"HTTP 오류 발생 (문서수): {err}")
        print(f"응답 내용 (문서수): {response.text}")
        return 0
    except requests.exceptions.RequestException as err:
        print(f"요청 오류 발생 (문서수): {err}")
        return 0

if __name__ == "__main__":
    input_keywords_str = input("검색할 초기 키워드를 입력하세요 (쉼표로 구분): ")
    initial_keywords = [kw.strip() for kw in input_keywords_str.split(',') if kw.strip()]

    if not initial_keywords:
        print("입력된 키워드가 없습니다. 프로그램을 종료합니다.")
    else:
        num_keywords_to_process = input("몇 개의 키워드를 조회하시겠습니까? (숫자를 입력하세요): ")
        try:
            num_keywords_to_process = int(num_keywords_to_process)
            if num_keywords_to_process <= 0:
                print("키워드 조회 개수는 양수여야 합니다. 프로그램을 종료합니다.")
                exit()
        except ValueError:
            print("잘못된 숫자 형식입니다. 프로그램을 종료합니다.")
            exit()

        keyword_queue = deque(initial_keywords)
        processed_keywords = set()
        all_keyword_data = [] # 모든 키워드 데이터를 저장할 리스트

        # 엑셀 워크북 및 시트 설정
        workbook = openpyxl.Workbook()
        sheet = workbook.active
        sheet.title = "키워드 검색량"

        # 헤더 작성
        headers = ["키워드", "월간 PC 검색량", "월간 모바일 검색량", "총 월간 검색량", "문서수", "경쟁률"]
        sheet.append(headers)

        # 엑셀에 기록된 고유 키워드의 개수를 추적합니다.
        keywords_recorded_count = 0

        while keyword_queue and keywords_recorded_count < num_keywords_to_process:
            current_keyword = keyword_queue.popleft()

            if current_keyword in processed_keywords:
                print(f"'{current_keyword}' 키워드는 이미 조회했거나 처리 중입니다. 건너킵니다.")
                continue

            processed_keywords.add(current_keyword) # 이 키워드는 이미 처리되었으므로 추가합니다 (API 호출)..

            print(f"'{current_keyword}' 키워드의 검색량 정보를 가져옵니다...")

            stats_data = get_keyword_stats(
                NAVER_SEARCH_ACCESS_LICENSE_KEY,
                NAVER_SEARCH_SECRET_KEY,
                NAVER_SEARCH_CUSTOMER_ID,
                [current_keyword]
            )

            if stats_data and "keywordList" in stats_data:
                keyword_list = stats_data["keywordList"]
                if isinstance(keyword_list, list):
                    for item in keyword_list:
                        rel_keyword = item.get("relKeyword", "N/A")

                        # 이 관련 키워드가 아직 엑셀에 기록되지 않았고, 목표 개수에 도달하지 않았다면 기록합니다.
                        if rel_keyword not in processed_keywords and keywords_recorded_count < num_keywords_to_process:
                            monthly_pc_qc_cnt = item.get("monthlyPcQcCnt", "N/A")
                            monthly_mobile_qc_cnt = item.get("monthlyMobileQcCnt", "N/A")

                            total_monthly_qc_cnt = "N/A"
                            try:
                                pc_qc = int(monthly_pc_qc_cnt) if monthly_pc_qc_cnt not in ["<10", "N/A"] else 0
                                mobile_qc = int(monthly_mobile_qc_cnt) if monthly_mobile_qc_cnt not in ["<10", "N/A"] else 0
                                total_monthly_qc_cnt = pc_qc + mobile_qc
                                if "<10" in [monthly_pc_qc_cnt, monthly_mobile_qc_cnt] and total_monthly_qc_cnt < 10:
                                    total_monthly_qc_cnt = "<10"
                                elif total_monthly_qc_cnt == 0 and "<10" in [monthly_pc_qc_cnt, monthly_mobile_qc_cnt]:
                                     total_monthly_qc_cnt = "<10"
                            except ValueError:
                                pass
                            
                            # 문서수 조회
                            document_count = get_document_count(rel_keyword, NAVER_BLOG_CLIENT_ID, NAVER_BLOG_CLIENT_SECRET)

                            # 경쟁률 계산 (월간총검색량이 0이거나 '<10'인 경우 0으로 처리, 문서수가 0인 경우 N/A)
                            competition_rate = "N/A"
                            try:
                                total_qc_for_calc = 0
                                if isinstance(total_monthly_qc_cnt, int):
                                    total_qc_for_calc = total_monthly_qc_cnt
                                elif total_monthly_qc_cnt == "<10":
                                    total_qc_for_calc = 5 # <10인 경우 대략 5로 가정하여 계산

                                if total_qc_for_calc > 0 and document_count > 0:
                                    competition_rate = round(document_count / total_qc_for_calc, 2)
                                elif document_count == 0:
                                    competition_rate = "N/A" # 문서수가 없으면 경쟁률을 계산할 수 없음
                                elif total_qc_for_calc == 0:
                                    competition_rate = "N/A" # 검색량이 없으면 경쟁률을 계산할 수 없음
                            except Exception as e:
                                print(f"경쟁률 계산 중 오류 발생: {e}")
                                competition_rate = "N/A"

                            print(f"키워드: {rel_keyword}")
                            print(f"  월간 PC 검색량: {monthly_pc_qc_cnt}")
                            print(f"  월간 모바일 검색량: {monthly_mobile_qc_cnt}")
                            print(f"  총 월간 검색량: {total_monthly_qc_cnt}")
                            print(f"  문서수: {document_count}")
                            print(f"  경쟁률: {competition_rate}")
                            print("-" * 30)

                            all_keyword_data.append([
                                rel_keyword,
                                monthly_pc_qc_cnt,
                                monthly_mobile_qc_cnt,
                                total_monthly_qc_cnt,
                                document_count,
                                competition_rate
                            ])
                            sheet.append([rel_keyword, monthly_pc_qc_cnt, monthly_mobile_qc_cnt, total_monthly_qc_cnt, document_count, competition_rate])

                            processed_keywords.add(rel_keyword) # 엑셀에 기록되었으므로, 처리된 키워드로 추가합니다.
                            keywords_recorded_count += 1 # 엑셀에 기록된 키워드 개수 증가

                            # 추가로 탐색할 키워드가 더 필요하고, 이 키워드가 아직 큐에 없다면 큐에 추가합니다.
                            if keywords_recorded_count < num_keywords_to_process and rel_keyword not in keyword_queue:
                                keyword_queue.append(rel_keyword)

                        # 이 관련 키워드가 이전에 처리되지 않았지만, 이미 목표 개수에 도달했다면
                        # 엑셀에는 기록하지 않고, 향후 중복 처리를 막기 위해 processed_keywords에만 추가합니다.
                        elif rel_keyword not in processed_keywords:
                            processed_keywords.add(rel_keyword)

                else:
                    print("API 응답의 'keywordList'가 리스트 형식이 아닙니다.")
                    print(json.dumps(stats_data, indent=2, ensure_ascii=False))
            elif stats_data:
                print("API 응답 형식이 예상과 다릅니다. 원시 응답:")
                print(json.dumps(stats_data, indent=2, ensure_ascii=False))
            else:
                print(f"'{current_keyword}' 키워드 검색량 정보를 가져오는데 실패했습니다.")
        
        # 엑셀 파일 저장
        first_keyword = initial_keywords[0]
        excel_file_name = f"{first_keyword}.xlsx"
        try:
            workbook.save(excel_file_name)
            print(f"키워드 검색량 정보가 '{excel_file_name}' 파일로 저장되었습니다.")
        except PermissionError:
            print(f"오류: '{excel_file_name}' 파일에 접근할 수 없습니다. 파일이 다른 프로그램에서 열려 있는지 확인하고 닫아주세요.")
        except Exception as e:
            print(f"엑셀 파일 저장 중 알 수 없는 오류가 발생했습니다: {e}")

이 코드는 다음과 같은 일을 자동으로 합니다:

① 씨앗 키워드 입력

"창문바람막이"
"AI 영어"
"겨울 외풍"
같은 시작 키워드를 넣습니다.

② 검색광고 API 호출 → 검색량 가져오기

  • PC 검색량

  • 모바일 검색량

  • 연관 키워드 수집

③ 검색 API 호출 → 문서수(total) 가져오기

  • 경쟁 글 개수 조회

④ 경쟁률 계산

경쟁률 = 문서수 ÷ 검색량
낮을수록 좋은 키워드

⑤ 키워드 BFS 확장

"창문바람막이" →
"창틀바람막이" →
"현관문방풍" →
"방문문풍지" …

이런 식으로 자동 확장됨.

⑥ 엑셀로 자동 저장

모든 결과를
창문바람막이.xlsx처럼 자동으로 저장합니다.


🍀 5단계 — 실제 예시 (문틈바람막이.xlsx로 계산)

실제로 프로그램을 돌려 나온 결과는 다음과 같습니다.

키워드

총 월간 검색량

문서수

경쟁률 (문서수 ÷ 검색량)

창문바람막이

8,600

39,304

4.57

현관문방풍

2,460

9,910

4.03

방문문풍지

520

14,360

27.62

창틀바람막이

4,120

6,871

1.67 (최고)

외풍차단시공

700

106,592

152.27 (최악)


🔍 결과 해석

🥇 최고의 키워드: 창틀바람막이 (1.67)

  • 검색량 대비 문서수가 매우 적음

  • 상위노출 가능성 가장 크다

  • “문틈/바람막이” 분야의 핵심 골든키워드


🥈 안정적인 키워드

  • 현관문방풍 – 4.03

  • 창문바람막이 – 4.57

적당한 검색량 + 안정적인 경쟁 수준


🔥 피해야 할 키워드

  • 방문문풍지 – 27.62

  • 외풍차단시공 – 152.27

검색량 대비 문서수가 너무 많음 → 레드오션


🟦 결과와 배운 점

① 검색량만 보는 분석은 반쪽짜리다

문서수(경쟁량)까지 함께 보면 키워드의 질이 완전히 다르게 보인다.

② 경쟁률이 낮을수록 상위노출 가능성이 커진다

이게 전체 프로그램의 핵심 공식.

경쟁률 = 문서수 ÷ 검색량
(낮을수록 좋음)

③ BFS(키워드 주변을 동그랗게 넓혀가며 탐색) 방식이 최고의 키워드 채굴법

연관 키워드를 자동으로 확장해서
예상치 못한 “숨은 황금 키워드”도 계속 발견됨.

🟦 도움 받은 글

  • 네이버 검색광고 API 공식 문서

  • 네이버 Developers 검색 API 문서

  • 오픈소스 BFS 탐색 구조 참고

  • 커서를 통한 오류 디버깅 및 로직 정교화


10
3개의 답글

뉴스레터 무료 구독

👉 이 게시글도 읽어보세요