GitHub

네이버 블로그 자동 크롤러 & 번역/DB 업로드 파이프라인 개발기 | python

hojun lee · 06/19/2025
커버이미지

python (selenium / bs4 ) : 블로그 크롤러

perplexity api ( LLM ) : 주소 기반 여행 테마 제공

deepl api : 번역 기능

영어로 된 블로그 하나 만들어볼까?

시작은 이러했다. 마포구에 살고 있는데, 마포구 (홍대 / 공덕 / 망원)에는 사실 외국인 반 한국인 반이다. 이 사람들은 어떻게 여행할까라는 호기심이 들었다. 일단 나의 여행 스타일을 먼저 분석해보면 지도로 여행을 한다. google map과 naver map, kakao map을 돌고 돌고 돌며 평점을 분석하고 리뷰를 체크한다. 그리고 내 기준에서 괜찮은 식당과 여행명소를 선정한다. 그리고 구글이나 네이버 블로그를 검색한다. 결국 자세한 정보를 얻기 위해서는 블로그를 검색하는 방법이 최선이다.

글의 소스는 어디서 구할까.

나는 네이버 블로그를 운영 중이다. 특히 마포구 인근의 맛집을 가보고 후기를 남긴다. 이 정보들을 한국 사람들만 보기엔 아쉽다는 생각을 했다. 그래! 이 소스를 이용해보자. 내가 쓴 글을 이용하는거니 저작권에서도 자유롭고, 컨텐츠를 재사용한다는 측면에서도 괜찮았다.

그래서 크롤러가 필요했다.

크롤러는 파이썬으로 많이 만든다는 애기를 들었다.

파이썬은 크롤러 제작에 특화된 라이브러리가 많고 사용이 간편합니다. 자바스크립트보다 코드가 더 짧아 개발과 유지보수가 쉽습니다. 대규모 데이터 처리와 자동화에 유리한 생태계를 가지고 있습니다.

크롤러 뿐만 아니라 AI 기술을 접목하고 외부 API도 활용하는 방안을 고민하고 실행해본다.

네이버 크롤러 파이프라인 기획

  1. naver rss를 통해 새로운 글이 올라왔는지 확인한다.
  2. 여행/맛집 분야 네이버 블로그 포스트를 자동으로 수집하고.
  3. 영어로 번역해 마크다운 파일로 저장한 뒤
  4. 글의 내용에서 주소와 식당명을 추출 한다.
  5. 식당과 주소를 기반으로 LLM을 통해 여행 코스를 짠다.
  6. 한글.md와 영어.md를 저장한다.
  7. rss 의 내용과 영어.md로 data를 만들고 supabase db에 업로드한다.

쉽게 말해 새 글을 감지한다. URL을 가지고 본문 내용을 크롤링한다. 번역한다. 주소를 기반으로 여행정보를 추가한다. DB에 업로드 한다.

블로그 크롤러 구현

1. 환경 준비와 구조 설계

  • 환경 변수 관리: .env파일로 api키와 DB 접속 정보를 안전하게 관리한다.
  • 모듈화 : 크롤링, 번역, 파일저장, DB연동 등 각 역할을 별도 모듈로 분리해 유지보수성과 확장성을 높인다.
  • 패키지 관리자 : poetry를 활용해 파이썬 버전과 라이브러리를 효율적으로 관리한다.
# 환경변수 관리
from dotenv import load_dotenv
load_dotenv()

// 패키지 관리
poetry install
poetry run python

2. 새글 감지 : 네이버 블로그 RSS 활용

    # 새 포스트 확인
    print("새로운 포스트 확인 중...")
    new_posts = check_new_posts()
    

components.naver_rss.check_new_posts 함수로 네이버 블로그의 RSS 피드를 주기적으로 확인, 새로운 포스트 URL을 자동으로 감지 ( 추후 git action으로 cronjob 기능 ) 이 구조 덕분에 "새 글이 올라오면 자동으로 처리"하는 완전 자동화가 가능

  • json 파일을 통해 기존 크롤링된 파일 주소 확인
  • 새로운 주소가 생겼을 시 특정 카테고리 확인하여 URL을 포함한 정보 추출
  • new_posts 정보를 담아 반환(return)

본문 크롤링 (selenium / bs4)

new_posts가 여러개 일 수도 있다는 전제하에 진행

    # 각 포스트 처리
    success_count = 0
    for post in new_posts:
        print(f"\n[처리 중] {post['title']}")
        if process_blog_post(post['url'], translator):
            success_count += 1
            

post의 url 정보와 처음에 초기화한 deepl 번역기를 함께 넣어줌

crawl_naver_blog.py

전제 사항 이미지는 주소로 따로 저장해둠 ( storage는 그대로 사용예정 ) 본문 내용을 파싱해서 번역 진행 ( deepl api) 한글 본문을 바탕으로 내용을 분석해서 store_and_address 정보 발췌 해당 address를 통해 pplx api를 통해 주소기반 여행코스 3가지 추천 한글.md 와 영문.md로 각각 저장

여기서 의미있었던 건 deepl API와 pplx api를 사용해보았던 점이다.

  • deepl api의 경우 한달에 500000 글자수의 번역을 무료로 제공한다. 30개 정도 블로그 글을 번역했는데 약 95000개의 글자만 사용했다.

해당 컴포넌트의 경우 초기화를 시키고 사용해야하며, 번역의 경우 string을 받아서 번역기를 돌리고 최종 text를 다시반환해준다.

import deepl

def init_translator(API_KEY):
    return deepl.Translator(API_KEY)

MAX_RETRIES = 3  # 최대 재시도 횟수

def translate_text(text, translator, target_lang="EN-US"):
    """
    Translate text using DeepL API
    
    Args:
        text (str): Text to translate
        translator: DeepL translator instance
        target_lang (str): Target language code (default: EN-US)
    
    Returns:
        str: Translated text or original text if translation fails
    """
    if not text.strip():
        return text
        
    retries = 0
    while retries < MAX_RETRIES:
        try:
            result = translator.translate_text(text, target_lang=target_lang)
            return result.text
        except Exception as e:
            print(f"번역 중 에러 발생 (시도 {retries + 1}/{MAX_RETRIES}): {str(e)}")
            retries += 1
    
    print(f"번역 실패 - 원본 텍스트 사용: {text[:50]}...")
    return text
  • pplx api의 경우 perplexity pro버전을 구독하면 월에 5달러 토큰을 제공한다. 간단한 프로젝트에 붙여보긴 좋은 것 같다. pplx의 경우 sonar-pro와 sonar를 제공하는데, 금액과 성능에 차이가 있으니 확인해보길 바란다.

pplx llm을 사용하며 재밌었던 부분은 prompt 였다. prompt의 기획에 따라 엄청나게 다른 결과가 나왔다. 주소를 입력해서 해당 주소와 관련된 여행정보를 얻는 프롬프트를 공유한다.

prompt = f"""
다음 주소와 주변 장소를 기반으로 3가지 여행 코스를 제안해주세요. 각 코스는 2~3시간 코스로 2~3개 장소를 포함해야 합니다. 현실적이고 즐거운 일정을 만들어주세요.

주소: {address}

# 페르소나 (Persona)
당신은 특정 동네를 자기 손바닥 보듯 꿰뚫고 있는 토박이 주민입니다. 당신은 정이 많고, 자신의 동네를 방문하는 사람들에게 숨겨진 보석 같은 장소들을 알려주는 것을 즐깁니다. 상업적인 광고나 블로그에서 흔히 볼 수 있는 뻔한 정보가 아닌, 당신의 실제 경험과 노하우가 담긴 진솔한 이야기를 들려주세요.

# 목표 (Objective)
아래에 제시된 `{address}` 주소를 기반으로, 그 동네의 매력을 흠뻑 느낄 수 있는 여행 코스 3가지를 추천해 주세요. 각 코스는 단순한 장소 나열이 아닌, 테마와 스토리가 있는 하나의 완성된 경험이어야 합니다. 여행객들이 "아, 여기는 정말 와보길 잘했다!"라고 느낄 만한 곳들로 엄선해 주세요.
일목요연하게 요약된 형태로 작성하고 글을 최대 300자를 넘지 않도록 하세요.
정확히 있는 식당과 공원 명칭을 사용하세요. address는 음식점일 확률이 높으니, 디저트나 카페, 공원 등 반드시 실제 명칭을 기반으로 추천해주세요.

# 결과물 형식 (Format)
1.  **세 가지 테마 코스**: '코스 1: [창의적인 코스 제목]', '코스 2: [창의적인 코스 제목]', '코스 3: [창의적인 코스 제목]' 형식으로 세 가지 여행 코스를 제안해 주세요. 제목은 여행객의 호기심을 자극할 수 있도록 지어주세요. (예: '시간이 멈춘 골목길 산책', '인스타그래머를 위한 힙스터 성지 순례', '혼자서 즐기는 사색의 시간')
2.  **구체적인 장소 추천**: 각 코스마다 3~4개의 장소(맛집, 카페, 명소, 상점 등)를 포함해 주세요.
3.  **생생한 추천 이유**: 각 장소를 추천하는 이유를 동네 주민의 시선에서 구체적이고 생생하게 설명해 주세요. (예: '여기는 사장님이 직접 내려주는 드립 커피가 일품인데요, 특히 창가 자리에 앉으면 지나가는 동네 고양이들을 볼 수 있어서 제가 가장 아끼는 자리랍니다.')
4.  **자연스러운 동선**: 각 코스 내 장소들은 자연스럽게 이어지는 동선으로 구성하고, 이동 팁이나 예상 소요 시간을 슬쩍 언급해주세요.

# 톤 앤 매너 (Tone & Style)
*   **친근한 구어체**: 마치 친한 친구나 동생에게 우리 동네를 소개해 주는 것처럼 다정하고 친근한 말투를 사용해 주세요. (예: '~거든요', '~랍니다', '~한번 가보세요')
*   **진솔함**: 과장되지 않고 진솔한 표현을 사용하여 추천에 진정성을 더해주세요.

# 제약 조건 (Constraints)
*   **지리적 근접성**: 추천하는 모든 장소는 주어진 주소에서 도보나 짧은 대중교통으로 쉽게 이동할 수 있어야 합니다.
*   **정확한 정보**: 반드시 실제로 존재하며 현재 운영 중인 장소만 추천해야 합니다.
*   **숨겨진 명소 위주**: 너무 유명해서 관광객으로 붐비는 곳보다는, 동네 주민들이 실제로 아끼고 사랑하는 숨겨진 장소 위주로 추천해 주세요. 만약 유명한 곳을 포함한다면, 그곳을 남들과는 다르게 즐길 수 있는 특별한 팁을 함께 제공해야 합니다.
"""

우리도 여행을 가면 현지인들의 맛집을 가고 싶어하는 경우가 많다. 이런 조건을 프롬프트에 반영했다. 결과는 나름 재밌고 의미있게 나온다.

# 여행 코스 추천

안녕하세요 마포구 독막로에 위치한 대흥브릭스에서 시작하는 여행 코스를 추천해 드리겠습니다. 각 코스는 2~3시간 정도 소요되며, 주변의 숨겨진 보석 같은 장소들을 포함하고 있습니다.

## 코스 1: **힙스터의 하모니**
- **줄라**: 독막로9길에 위치한 이곳은 서교동에서 가장 아름다운 분위기를 제공합니다. 특히 인테리어와 음식이 잘 어우러져서 힙스터들이 사랑하는 곳입니다.
- **잇텐바리**: 독막로7길에 위치한 이곳은 독특한 분위기와 맛있는 음식을 제공합니다. 여유로운 시간을 즐기기에 딱입니다.
- **서교동 거리**: 줄라와 잇тен바리 근처에 위치한 서교동 거리는 다양한 카페와 상점을 볼 수 있어 산책하기 좋습니다.

## 코스 2: **인스타그램을 위한 스냅**
- **피쉬버켓 합정역**: 독특한 인테리어와 맛있는 음식을 제공하는 이곳은 사진을 찍기에도 좋습니다.
- **색다른한잔 합정**: 합정의 분위기를 즐길 수 있는 이곳은 다양한 주류와 음식을 제공합니다.
- **합정거리**: 합정의 주변 거리 산책을 통해 다양한 힙스터 문화를 경험할 수 있습니다.

## 코스 3: **사색의 시간**
- **진미식당**: 독막로 312에 위치한 이곳은 정겨운 분위기에서 맛있는 한식 요리를 즐길 수 있습니다.
- **리치몬드과자점**: 독특한 디저트를 제공하는 이곳은甜蜜한 순간을 선사합니다.
- **서교공원**: 주변 공원에서 여유로운 시간을 보내며 자연을 즐길 수 있습니다.

이러한 코스들은 마포구의 다양한 매력을 흠뻑 느낄 수 있도록 구성되었습니다. 각 장소는 도보나 짧은 대중교통으로 이동할 수 있어 편리합니다. 즐거운 여행이 되길 바랍니다

마크다운 파일 저장(한글/영문)

파일명에 사용할수 없는 문자들이 있다(특수문자 / 등등) 이를 제거하는 senitize_fileName을 사용하여 파일저장 오류를 차단하고. 한글/영문 마크다운 파일을 각각 kor/ eng/ 폴더에 저장한다.

# 파일명 오류로 저장안되는 오류가 있어서 세니타이즈
def sanitize_filename(filename: str) -> str:
    return re.sub(r'[\\/*?:"<>|]', '', filename)


        # 2. 마크다운 파일 저장 (한글/영문)
        file_paths = save_markdown_files(
            result['content_parts'],
            result['content_parts_en'],
            result['safe_title'],
            result['eng_title']
        )

마지막으로 supabase DB저장

result를 가지고 DB 테이블 컬럼 모양에 따라 전처리를 거친다. 번역된 영문 포스트를 Supabase engPost테이블에 업로드한다.

개발 팁 / 확장 아이디어

  • 테스트 코드 : pytest 기반 단위 테스트로 각 기능의 신뢰성을 확보한다.
  • 환경 변수 관리 및 컴포넌트 추상화 진행
  • 주소를 기반으로한 지도 기반 여행테마 추천

결론

네이버 블로그의 여행/맛집 콘텐츠를 전자동으로 글로벌화는 완성형 파이프라인이다. 실제 코드 구조와 모듈의 역할을 명확하게 분리하기 위해 힘썼다.

나보다 GPT 4.1이 더 힘썼다. 에이전트의 도움을 많이 받았다. 파이썬 코드는 잘 모르기때문이다. 이번 프로젝트는 기획과 진행 상황에 대한 점검을 더 많이했다. 기획까지 맡겨줘 가 아닌 이렇게 이렇게 구현할 건데 아이디어줘. 테스트해봐. 진행해봐 위주로 프로젝트를 진행했고, 그 결과는 내가 원하는 대로 나와서 좋았다. AI에 휘둘리지 않고 컨트롤 하는 능력이 향상 된 느낌이다.

그저 프론트엔드 화면을 구현하고 싶었는데, 그 이면에는 더 나은 데이터구조가 필요하다. 이 데이터를 가지고 UI 화면 구현을 진행해보도록 하겠다.

화면은 Nextjs로 진행하겠다.