언어/Python

[python] CES 뉴스 크롤링 /csv 파일명에 날짜 지정하기

함부작 2021. 1. 8. 17:50

다음주면 CES 관련 뉴스가 쏟아질 듯 하여, 하루에 한 번 씩 CES관련 뉴스를 크롤링하는 코드를 만들어뒀다.

나중에 키워드만 쓱 바꾸면 다른 주제로도 잘 써먹을 수 있을 듯!


<목표>

1. CES 키워드로 네이버 뉴스탭에 나오는 뉴스를 크롤링한다

2. 네이버 뉴스는 알아서 클러스터링되기 때문에, 동일주제는 1개 뉴스만 가져온다

3. 페이징 처리가 되어있기 때문에 1페이지부터 10페이지까지 수집한다

4. 어제자 수집 파일과 비교하여 새로운 url만 수집한다


<하면서 배운것>

1. csv파일명에 날짜 넣기

2. 날짜 조작 (어제, 오늘 등) 

3. pandas 컬럼명 변경

4. pandas 컬럼 삭제(drop)

5. pandas index 삭제, 초기화 ( reset)

6. pandas left join

7. pandas 중복 값 제거


[0] 사이트 둘러보기

- 붉은 박스

네이버 뉴스에서는 검색어를 치면 query=검색어 

이런식으로 검색 쿼리를 조회하는 것을 알 수 있다

- 파란 박스

내가 가져오고자 하는 영역

제목, 상세내용 조금, 링크 

i) 큰제목 영역

크롬에서 제목에 마우스를 대고 우클릭 > 검사를 하면 html 코드를 확인할 수 있다

제목 영역의 html코드는 아래와 같다

 

class name이 "news_tit"인 것을 확인하면 된다

<a href="https://biz.chosun.com/site/data/html_dir/2021/01/08/2021010802386.html?utm_source=naver&amp;utm_medium=original&amp;utm_campaign=biz" class="news_tit" target="_blank" onclick="return goOtherCR(this, 'a=nws*h.tit&amp;r=1&amp;i=88127058_000000000000000000649285&amp;g=366.0000649285&amp;u='+urlencode(this.href));" title="LG전자가 만든 가상인간 ‘김래아’ CES 2021에서 연사로 출연">LG전자가 만든 가상인간 ‘김래아’ <mark>CES</mark> 2021에서 연사로 출연</a>

ii) 설명 영역

설명 영역에 마우스를 대고 검사를 눌러보면, html코드 확인할 수 있다.

 

class name이 "dsc_wrap"인 것을 확인하면 되겠다!

<div class="dsc_wrap"> <a href="https://biz.chosun.com/site/data/html_dir/2021/01/08/2021010802386.html?utm_source=naver&amp;utm_medium=original&amp;utm_campaign=biz" class="api_txt_lines dsc_txt_wrap" target="_blank" onclick="return goOtherCR(this, 'a=nws*h.body&amp;r=1&amp;i=88127058_000000000000000000649285&amp;g=366.0000649285&amp;u='+urlencode(this.href));">LG전자가 기획한 가상인간이 오는 11일(현지시각) 사상 첫 온라인 개최되는 세계 최대 IT·가전 전시회 <mark>CES</mark> 2021에 연사로 참여한다. 8일 LG전자에 따르면 오는 11일 오후 10시(미국 동부표준시 11일 오전 8시) LG전자의 <mark>CES</mark>...</a> </div>

 

또, 1페이지의 기사만 모으기에는 10개 밖에 안되기 때문에...

페이지 이동 버튼을 눌러주기 위해 1이 있는 곳에 마우스를 두고 우클릭하여 html코드를 검사해본다!

 

1페이지에서 2페이지, 3페이지로 계속 이동하기 위해 xpath를 활용하는게 좋다고 생각했고, xpath를 확인해보면 아래와 같다

 

 

1 페이지의 Xpath

//*[@id="main_pack"]/div[2]/div/div/a[1]

2페이지의 Xpath

//*[@id="main_pack"]/div[2]/div/div/a[2]

Xpath를 살펴보면 맨 뒤에 페이지 번호가 적혀있는 것 확인할 수 있다

/a[페이지번호] 

[1] 패키지 설치 및 불러오기

#기본적으로 필요한것들 설치, import
!pip install selenium
!apt-get update # to update ubuntu to correctly run apt install
!apt install chromium-chromedriver
!cp /usr/lib/chromium-browser/chromedriver /usr/bin
import sys
sys.path.insert(0,'/usr/lib/chromium-browser/chromedriver')
from selenium import webdriver
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
wd = webdriver.Chrome('chromedriver',chrome_options=chrome_options)
#wd.get("https://www.webite-url.com")
pip install bs4
import requests
from selenium import webdriver
from bs4 import BeautifulSoup
import pandas as pd
import time
import re
import csv
import datetime
from datetime import timedelta

 

[2] URL 가져오기

여기서  keyword만 바꿔주면 원하는 뉴스를 크롤링 할 수 있다.

keyword = 'ces' ## 여기 키워드 바꿔주면 바뀐 키워드의 검색 결과 수집
naver_news = 'https://search.naver.com/search.naver?where=news&sm=tab_jum&query='
search_url = naver_news+keyword
# url 가져오기
wd.get(search_url)

[3] 오늘자 데이터 만들기

 

#오늘자 데이터 담을 그릇 만들어두기
today_data=[]
#타임스탬프를 찍어주기 위해 날짜 작업
now = datetime.datetime.now()
dateandtime = (now.year,now.month,now.day,now.hour) 
#오늘 날짜로 timestamp만들어줌
timestamp = str(dateandtime[0])+'-'+str(dateandtime[1])+ '-'+str(dateandtime[2])

for i in range(1,11): ## 1페이지부터 10페이지까지 수집
  #i에 페이지 번호가 들어감
  
  #i페이지를 눌러준다
  wd.find_element_by_xpath(f'//*[@id="main_pack"]/div[2]/div/div/a[{i}]').click() # 페이지를 눌러줍니다
  #로딩을 기다리기 위해 5초 쉬고
  
  time.sleep(5)
  #뉴스 제목수집_위에서 본 것 처럼 class_name ='news_tit'인 것을 찾아주자
  news_title = wd.find_elements_by_class_name('news_tit') 
  
  #뉴스 상세 설명의 class_name = 'dsc_wrap'을 찾아주자
  news_desc = wd.find_elements_by_class_name('dsc_wrap')
  
  ## 여기서, find_element~~를 쓰면 1개 항목만 찾는거고, elements~~면 있는거 다 찾는것임
  
  # 데이터를 담을 틀을 만들어준다. 뉴스타이틀 수만큼 생성해둠
  news_title_dt = [None]*len(news_title)
  news_desc_dt = [None]*len(news_title)
  news_url_dt = [None]*len(news_title)
  timestamp_dt = [timestamp]*len(news_title)
  # 뉴스타이틀 개수만큼 순서대로 데이터 할당
  for rk in range(0,len(news_title)):
    news_title_dt[rk] = news_title[rk].text
    news_desc_dt[rk] = news_desc[rk].text
    news_url_dt[rk] = news_title[rk].get_attribute('href')
    today_data.append([news_title_dt[rk],news_desc_dt[rk],news_url_dt[rk],timestamp_dt[rk]])
    
today_dt = pd.DataFrame(today_data,columns=['title','description','url','timestamp'])   
    

[4] 어제자 데이터 가져오기

+timedelta(days=-1)

이런식으로 오늘 날짜에 더해주면 날짜 조정이 가능하다.

난 어제자를 지정하게 위해 -1을 했지만,

 months, years, hours 등 다양한 옵션으로 날짜와 시간을 조정할 수 있다.

#어제자 데이터 가져오기
yest = now+timedelta(days=-1)
yester = (yest.year,yest.month,yest.day,yest.hour) 
yest_timestamp = str(yester[0])+'-'+str(yester[1])+ '-'+str(yester[2])
yesterday_dt = pd.read_csv('ces-'+yest_timestamp+'.csv')
#컬럼명을 수정해줍니다*_*
yesterday_dt = yestdeay_dt.rename({'title_x':'title','description_x':'description','timestamp_x':'timestamp'},axis='columns')

[5] 데이터 조인하기 / null값 필터링 하기

어제자 데이터랑 오늘자 데이터를 조인해서 조인되지 않는 값만 남겨준다

#left outer join으로 url컬럼을 기준으로 조인 수행
#컬럼명이 같으면 left의 컬럼명은 _x 가 붙고  right table의 컬럼명엔 _y가 붙는다

comp_data = pd.merge(today_dt,yesterday_dt,how='left', on = 'url')

#right table에서 불러온 title_y 가 null인경우만 가져와준다

fin = comp_data[comp_data.title_y.isnull()]

[6] 일부 컬럼삭제하기 

어차피 null 이지만 컬럼을 정리해준다

fin = fin.drop(['title_y','description_y','timestamp_y','Unnamed: 0'], axis='columns')

[7] 중복값이 있으면 삭제하고 1개만 남기기

#url에 중복이 있으면 없애줍니다
fin2 = fin.drop_duplicates('url',keep='last')

[8] 인덱스 초기화, 삭제

중복데이터가 삭제되면서 인덱스가 뒤죽박죽~

인덱스 초기화,삭제 진행 

#중복을 없애면서 index가 뒤죽박죽이 됩니다. reset 해줍시다
fin2.reset_index(drop=True, inplace=True)

[9] csv파일로 저장, 앞에서 만들어둔 string 활용

원하는 파일명을 지정할 때 앞에서 만들어준 날짜  string 을 활용한다

fin2.to_csv('ces-'+timestamp+'.csv')

 

끝!