AI/정책 댓글 반응 NLP

[1] 네이버 뉴스 댓글 크롤러_ver1_2. 각 기사 링크 페이지에 접속해 정보 가져오기

eraser 2020. 4. 1. 02:44
반응형

 받아 온 뉴스 링크에서 댓글 링크를 통해 댓글 페이지에 접속하고자 한다. 접속 후, 댓글 관련 정보를 제외한 기사 제목, 기사 작성 시간, 언론사 정보까지 가져온다. 이로써 접속하여 데이터를 정상적으로 받아올 수 있는지 확인한다. 작업을 통해 얻고자 하는 결과물이전 단계에서 추출한 모든 네이버 뉴스 링크들의 제목, 작성 시간, 언론사이다.

 

 


# 댓글 페이지 접속하기

 

 사전 작업에서 확인했듯, 댓글 페이지의 URL에는 'm_view=1'이 붙는다. 따라서 이전 단계에서 찾은 모든 네이버 기사 URL의 뒤에 '&m_view'를 붙여 준다. 출력 상태 코드를 확인하여 제대로 실행되는지 확인한다.

 

naver_news_links = get_news_links(last_page, LINK_PAT)

for link in naver_news_links:
    print(f"{link}&m_view=1")
    req = requests.get(f"{link}&m_view=1")
    print(req.status_code)

 

 출력 결과에서 URL을 클릭한 결과, 제대로 댓글 페이지에 연결됨을 확인할 수 있다. 또한, 상태 코드가 모두 200으로 출력되는 것을 보아 정상적으로 HTTP 요청을 보내 연결되었음을 알 수 있다.

 


 

 

 각 링크를 돌면서 스크레이핑을 하도록 지시하는 함수를 만든다. 아직 설계하지 않았지만, 정보를 얻는 새로운 함수를 함수 내부에서 호출하고, 그 함수가 스크레이핑한 정보를 contents 리스트에 저장한다. 아직은 정보를 얻은 게 없기 때문에, 함수 실행 시 빈 contents 리스트가 출력된다.

 

naver_news_links = get_news_links(last_page, LINK_PAT)

def extract_contents(links):
    contents = []
    for link in links:
    	req = requests.get(f"{link}&m_view=1")
        print(req.status_code)
    return contents

 

 함수 실행 시, 상태 코드가 올바르게 200으로 10개씩 출력되는 것을 보아 제대로 작동하고 있음을 알 수 있다.

 


 

# 각 페이지에서 정보 스크레이핑

 

 해당 URL에 정보를 보내고 스크레이핑한다. 개발자 도구를 통해 각 개체의 태그가 무엇인지 확인하는 과정은 생략한다. 검사 결과, 기사 제목은 id가 'articleTitle'인 'h3'태그의 text로, 기사 작성 시간은 class가 't11'인 'span' 태그의 text로, 언론사는 class가 'press logo'인 'div' 태그 하의 'a' 태그의 'img' 태그에서 'title' 속성으로 존재함을 알 수 있다.

 

 일단 하나의 테스트용 URL을 통해 각각의 정보를 가져올 수 있는지 확인한다.

 

test_url = "https://news.naver.com/main/read.nhn?mode=LSD&mid=sec&sid1=101&oid=023&aid=0003518886&m_view=1"

req = requests.get(test_url)
soup = BeautifulSoup(req.text, 'lxml')

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'] # 언론사

print(f"제목 : {title}")
print(f"기사 작성 시간 : {article_time}")
print(f"언론사 : {press}")

 

 파싱된 HTML에서 각각의 정보를 추출했다. 'get_text()' 메서드를 사용할 때 공백이 있을 수도 있기 때문에 'strip=True' 옵션을 줬다. 실행하면 각각의 정보가 올바르게 출력되는 것을 확인할 수 있다.

 


def extract_info(url):
    req = requests.get(url)
    soup = BeautifulSoup(req.text, 'lxml')
    
    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'] # 언론사 
    
    return {'title':title, 'article_time':article_time, 'press':press}

 

 위의 함수는 각 페이지 하나, 하나에 대해 작동한다. URL을 인자로 넘겨 받으면, 그 URL에 접속하여 HTML을 파싱하여 해당하는 정보를 추출하는 것이다. 해당하는 정보는 나중에 JSON 형태로 만들거나, CSV 파일로 만들 때를 대비하여 dict 형태로 저장해 반환한다.

 

 


 

# 각 페이지 돌며 스크레이핑하는 크롤러 함수 만들기

 

 위에서 정의한 extract_contents 함수를 수정한다. 네이버 뉴스 리스트 전체를 반환받고, 리스트 안에 존재하는 하나 하나의 링크를 상대로 함수 안에서 extract_info 함수를 호출한다. 호출한 함수가 반환한 dict 형태의 데이터를 리스트 안에 저장한다. 최종적으로 해당 리스트를 반환한다.

 

def extract_contents(links):
    contents = []
    for link in links:
        content = extract_info(f"{link}&m_view=1") 
        # 각 링크에 대해 댓글 페이지를 인자로 넘겨 extract_info 함수 호출
        contents.append(content)
    return contents

 


 

 이 단계에서의 작업을 모두 정리하면 다음과 같다.

 

더보기

 'main()' 함수를 실행한 결과, 현재 단계까지는 10페이지까지 이동하며 네이버 뉴스 플랫폼에 등록된 기사 링크에서 원하는 정보를 가져오는 것을 확인했다. 

 

# module import
from urllib.parse import urlparse
import requests
from bs4 import BeautifulSoup
import re

# 변수 설정
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\?"

# 마지막 페이지 호출
def get_last_page():
    req = requests.get(URL)
    soup = BeautifulSoup(req.text, 'lxml')
    pagination = soup.find('div', {'class' : 'paging'})
    pages = pagination.find_all("a")
    page_list = []
    for page in pages[-1]:
        page_list.append(int(page.get_text(strip = True)))
    max_page = page_list[-1]
    return max_page
    
# 뉴스 링크 찾기
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):
    req = requests.get(url)
    soup = BeautifulSoup(req.text, 'lxml')
    
    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'] # 언론사 
    
    return {'title':title, 'article_time':article_time, 'press':press}

# 각 페이지별로 정보 호출 지시
def extract_contents(links):
    contents = []
    for link in links:
        content = extract_info(f"{link}&m_view=1")
        contents.append(content)
    return contents

# 모든 작업 진행
def main():
    last_page = get_last_page()
    print(last_page) # 확인용
    news_links = get_news_links(last_page, LINK_PAT)
    result = extract_contents(news_links)
    return result

# 함수 실행
main()

 

 

 

반응형