AI/정책 댓글 반응 NLP

[1] 네이버 뉴스 댓글 크롤러_ver1.5_검색페이지 확대

eraser 2020. 4. 1. 15:32
반응형

  ver1의 크롤러를 조금 수정하여 최대 검색 페이지 숫자를 지정하여 크롤링하도록 함수를 수정했다. 작업을 통해 얻고자 하는 결과물은 '주52시간'으로 검색했을 때 나오는 모둔 기사들에서 스크레이핑한 필요한 정보들이다. 

 


 

# 검색 페이지 지정

 

 ver1의 함수에서 크게 달라진 것은 없다. 다만, 어차피 네이버는 최대로 제공하는 검색 결과 페이지가 400페이지 까지이기 때문에, 마지막 페이지를 얻는 함수를 없애고, 검색 페이지를 변수로 지정하여 해당하는 페이지까지 루프를 돌면서 네이버 뉴스 링크를 모아 오는 방식으로 함수를 수정했다.

 실행할 main 함수에서 검색 페이지는 전역 변수로 설정했다.

 

# 변수 설정
QUERY = "주52시간"
search_QUERY = urllib.parse.urlencode({'query':QUERY}, encoding='utf-8')
URL = f"https://search.naver.com/search.naver?&where=news&{search_QUERY}\
        &sm=tab_pge&sort=0&photo=0&field=0&reporter_article=&pd=0&ds=&de=&docid=&nso=so:r,p:all,a:all&mynews=0"
LINK_PAT = "https:\/\/news\.naver\.com\/main\/read\.nhn\?"
search_PAGE = 400 # 검색할 페이지

(...)

# main 함수
def main():
    global search_PAGE
    news_links = get_news_links(search_PAGE, LINK_PAT) 
    result = extract_contents(news_links)
    driver.quit()
    return result

# 출력 결과 저장 함수
def save_to_file(lst):
    global search_PAGE
    file = open(f"news_comments_NAVER_{search_PAGE}page.csv", mode="w", encoding="UTF-8")
    writer = csv.writer(file)
    writer.writerow(['site', 'title', 'article_time', 'press', 'total_comments', 'contents'])
    for result in lst:
        writer.writerow(list(result.values()))
    file.close()
    return

# 함수 실행
NAVER_RESULT = main()
save_to_file(NAVER_RESULT)

 

 그런데, 오류가 난다. 이전까지는 잘 찾아 오던 태그 속성이 갑자기 없다는 것이다! 검색 범위가 늘어나면서 HTML 형식이 다른 기사가 있나 궁금해졌다. 

 

 extract_contents 함수에서 각 링크를 돌며 extract_info 함수를 호출하기 전 해당 링크를 콘솔에 출력하고, extract_info 함수를 수정하여 작업이 완료되면 "완료"를 표시하도록 했다.

 

너는 어떤 오류인 것이냐!

 

 

첫 기사부터 오류가 나는 것을 확인할 수 있다.

 

 

 급하게 '주52시간'을 검색했다. 이전에는 볼 수 없던 새로운 기사가 떠 있다. 하필, 연예 분야의 기사다. 올라온 지 얼마 되지 않은 기사다.

  • 웹은 정말 빨리 변하는 구나.
  • 검색어 및 검색 조건을 naive하게 잡아 두니, 필요하지 않은 기사도 나오는구나.

 네이버 뉴스의 경우 정책 상 연예 분야의 기사에는 댓글 기능이 제공되지 않는다. 댓글이 없으니 NoneType이 반환되어 오류가 난 것이다.

 

 일단은 데이터를 쌓는 게 목적이었으므로, 해당 기사와 같은 경우가 발견된다면 None 객체를 반환하고, 나중에 결측치를 처리하자고 생각했다. 그리고 다음과 같이 최종적으로 코드를 수정했다.

 


 오류가 날 수도 있기 때문에 페이지 링크와 처리 메시지를 넣는 부분을 추가했다. 또한, try, except 구문을 활용해 오류가 난다면 None 객체를 반환하도록 했다.

 

 

더보기
# module import
import requests
import urllib.request
from urllib.request import urlopen
from urllib.parse import urlparse
from urllib.request import URLError, HTTPError
from bs4 import BeautifulSoup
import re
from selenium import webdriver
import time
import numpy as np
import csv

# 변수 설정
QUERY = "주52시간"
search_QUERY = urllib.parse.urlencode({'query':QUERY}, encoding='utf-8')
URL = f"https://search.naver.com/search.naver?&where=news&{search_QUERY}\
        &sm=tab_pge&sort=0&photo=0&field=0&reporter_article=&pd=0&ds=&de=&docid=&nso=so:r,p:all,a:all&mynews=0"
LINK_PAT = "https:\/\/news\.naver\.com\/main\/read\.nhn\?"
search_PAGE = 400 # 검색할 페이지

# driver 설정
driver = webdriver.Chrome("D:\python_DA\chromedriver.exe")

# 검색결과 내 링크 찾기 : news.naver.com으로 시작하는 모든 링크 반환
def get_news_links(page_num, link_pattern):
    links = []
    for page in range(page_num):
        print(f"Scrapping page : {page+1}") # 확인용
        req = requests.get(f"{URL}&start={10*page+1}"); print(req.status_code)
        soup = BeautifulSoup(req.text, 'lxml')
        results = soup.find_all('a', {'href' : re.compile(link_pattern)})
        for result in results:
            links.append(result['href'])
    print(f"총 {len(links)}개의 뉴스 링크를 찾았습니다.") # 확인용
    return links
   

# 한 페이지 별로 필요한 정보 스크레이핑
def extract_info(url, wait_time=1, delay_time=0.3):
    
    driver.implicitly_wait(wait_time)
    driver.get(url)
    
    # 댓글 창 있으면 다 내리기
    while True:
        try:
            more_comments = driver.find_element_by_css_selector('a.u_cbox_btn_more')
            more_comments.click()
            time.sleep(delay_time)
        except:
            break
    
    # html 페이지 읽어오기
    html = driver.page_source
    soup = BeautifulSoup(html, 'lxml')
    
    try:               
        site = soup.find('h1').find("span").get_text(strip=True) # 출처
        title = soup.find('h3', {'id':'articleTitle'}).get_text(strip=True) # 기사 제목    
        article_time = soup.find('span', {'class':'t11'}).get_text(strip=True) # 작성 시간    
        press = soup.find('div', {'class':"press_logo"}).find('a').find('img')['title'] # 언론사   


        reply = [] # 댓글 내용 취합

        total_com = soup.find("span", {"class" : "u_cbox_info_txt"}).get_text() # 댓글 수    

        nicks = soup.find_all("span", {"class":"u_cbox_nick"}) # 댓글 작성자
        nicks = [nick.text for nick in nicks]    

        dates = soup.find_all("span", {"class":"u_cbox_date"}) # 댓글 날짜
        dates = [date.text for date in dates]    

        contents = soup.find_all("span", {"class":"u_cbox_contents"}) # 댓글 내용
        contents = [content.text for content in contents]    

        recomms = soup.find_all("em", {"class":"u_cbox_cnt_recomm"}) # 공감 수
        recomms = [recomm.text for recomm in recomms]

        unrecomms = soup.find_all("em", {"class":"u_cbox_cnt_unrecomm"}) # 비공감수
        unrecomms = [unrecomm.text for unrecomm in unrecomms]    

        for i in range(len(contents)):
            reply.append({'nickname':nicks[i],
                          'date':dates[i],
                          'contents':[i],
                          'recomm':recomms[i],
                          'unrecomm':unrecomms[i]})
        print("완료")        
        return {'site':site, 'title':title, 'article_time':article_time, 'press':press, 'total_comments':total_com, 'reply_content':reply}
    
    except:
        print("오류")
        return {'site':None, 'title':None, 'article_time':None, 'press':None, 'total_comments':None, 'reply_content':None}
        

# 각 페이지 돌면서 스크레이핑
def extract_contents(links):
    contents = []
    for link in links:
        print(f"{link}&m_view=1") # 확인용 링크 출력
        content = extract_info(f"{link}&m_view=1")
        contents.append(content)   
    return contents


# main 함수
def main():
    global search_PAGE
    news_links = get_news_links(search_PAGE, LINK_PAT) 
    result = extract_contents(news_links)
    driver.quit()
    return result

# 출력 결과 저장 함수
def save_to_file(lst):
    global search_PAGE
    file = open(f"news_comments_NAVER_{search_PAGE}page.csv", mode="w", encoding="UTF-8")
    writer = csv.writer(file)
    writer.writerow(['site', 'title', 'article_time', 'press', 'total_comments', 'contents'])
    for result in lst:
        writer.writerow(list(result.values()))
    file.close()
    return

# 함수 실행
NAVER_RESULT = main()
save_to_file(NAVER_RESULT)

 


 

 그 결과, 400페이지까지 돌며 해당하는 정보가 있는 경우 스크레이핑한 파일을 얻었다. 

 

 

news_comments_NAVER_400page.zip
1.79MB

 

 

 

 

 

반응형