PROJECT/텍스트 마이닝을 활용하여 문자 대화내용 분석

네이버 기사 크롤러 만들기

HeyTeddy 2023. 3. 22. 18:49
반응형

네이버 기사를 크롤링해보겠습니다.

 

전에 포스팅했던 '네이버 오픈 API 등록' 글에서 'Client ID'와 'Client Secret'을 가져옵니다.

https://kjws0712.tistory.com/100

 

네이버 오픈 API 등록

1. 구글 검색에 '네이버 개발자 API' 검색 2. 네이버 오픈 API 목록 클릭 3. 네이버 기사를 크롤링 하기 위해서는 검색 기사를 가져올 수 있어야 함으로 '검색' API 이용할 예정 4. 서비스 API 클릭 5. 검

kjws0712.tistory.com

 

import os
import sys
import urllib.request
client_id = "본인의 발급받은 Client ID"  # 발급받은 client ID 작성
client_secret = "본인의 발급받은 Client Secret" # 발급받은 client secret 작성
encText = urllib.parse.quote("파이썬") # 검색할 단어
url = "https://openapi.naver.com/v1/search/news?query=" + encText # JSON 결과
# url = "https://openapi.naver.com/v1/search/blog.xml?query=" + encText # XML 결과

request = urllib.request.Request(url)
request.add_header("X-Naver-Client-Id",client_id)
request.add_header("X-Naver-Client-Secret",client_secret)

response = urllib.request.urlopen(request) # 접속해준다
rescode = response.getcode() # 코드를 받아온다

if(rescode==200): # 200이면 정상, 
    response_body = response.read()
    print(response_body.decode('utf-8'))
else:
    print("Error Code:" + rescode) # 에러코드 출력

10, 11, 12번째 줄 request는 말 그대로 서버에 요청한다는 뜻입니다.

서버한테 이런 내용을 줘라.(어디에 접속할 지 URL, 정보를 줘야 함, 나는 API를 받았어, id와 secret)

 

요청한 URL을 승인 받으면 response변수는 요청한 URL을 접속해줍니다.

그리고 rescode변수를 통해 코드를 받아옵니다.

 

rescode가 200인 경우가 정상이라는 뜻입니다.

https://developers.naver.com/docs/serviceapi/search/news/news.md#%EB%89%B4%EC%8A%A4

 

검색 > 뉴스 - Search API

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

developers.naver.com

 

위 링크를 참고하셔서 '응답 예', '오류 코드'를 보시면 좋으실 것 같습니다.

 

출력된 결과는 네이버 → 뉴스 → '파이썬' 검색시 나오는 최신순 기사입니다.

위 코드의 단점은 본문 API를 제공하지 않아서 본문 내용을 가져올 수가 없습니다.

그래서 본문 내용을 가져오기 위해선 따로 접속해서 가져와야 합니다.

 

import os
import sys
import urllib.request
import requests

news_data = [] # 딕셔너리 형태

client_id = "발급받은 Client ID"
client_secret = "발급받은 Client Secret" # 발급받은 client secret
encText = urllib.parse.quote("파이썬") # 검색할 단어
url = "https://openapi.naver.com/v1/search/news?query=" + encText # JSON 결과
# url = "https://openapi.naver.com/v1/search/blog.xml?query=" + encText # XML 결과

request = urllib.request.Request(url)
request.add_header("X-Naver-Client-Id",client_id)
request.add_header("X-Naver-Client-Secret",client_secret)

response = urllib.request.urlopen(request) # 접속해준다
rescode = response.getcode() # 코드를 받아온다

if (rescode==200):
    result = requests.get(response.geturl(),
                          headers = {"X-Naver-Client-Id":client_id,
                                    "X-Naver-Client-Secret":client_secret}
                         )
    news_data.append(result.json()) # 딕셔너리 형태로 저장
else:
    print("Error Code:" + rescode)
# 네이버 OPEN API를 통해 가져온 데이터 확인하기
news_data

 

첫 번째 코드는 단지 print만 수행했지만, 불러온 내용을 사용해야 함으로 저장코드가 필요하기 때문에 두 번째 코드를 작성하였습니다.

request.get은 요청하면 받아온다는 뜻이고, json은 파이썬 입장에서는 key, value를 뜻합니다.

 

news_data[0]['items'][0]['link']

# print(news_data[0]['items'][0]['link'])
# print 함수를 사용하면 바로 url을 통해 접속 가능
'http://www.veritas-a.com/news/articleView.html?idxno=450711'

 

모든 기사의 태그가 달라서 모든 기사를 가져올 수는 없습니다. 일일이 직접 가져와야하는데 시간적 번거로움이 발생합니다. 네이버 뉴스 기사가 통계적으로 많아서 네이버 뉴스만 가져오는게 효과적입니다.

 

가져온 URL이 네이버 뉴스인지 확인해보겠습니다.

page_news_link = []

for item in news_data[0]['items']:
    link = item['link']
    if "naver" in link:
        page_news_link.append(link)

len(page_news_link) # 페이지 링크 안에 네이버 뉴스는 2개, 현재시간 기준이므로 다시 할 시 바뀜
2

현 시간에 네이버 뉴스 검색창에 "파이썬"을 검색하면 네이버 뉴스는 2개가 존재합니다.

 


여러 페이지 기사 가져오기

한 페이지에 10개의 기사만 존재하고, 그 중에서 네이버 뉴스는 2개만 존재하였습니다.

이번에는 여러 페이지를 가져와 보겠습니다.

뉴스 1페이지에 URL 링크를 보면 Start=1이라는 점을 참고하면서 진행하겠습니다.

한 페이지에 기사가 10개 존재함으로, 뉴스 2페이지에서는 Start=11입니다.

 

import os
import sys
import urllib.request
import requests

news_data = [] # 딕셔너리 형태
page_count = 3 # 3페이지만 가져오기 위해

client_id = "발급받은 Client ID"
client_secret = "발급받은 Client Secret" # 발급받은 client secret
encText = urllib.parse.quote("파이썬") # 검색할 단어

for idx in range(page_count):
    url = "https://openapi.naver.com/v1/search/news?query=" + encText + "&start=" + str(idx*10+1) # JSON 결과
    # url = "https://openapi.naver.com/v1/search/blog.xml?query=" + encText # XML 결과

    request = urllib.request.Request(url)
    request.add_header("X-Naver-Client-Id",client_id)
    request.add_header("X-Naver-Client-Secret",client_secret)
    response = urllib.request.urlopen(request) # 접속해준다
    rescode = response.getcode() # 코드를 받아온다

    if (rescode==200):
        result = requests.get(response.geturl(),
                              headers = {"X-Naver-Client-Id":client_id,
                                        "X-Naver-Client-Secret":client_secret}
                             )
        news_data.append(result.json()) # 딕셔너리 형태로 저장
    else:
        print("Error Code:" + rescode)

 

2번째 코드와 다른 점은 news_data 딕셔너리를 생성하였고, 3페이지만 가져오기 위해 page_count를 3으로 지정했습니다.

for문을 활용하여 1, 2, 3페이지별 URL을 지정하였습니다.

1페이지의 Start=1이라는 점을 이용하여 코드를 알맞게 설정하였습니다.

 

news_data[0]['items'][0]['link']
'https://n.news.naver.com/mnews/article/092/0002286288?sid=105'

 

크롤링한 1페이지의 첫번째 기사의 URL 주소입니다.

 

news_data

살펴보면 3개의 딕셔너리가 존재하고 각 딕셔너리의 'start' key값을 보면 1, 11, 21을 확인할 수 있습니다.

 

페이지 별 네이버 기사 수 가져오기

지정한 페이지 수 별로 네이버 기사만 가져오는 코드를 작성해보겠습니다.

naver_news_link = [] # 페이지 별 네이버 기사 수
 
for page in news_data:
    
    page_news_link = []
    
    for item in page['items']:
        link = item['link']
        if "naver" in link:
            page_news_link.append(link)

    naver_news_link.append(page_news_link)

for page in naver_news_link:
    for link in page:
        print(link) # 링크 다 나옴, 총 8개 기사 있음
        
print(naver_news_link) # 각 페이지별 기사 URL
print(len(naver_news_link)) # 3페이지, 3

 

이 코드는 딱히 설명드릴점이 없으니 눈으로 보고 한번 작성해보심이 좋을 듯 싶습니다.

 

크롤링하는데 필요한 모듈과 패키지

import pandas as pd
import numpy as np
from selenium import webdriver
from tqdm import tqdm_notebook
import requests
import pickle
import re
import ast

from bs4 import BeautifulSoup 
from urllib.request import urlopen
import urllib
import time


# 가상 크롬드라이버를 불러옴
# 윈도우 10의 경우 chromedriver.exe
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

driver = webdriver.Chrome('chromedriver')

 

크롤링하는데 필요한 모듈과 패키지를 적어보았습니다.

selenium과 BeautifulSoup가 중요합니다.

 

가상 크롬드라이버를 만드는 이유는 컴퓨터에 있는 크롬이 가상환경에서는 적용되지 않더라고요...(맞는지는 잘 모르겠지만 그럴거 같네요..뇌피셜입니다)

 

이제 한 코드블록으로 원하는 기사 링크의 제목, 본문 내용을 크롤링 해보겠습니다.

# https://n.news.naver.com/mnews/article/092/0002286288?sid=105
# 위 링크 기사를 크롤링 함

naver_news_title = []
naver_news_content = []


for n in tqdm_notebook(range(len(naver_news_link))):
    #print(n)
    news_page_title = []
    news_page_content = []
    
    for idx in tqdm_notebook(range(len(naver_news_link[n]))):
        
        
    ########### 긁어온 URL로 접속하기 ############    
        try:
            driver.get(naver_news_link[n][idx])
            print(naver_news_link[n][idx])
            
        except:
            print("Timeout!")
            continue
        
        
        try:
            response = driver.page_source
            
        except UnexpectedAlertPresentException: # 게시글이 삭제된 경우
            driver.switch_to_alert().accept()
            print("게시글이 삭제된 경우입니다.")
            continue
        
        soup = BeautifulSoup(response, "html.parser")
        
        ###### 뉴스 타이틀 긁어오기 ######
        
        title = None
        
        try:
            item = soup.find('div', class_="media_end_head_title")
            title = item.find('h2', class_="media_end_head_headline").get_text()
            #print(title)

        except:
            title = "OUTLINK"
        
        #print(title)
        news_page_title.append(title)
        
        
        ###### 뉴스 본문 긁어오기 ######
        
        doc = None
        text = ""
                
        data = soup.find_all("div", {"class" : "go_trans _article_content"}) # 그림, 텍스트 다 가져옴
        if data:
            for item in data:

                text = text + str(item.find_all(text=True)).strip() # 텍스트만 가져옴
                text = ast.literal_eval(text) # 어려운 코드, skip, 안정적임
                doc = ' '.join(text)
   
        else:
            doc = "OUTLINK"
            
        news_page_content.append(doc.replace('\n', ' '))

                
    naver_news_title.append(news_page_title)
    naver_news_content.append(news_page_content)

    time.sleep(2) # DDOS로 인식해서 막아버림
    
    
print(naver_news_title[0])
print("==================================")
print(naver_news_content[0])

 

※ 뉴스 타이틀과 뉴스 본문의 'div', 'class', 'h2', 'h3'등이 다를 수 있으므로 해당 기사 페이지에 들어가서 제목과 내용에서 오른쪽 누르고 '검사'를 클릭 후 확인하시고 알맞게 사용하셔야 합니다.

 

naver_news_title과 naver_news_content 리스트를 생성하고, try문을 활용합니다.

  1. 우선 긁어온 URL로 접속하고
  2. 뉴스 타이틀을 긁어옵니다.
  3. 뉴스 본문을 긁어옵니다.
  4. time.sleep을 적용합니다.
print(naver_news_title)
[['AI 챗GPT가 개발자들의 미래에 미치는 영향은?', '허깅페이스, 자바스크립트용 기계학습 라이브러리 공개', '한국경영인증원(KMR), 도봉구와 빅데이터 분석 입문 무료교육 진행'], ['지역 청년 위한 인턴십 프로그램 확대', '메타버스로 일 배운다…리모트 인턴십 확대 운영', 'CJ올리브네트웍스, 지역청년 인턴십 확대'], ['CJ올리브네트웍스, 지역거점 청년 위한 리모트인턴십 확대 운영', 'CJ올리브네트웍스, 지역거점 청년 위한 ‘리모트 인턴십’ 확대 \xa0']]

 

naver_news_title을 보면 총 3페이지에 있는 네이버 뉴스 기사 제목 8개가 나타납니다.

 

print(naver_news_content)

 

naver_news_title을 보면 총 3페이지에 있는 네이버 뉴스 기사 원본 8개가 나타납니다.

 

크롤링 결과 저장 (pickle)

이제 크롤링한 결과를 저장해보겠습니다. pickle을 이용해보겠습니다.

with open("naver_news_title.pk", "wb") as f:
    pickle.dump(naver_news_title, f)
    
with open("naver_news_content.pk", "wb") as f:
    pickle.dump(naver_news_content, f)

 

디렉토리를 확인해보면, naver_news_title.pk와 naver_news_content.pk가 저장되었습니다.

반응형