+ 개발

뉴스 크롤링(#네이버 뉴스)_코드 설명

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


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

https://search.naver.com/search.naver?where=news&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&sort=1&photo=0&field=0&pd=3&ds=2024.01.01&de=2024.07.31&docid=&related=0&mynews=0&office_type=0&office_section_code=0&news_office_checked=&nso=so%3Add%2Cp%3Afrom20240101to20240731&is_sug_officeid=0&office_category=0&service_area=0

 

청년취업사관학교 : 네이버 뉴스검색

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

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("\t", "").replace("\r", "")

def get_news_item(url):
    try:
        res = requests.get(url)
        res.raise_for_status()
    except requests.exceptions.RequestException as e:
        print(f"Failed to retrieve news item: {url}, error: {e}")
        return None

    soup = BeautifulSoup(res.text, "html.parser")
    
    try:
        title = soup.select_one("h2#title_area").text
        media = soup.select_one(".media_end_head_top_logo img")["title"]
        date = soup.select_one(".media_end_head_info_datestamp_time")["data-date-time"]
        content = clean_text(soup.select_one("#newsct_article").text)
        return title, media, date, content, url
    except AttributeError as e:
        print(f"Failed to parse news item: {url}, error: {e}")
        return None

def get_naver_news(keyword, startdate, enddate, to_csv=False):
    ret = []
    for d in pd.date_range(startdate, enddate, freq='D'):
        page = 1
        print(f"Processing date: {d}")
        
        while True:
            start = (page - 1) * 10 + 1
            url = f"https://s.search.naver.com/p/newssearch/search.naver?de={d.strftime('%Y.%m.%d')}&ds={d.strftime('%Y.%m.%d')}&eid=&field=0&force_original=&is_dts=0&is_sug_officeid=0&mynews=0&news_office_checked=&nlu_query=&nqx_theme=&nso=%26nso%3Dso%3Add%2Cp%3Afrom{d.strftime('%Y%m%d')}to{d.strftime('%Y%m%d')}%2Ca%3Aall&nx_and_query=&nx_search_hlquery=&nx_search_query=&nx_sub_query=&office_category=0&office_section_code=0&office_type=0&pd=3&photo=0&query={keyword}&query_original=&service_area=0&sort=1&spq=0&start={start}&where=news_tab_api&nso=so:dd,p:from{d.strftime('%Y%m%d')}to{d.strftime('%Y%m%d')},a:all"
            
            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

            li = res_dic.get('contents', [])
            
            if len(li) == 0:
                break

            for item in li:
                soup = BeautifulSoup(item, 'html.parser')
                a_tags = soup.select("div.info_group a")
                if len(a_tags) == 2:
                    news_data = get_news_item(a_tags[1]['href'])
                    if news_data:
                        ret.append(news_data)
                        
            page += 1
                    
    df = pd.DataFrame(ret, columns=["title", "media", "date", "content", "url"])
    
    if to_csv:
        df.to_csv(f"{keyword}_{startdate.replace('.','')}_{enddate.replace('.','')}.csv", index=False)
    
    return df

get_naver_news("청년취업사관학교", "2024.01.01", "2024.07.31", to_csv=True)

 

코드 설명

1. 필요한 라이브러리 임포트

import requests
from bs4 import BeautifulSoup
import pandas as pd
import json
  • requests: HTTP 요청을 보내 웹 페이지의 내용을 가져오는 데 사용됩니다.
  • BeautifulSoup: HTML 및 XML 파일을 파싱하고 탐색하는 데 사용됩니다. 주로 웹 스크래핑에 사용되며, 이 코드에서는 네이버 뉴스 페이지의 HTML을 파싱하는 데 사용됩니다.
  • pandas: 데이터 분석을 위한 라이브러리로, 데이터프레임 형태로 데이터를 저장하고 조작하는 데 사용됩니다.
  • json: JSON 데이터를 파싱하는 데 사용됩니다. 이 코드는 json.loads를 사용해 서버로부터 받은 JSON 데이터를 딕셔너리로 변환합니다.

 

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

def clean_text(text):
    return text.replace("\n", "").replace("\t", "").replace("\r", "")
  • 문자열에서 불필요한 공백 문자(\n, \t, \r)를 제거하는 함수입니다. 뉴스 본문 내용을 깔끔하게 정리하는 데 사용됩니다.

 

3. 뉴스 정보 추출 함수 (get_news_item)

def get_news_item(url):
    try:
        res = requests.get(url)
        res.raise_for_status()
    except requests.exceptions.RequestException as e:
        print(f"Failed to retrieve news item: {url}, error: {e}")
        return None

    soup = BeautifulSoup(res.text, "html.parser")
    
    try:
        title = soup.select_one("h2#title_area").text
        media = soup.select_one(".media_end_head_top_logo img")["title"]
        date = soup.select_one(".media_end_head_info_datestamp_time")["data-date-time"]
        content = clean_text(soup.select_one("#newsct_article").text)
        return title, media, date, content, url
    except AttributeError as e:
        print(f"Failed to parse news item: {url}, error: {e}")
        return None
  • 주어진 뉴스 URL에서 뉴스의 제목, 매체 이름, 작성일, 본문 내용을 추출하여 튜플로 반환합니다.
  • 작동 방식:
    • HTTP 요청: requests.get으로 URL에 대해 GET 요청을 보내고, raise_for_status()로 요청이 성공했는지 확인합니다.
    • HTML 파싱: BeautifulSoup을 사용해 페이지의 HTML을 파싱합니다.
    • 데이터 추출:
      • 제목: h2#title_area 요소에서 뉴스 제목을 추출합니다.
      • 매체 이름: .media_end_head_top_logo img 요소의 title 속성에서 매체 이름을 추출합니다.
      • 작성일: .media_end_head_info_datestamp_time 요소의 data-date-time 속성에서 작성일을 추출합니다.
      • 본문 내용: #newsct_article 요소에서 본문 내용을 추출하고, 불필요한 공백을 제거합니다.
    • 예외 처리: 뉴스 아이템을 가져오거나 파싱하는 과정에서 오류가 발생하면, 오류 메시지를 출력하고 None을 반환합니다.

 

4. 네이버 뉴스 검색 결과 가져오기 (get_naver_news)

def get_naver_news(keyword, startdate, enddate, to_csv=False):
    ret = []
    for d in pd.date_range(startdate, enddate, freq='D'):
        page = 1
        print(f"Processing date: {d}")
        
        while True:
            start = (page - 1) * 10 + 1
            url = f"https://s.search.naver.com/p/newssearch/search.naver?de={d.strftime('%Y.%m.%d')}&ds={d.strftime('%Y.%m.%d')}&eid=&field=0&force_original=&is_dts=0&is_sug_officeid=0&mynews=0&news_office_checked=&nlu_query=&nqx_theme=&nso=%26nso%3Dso%3Dd d%2Cp%3Afrom{d.strftime('%Y%m%d')}to{d.strftime('%Y%m%d')}%2Ca%3Aall&nx_and_query=&nx_search_hlquery=&nx_search_query=&nx_sub_query=&office_category=0&office_section_code=0&office_type=0&pd=3&photo=0&query={keyword}&query_original=&service_area=0&sort=1&spq=0&start={start}&where=news_tab_api&nso=so:dd,p:from{d.strftime('%Y%m%d')}to{d.strftime('%Y%m%d')},a:all"
            
            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

            li = res_dic.get('contents', [])
            
            if len(li) == 0:
                break

            for item in li:
                soup = BeautifulSoup(item, 'html.parser')
                a_tags = soup.select("div.info_group a")
                if len(a_tags) == 2:
                    news_data = get_news_item(a_tags[1]['href'])
                    if news_data:
                        ret.append(news_data)
                        
            page += 1
                    
    df = pd.DataFrame(ret, columns=["title", "media", "date", "content", "url"])
    
    if to_csv:
        df.to_csv(f"{keyword}_{startdate.replace('.','')}_{enddate.replace('.','')}.csv", index=False)
    
    return df
  • 주어진 키워드와 날짜 범위에 해당하는 네이버 뉴스 기사를 수집하여 데이터프레임에 저장하고, 필요시 CSV 파일로 저장합니다.
  • 작동 방식:
    • 날짜 범위 설정: pd.date_range를 사용해 startdate부터 enddate까지의 날짜 범위를 생성합니다.
    • 페이지 순회:
      • 각 날짜에 대해 뉴스 목록 페이지를 순회합니다. 페이지마다 시작 인덱스를 계산하고, 이를 포함한 URL을 생성합니다.
      • HTTP 요청: requests.get으로 URL에 대해 GET 요청을 보내고, raise_for_status()로 요청이 성공했는지 확인합니다.
      • JSON 파싱: json.loads를 사용해 서버 응답을 JSON 형식으로 파싱합니다.
    • 뉴스 목록 추출: JSON 데이터에서 contents 키를 통해 뉴스 목록을 가져옵니다.
      • 뉴스 목록이 비어 있으면 해당 날짜의 처리 루프를 종료합니다.
      • 각 뉴스 항목에 대해 HTML을 파싱하고, div.info_group a에서 두 번째 링크(기사 링크)를 선택해 뉴스 URL을 추출합니다.
      • 추출한 URL을 get_news_item 함수로 처리하여 데이터를 수집합니다.
      • 다음 페이지로 이동하기 위해 page를 증가시킵니다.
    • 데이터 저장: 수집된 데이터를 pandas 데이터프레임에 저장합니다. to_csv=True인 경우, 결과를 CSV 파일로 저장합니다.

 

5. 함수 호출

get_naver_news("청년취업사관학교", "2024.01.01", "2024.07.31", to_csv=True)
  • get_naver_news 함수를 실행하여 "청년취업사관학교"라는 키워드를 포함한 뉴스 기사를 2024년 1월 1일부터 2024년 7월 31일까지 수집하고, 결과를 CSV 파일로 저장합니다.