+ 개발

블로그 크롤링(#네이버 블로그)_코드 설명

AI.Logger 2024. 8. 30. 00:04
  • 수업 내용 리마인드 및 아카이빙 목적의 업로드


Q. 네이버 블로그에서 '특정 키워드(청년취업사관학교)'와 '날짜 범위(20240101~20240731)'에 해당하는 게시물들을 자동으로 수집하고, 각 게시물의 "제목", "작성자 닉네임", "작성일", "본문 내용", "URL"을 추출하여 CSV 파일로 저장하는 코드를 작성하세요.

https://search.naver.com/search.naver?ssc=tab.blog.all&query=%EC%B2%AD%EB%85%84%EC%B7%A8%EC%97%85%EC%82%AC%EA%B4%80%ED%95%99%EA%B5%90&sm=tab_opt&nso=so%3Add%2Cp%3Afrom20240101to20240731

 

청년취업사관학교 : 네이버 블로그검색

'청년취업사관학교'의 네이버 블로그검색 결과입니다.

search.naver.com


A. 네이버 블로그 데이터 수집 스크립트

전체 코드

import requests
from bs4 import BeautifulSoup
import pandas as pd
import json

def clean_text(text):
    return text.replace("\n", "").replace("\u200b", "").replace("\xa0", "").replace("\t", "").replace("\r", "")

def get_blog_item(url):
    tmp = url.split("/")
    url = f'https://blog.naver.com/PostView.naver?blogId={tmp[-2]}&logNo={tmp[-1]}'
    try:
        res = requests.get(url)
        res.raise_for_status()
    except requests.exceptions.RequestException as e:
        print(f"Failed to retrieve blog post: {url}, error: {e}")
        return None
    
    nick = tmp[-2]
    soup = BeautifulSoup(res.text, "html.parser")
    
    try:
        if soup.select_one(".se-title-text"):
            title = soup.select_one(".se-title-text").text
            date = soup.select_one(".se_publishDate").text
            content = soup.select_one(".se-main-container").text
        else:
            title = soup.select_one(".se_title h3").text
            date = soup.select_one(".se_publishDate").text
            content = soup.select(".se_component_wrap")[1].text
        
        title = clean_text(title)
        content = clean_text(content)
        
        return title, nick, date, content, url
    except AttributeError as e:
        print(f"Failed to parse blog post: {url}, error: {e}")
        return None

def get_naver_blog(keyword, startdate, enddate, to_csv=False):
    url = f'https://s.search.naver.com/p/review/48/search.naver?ssc=tab.blog.all&api_type=8&query={keyword}&start=1&nx_search_query=&nx_and_query=&nx_sub_query=&ac=1&aq=0&spq=0&sm=tab_opt&nso=so%3Add%2Cp%3Afrom{startdate}to{enddate}&prank=30&ngn_country=KR&lgl_rcode=09170128&fgn_region=&fgn_city=&lgl_lat=37.5278&lgl_long=126.9602&enlu_query=IggCADqCULhpAAAAj0RP67RmqTWRq3DkdYxhcV7F5RKpJAPG1d9ZSyMVgoc%3D&abt=&retry_count=0'

    ret = []

    while url:
        try:
            res = requests.get(url)
            res.raise_for_status()
            res_dic = json.loads(res.text)
        except requests.exceptions.RequestException as e:
            print(f"Failed to retrieve search results: {url}, error: {e}")
            break
        except json.JSONDecodeError as e:
            print(f"Failed to parse JSON: {url}, error: {e}")
            break

        soup = BeautifulSoup(res_dic.get('contents', ''), 'html.parser')
        url = res_dic.get('nextUrl')
        
        for item in soup.select('.title_area > a'):
            try:
                blog_data = get_blog_item(item['href'])
                if blog_data:
                    ret.append(blog_data)
            except Exception as e:
                print(f"Failed to process blog item: {item['href']}, error: {e}")
        
        if not url:
            break

    df = pd.DataFrame(ret, columns=['title', 'nick', 'date', 'content', 'url'])
    if to_csv:
        df.to_csv(f'blog_{keyword}_{startdate}_{enddate}.csv', index=False)

get_naver_blog('청년취업사관학교', '20240101', '20240731', to_csv=True)

 

코드 설명

 

1. 필요한 모듈 임포트

import requests
from bs4 import BeautifulSoup
import pandas as pd
import json
  • requests: 웹 페이지의 내용을 가져오기 위해 사용됩니다.
  • BeautifulSoup: HTML 파싱을 위해 사용되는 라이브러리입니다.
  • pandas: 데이터를 구조화된 형태(예: DataFrame)로 관리하고, CSV 파일로 저장하기 위해 사용됩니다.
  • json: JSON 형식의 데이터를 다루기 위해 사용됩니다.

 

2. 텍스트 정리 함수 (clean_text)

def clean_text(text):
    return text.replace("\n", "").replace("\u200b", "").replace("\xa0", "").replace("\t", "").replace("\r", "")
  • 이 함수는 텍스트에서 불필요한 공백 문자나 특수 문자를 제거하여 텍스트를 깔끔하게 정리합니다.

 

3. 블로그 게시물 정보 추출 함수 (get_blog_item)

def get_blog_item(url):
    tmp = url.split("/")
    url = f'https://blog.naver.com/PostView.naver?blogId={tmp[-2]}&logNo={tmp[-1]}'
    try:
        res = requests.get(url)
        res.raise_for_status()
    except requests.exceptions.RequestException as e:
        print(f"Failed to retrieve blog post: {url}, error: {e}")
        return None
  • url.split("/"): 블로그 URL을 슬래시(/)를 기준으로 나눕니다.
  • f'https://blog.naver.com/PostView.naver?blogId={tmp[-2]}&logNo={tmp[-1]}': 네이버 블로그의 포스트 뷰 URL 형식에 맞게 새로운 URL을 생성합니다.
  • requests.get(url): 블로그 게시물의 HTML 페이지를 요청합니다.
  • res.raise_for_status(): 요청이 성공적이지 않으면 예외를 발생시킵니다.
    nick = tmp[-2]
    soup = BeautifulSoup(res.text, "html.parser")
  • nick = tmp[-2]: 블로그 작성자의 닉네임을 추출합니다.
  • BeautifulSoup(res.text, "html.parser"): HTML 문서를 파싱하여 soup 객체로 만듭니다.
    try:
        if soup.select_one(".se-title-text"):
            title = soup.select_one(".se-title-text").text
            date = soup.select_one(".se_publishDate").text
            content = soup.select_one(".se-main-container").text
        else:
            title = soup.select_one(".se_title h3").text
            date = soup.select_one(".se_publishDate").text
            content = soup.select(".se_component_wrap")[1].text
  • 블로그 게시물의 제목, 작성일자, 내용이 위치하는 HTML 태그를 선택합니다. 두 가지 형식이 존재할 수 있어 조건문으로 분기 처리합니다.
        title = clean_text(title)
        content = clean_text(content)
        
        return title, nick, date, content, url
    except AttributeError as e:
        print(f"Failed to parse blog post: {url}, error: {e}")
        return None
  • clean_text 함수를 사용해 텍스트를 정리한 후, 제목, 작성자, 작성일, 내용, URL을 반환합니다.
  • 만약 필요한 정보를 찾지 못하면 AttributeError 예외를 처리하고 None을 반환합니다.

 

4. 네이버 블로그 검색 결과 가져오기 (get_naver_blog)

def get_naver_blog(keyword, startdate, enddate, to_csv=False):
    url = f'https://s.search.naver.com/p/review/48/search.naver?ssc=tab.blog.all&api_type=8&query={keyword}&start=1&nx_search_query=&nx_and_query=&nx_sub_query=&ac=1&aq=0&spq=0&sm=tab_opt&nso=so%3Add%2Cp%3Afrom{startdate}to{enddate}&prank=30&ngn_country=KR&lgl_rcode=09170128&fgn_region=&fgn_city=&lgl_lat=37.5278&lgl_long=126.9602&enlu_query=IggCADqCULhpAAAAj0RP67RmqTWRq3DkdYxhcV7F5RKpJAPG1d9ZSyMVgoc%3D&abt=&retry_count=0'
  • 네이버 블로그 검색 URL을 생성합니다. 검색어와 시작일, 종료일을 포함한 URL을 만듭니다.
    ret = []

    while url:
        try:
            res = requests.get(url)
            res.raise_for_status()
            res_dic = json.loads(res.text)
        except requests.exceptions.RequestException as e:
            print(f"Failed to retrieve search results: {url}, error: {e}")
            break
        except json.JSONDecodeError as e:
            print(f"Failed to parse JSON: {url}, error: {e}")
            break
  • 블로그 검색 결과를 가져와 JSON으로 파싱합니다. 요청에 실패하거나 JSON 파싱에 실패하면 반복을 중단합니다.
        soup = BeautifulSoup(res_dic.get('contents', ''), 'html.parser')
        url = res_dic.get('nextUrl')
  • 검색 결과에서 블로그 게시물 링크를 추출할 수 있도록 HTML을 파싱합니다. 다음 페이지가 있으면 nextUrl을 업데이트합니다.
        for item in soup.select('.title_area > a'):
            try:
                blog_data = get_blog_item(item['href'])
                if blog_data:
                    ret.append(blog_data)
            except Exception as e:
                print(f"Failed to process blog item: {item['href']}, error: {e}")
  • 각 블로그 게시물의 링크를 순회하며 get_blog_item 함수를 사용해 게시물 정보를 가져옵니다. 가져온 정보는 ret 리스트에 추가됩니다.
        if not url:
            break
  • 더 이상 검색 결과가 없으면 반복을 종료합니다.

 

5. 데이터프레임 생성 및 CSV 저장

    df = pd.DataFrame(ret, columns=['title', 'nick', 'date', 'content', 'url'])
    if to_csv:
        df.to_csv(f'blog_{keyword}_{startdate}_{enddate}.csv', index=False)
  • 수집한 블로그 데이터를 pandas.DataFrame으로 구조화합니다.
  • to_csv가 True로 설정되면, 데이터프레임을 CSV 파일로 저장합니다.

 

6. 함수 호출

get_naver_blog('청년취업사관학교', '20240101', '20240731', to_csv=True)
  • 키워드 "청년취업사관학교"에 대해 2024년 1월 1일부터 2024년 7월 31일까지의 블로그 게시물을 수집하고, 결과를 CSV 파일로 저장합니다.