Data/Python

셀레니움 없이 네이버 블로그 검색 결과 수집하기 (API & beautifulsoup)

sennysideup 2023. 7. 8. 08:23
반응형

저는 최근에 여러 공모전에 출전하고 있는데요, 그 중 한 공모전에서는 SNS 분석을 요구했습니다. 그렇지만 트위터 API는 올해 2월부터 유료화가 되었고, 인스타그램은 사진 위주라서 텍스트마이닝이 어렵고, 페이스북은 UI가 너무 어렵고.. 이런저런 고민이 많았어요.

결국 수집이 용이한 네이버 블로그를 분석하기로 했습니다. 저는 네이버 API와 Beautifulsoup를 통해 포스팅을 수집했는데요, 오늘은 그 과정에 대해서 포스팅해보려고 합니다.


네이버 API 발급받기

1. 네이버 개발자 센터로 이동 및 로그인

 

NAVER Developers

네이버 오픈 API들을 활용해 개발자들이 다양한 애플리케이션을 개발할 수 있도록 API 가이드와 SDK를 제공합니다. 제공중인 오픈 API에는 네이버 로그인, 검색, 단축URL, 캡차를 비롯 기계번역, 음

developers.naver.com

2. 어플리케이션 등록 클릭 및 등록

사용 API는 '검색'으로 지정해주세요

3. 내 애플리케이션으로 이동

client id와 client secret은 따로 메모장에 저장해주세요. 

API 설정으로 이동해서 아래와 같이 설정을 지정해주세요(사용 API: 검색, 웹서비스 url 추가)

API 설정이 끝났습니다.

4. Documents > 서비스 API > 검색으로 이동

documents를 통해 API 요청 시 투입해야하는 요소를 알 수 있습니다.

검색어, 검색 결과 개수, 검색 시작 위치, 정렬 방법을 넣어주어야 한다는 것을 알 수 있습니다.

 

블로그 검색하기

.py 파일 생성

라이브러리 import

# python version : 3.9
import urllib.request
import re
import pandas as pd
from bs4 import BeautifulSoup
import numpy as np

class로 구현하기

class 안에 변수 초기화, 검색, 전처리 순으로 사용자 정의 함수를 만들어줍니다.

  • 변수 초기화 함수 : client id, client secret, 검색어, 검색 결과 개수, 검색 시작 위치를 모두 초기화합니다.
def __init__(self, client_id, client_secret, search, num, start):
    self.client_id = client_id
    self.client_secret = client_secret
    self.search = search
    self.num = num
    self.start = start
  • 검색 : 검색어, 검색 시작 위치, 검색 결과 개수를 사용합니다.
def blog_api(self):
    encText = urllib.parse.quote(self.search) #검색어 인코딩
    # 검색어, 시작 위치, 검색 결과 개수 포함한 url 생성
    url = "https://openapi.naver.com/v1/search/blog?query=" + encText + "&display=" + str(self.num) + "&start=" + str(self.start)
    request = urllib.request.Request(url) 
    request.add_header("X-Naver-Client-Id", self.client_id) #client id 투입
    request.add_header("X-Naver-Client-Secret", self.client_secret) #client secret 투입
    response = urllib.request.urlopen(request) #url 열기
    rescode = response.getcode() # 현재 상태 확인하기

    if(rescode==200): #현재 상태에 문제가 없다면
        response_body = response.read() # 내용 받아오기
        body = response_body.decode('utf-8') #utf-8 형식으로 디코딩하기
        return body # 디코딩한 결과 리턴
    else:
        print("Error Code:" + rescode) #현재 상태에 문제가 있다면 상태 출력하기
  • 전처리 : 디코딩한 결과에는 제목, 링크, 포스팅 날짜 외에도 다양한 정보가 포함되어 있습니다. 디코딩한 결과를 곧장 사용하기는 어렵기 때문에 전처리 과정을 한 번 더 거칩니다.
def preprocessing(self, body):
    body = body.replace('"','') # 큰 따옴표 제거
    list1 = body.split('\n\t\t{\n\t\t\t')
    list1 = [i for i in list1 if 'naver' in i] # 검색 결과의 주소가 네이버인 경우만 포함하기
	
    #제목, 링크, 날짜 넣을 리스트 생성
    titles = list(); links = list(); dates = list()
    for i in list1: 
        title = i.split("\n\t\t\t")[0] # 구분자 기준 첫 번째는 제목
        title = title.replace('title:',"") # title: 문자열 지우기
        link = i.split("\n\t\t\t")[1] # 구분자 기준 두 번째는 링크
        link = link.replace('link:',"") # link: 문자열 지우기
        postdate = i.split("\n\t\t\t")[-1] # 구분자 기준 마지막은 날짜
        postdate = re.sub(r'[^0-9]',"", postdate) #숫자만 남기기
        # 리스트에 추가
        titles.append(title)
        links.append(link)
        dates.append(postdate)

    for i in range(len(titles)): #제목 전처리 : 특수문자 등 지우기
        titles[i] = titles[i].replace("<\/b>", "")
        titles[i] = titles[i].replace("<b>", "")
        titles[i] = titles[i].replace(",", "")
        links[i] = links[i].replace("\\",'')
        links[i] = links[i].replace(",", "")

    # 제목, 링크, 날짜 데이터프레임 만들기
    df = pd.DataFrame({"title":titles, "link":links, "postdate":dates, "text":np.nan})
    return df

생성한 .py 파일은 실제 작업하실 파일과 같은 경로 내에 위치시켜주세요. 파일명은 naver_blog_api, 파일 내 클래스 명은 naver_blog 입니다.

.ipynb에서 검색

라이브러리 import 

import pandas as pd
from bs4 import BeautifulSoup
import numpy as np
from naver_blog_api import naver_blog # .py 파일 동일한 폴더 내에 저장할 것
from konlpy.tag import Okt
import requests

위 코드를 실행하시면 폴더 내에 __pycache__ 폴더가 만들어집니다. 

검색하기

client_id = "발급받은 api id"
client_secret = "발급받은 api 비밀번호"

# 검색 결과 중 1번부터 100번까지 가져오기
blog_temp = naver_blog(client_id, client_secret, "검색어", 100, 1)
temp = blog_temp.blog_api()
temp_df1 = blog_temp.preprocessing(temp)

# 링크에 blog 미포함 시 제외
temp_df1 = temp_df1[temp_df1['link'].str.contains("blog") == True].reset_index(drop=True)

에러 발생 시 오류 코드를 확인해보시는 걸 추천드립니다. https://developers.naver.com/docs/serviceapi/search/blog/blog.md#%EC%98%A4%EB%A5%98-%EC%BD%94%EB%93%9C

 

검색 > 블로그 - Search API

검색 > 블로그 블로그 검색 개요 개요 검색 API와 블로그 검색 개요 검색 API는 네이버 검색 결과를 뉴스, 백과사전, 블로그, 쇼핑, 영화, 웹 문서, 전문정보, 지식iN, 책, 카페글 등 분야별로 볼 수

developers.naver.com

본문 수집하기

수집된 링크에 포함된 본문을 크롤링하기 위해 아래와 같은 사용자 정의 함수를 만들어줍니다. 네이버 블로그의 본문을 수집하기 위해서는 src 요소를 크롤링해야 합니다.

def text_crawl(dataframe):
    for i,j in enumerate(dataframe['link']):
        if pd.isnull(dataframe.loc[i, 'text']): #아직 크롤링되지 않은 부분부터
            resp = requests.get(j, timeout=3)
            soup = BeautifulSoup(resp.content, "html.parser")
            try:
                # 수집할 수 있는 url 파싱
                url = "https://blog.naver.com/"+soup.iframe['src']
                resp = requests.get(url, timeout=3)
                soup = BeautifulSoup(resp.content, "html.parser")

                if soup.find("div", attrs={'class':'se-main-container'}): #스마트 에디터 ONE
                    text = soup.find("div", attrs={'class':'se-main-container'}).get_text()
                    text = text.replace("\n", "")
                elif soup.select_one('div#postViewArea'): #스마트 에디터 2.0
                    text = soup.select_one('div#postViewArea').text
                    text = text.replace("\n", "")
                else:
                    text = 'None'
                dataframe.loc[i, 'text'] = text
            except TypeError:
                print(i, "번째 크롤링 불가")
                text = 'None'
                dataframe.loc[i, 'text'] = text
            if i % 100 == 0:
                print(i, "번째 본문 크롤링 완료")
        time.sleep(0.5)
    return dataframe

수집하면서 보니 스마트 에디터 버전에 따른 이슈가 있었습니다.

  • 스마트 에디터 버전별로 html이 다름 : 이에 따라 스마트 에디터 one html 기준으로 크롤링을 시도하고, 안 될 경우 스마트 에디터 2.0 기준으로 크롤링을 시도하도록 코드를 구현했습니다.
  • 스마트 에디터 3.0 크롤링 불가 : 다른 포스팅을 보니 셀레니움을 사용해도 스마트 에디터 3.0으로 작성한 경우는 크롤링이 어려운 것 같아요. 크롤링이 안 된 경우는 text 컬럼에 None이라고 입력되어 있기 때문에, 추후 필터링해주셔야합니다.

검색 결과로 받은 데이터프레임을 사용자 정의 함수의 인자로 넣어주시면 끝입니다! 이후 수집한 데이터를 분석 목적에 맞게 가공해서 사용하시면 됩니다. 

 

Github에서 보기

 

GitHub - Sennysideup/Project

Contribute to Sennysideup/Project development by creating an account on GitHub.

github.com


오늘 포스팅은 여기까지입니다. 좋은 주말 보내세요!