나른한 코딩 생활

[11일차] ABC 부트캠프 데이터 크롤링 실전 본문

ABC 부트캠프

[11일차] ABC 부트캠프 데이터 크롤링 실전

GerHerMo 2024. 7. 9. 13:25

이번주차부터는 실제로 데이터 크롤링을 해보면서 기초를 기르고

여러가지 매체에서 원하는 데이터를 수집해 보는 작업을 해볼 것이다.

 

오늘은 네이버 랭킹뉴스에서 얼론사별 데이터를 수집하고 시각화 하는 작업을 해보자


인터넷과 HTML 기초

인터넷 관련 용어

`IP 주소`
인터넷에 연결된 모든 컴퓨터에 부여되는 고유의 식별 주소를 의미한다

`도메인 이름 Domain Name`
IP 주소의 별칭이라고 생각하면 편함
just like 집주소

`URL Uniform Resource Locator`
ex ) ‘프로토콜://정보를 가진 컴퓨터 이름/디렉터리 이름/파일명’

`HTTP (HyperText Transfer Protocol)`
통신 규약 중 하나

'http://www...' 와 같이 하는 것 
https : security 서버

 

문서화 언어

총 3개 SGML , HTML, XML

SGML : 정해진 구조
HTML : 커스텀
XML : SGML + HTML

 

HTML 기본구조

- 태그를 열었으면 끝날 때 닫아라
- <태그명> ... </태그명>
- <head.> 태그 안에 화면에 대한 설명
- <body.> 문서 정보가 담김

 

글자/문단/목록 태그와 링크 태그

글자 태그

`<Hn> 태그`

`<FONT>태그` 

잘 안쓴다고 함

문단 태그

`<DIV>태그`
특정 파트만 수집하고 싶다 하는데 해당 구역이 DIV 태그 안에 나눠져 있을 수 있음

목록 태그

- 지금 이런
- 형태와 비슷한
- 태그가 목록 태그

`<OL>태그`

하이퍼링크 태그

`<A>태그`
문서, 사이트들 이동가능

 

`<A href= ~ >`
href 속성 : 페이지로 이동할 수 있게 도와주는 태그

 

멀티미디어 태그와 테이블 태그

요즘에는 굉장히 사용안함
이미지 , 동영상 등의 태그

이미지 태그

`<IMG src>`
src : sorce 의 약자
실제 어디있는 이미지인지를 알려줌

테이블 태그

테이블로 정렬하게 하는 태그
(자바의 컨테이너와 비슷해보임)

프레임 태그와 입력 태그

프레임 태그

입력 태그


`<INPUT> 태그`
`<INPUT type="text" ~ >` : 텍스트박스 형식의 인풋태그
`<INPUT type = "raido" ~ >` : 라디오 버튼 형식의 인풋태그
`<INPUT type = "checkbox" ~ >` : 체크박스 형식의 인풋태그

팝업 메뉴 & 리스트박스

TEXTAREA 태그

CSS (Cascading Style Sheet)

크롤링의 키 포인트 ! 
예쁜 홈피 만들기의 기초 

개발자와 디자이너의 작업 환경을 불리시켜 나온 스타일 시트 

CSS의 이해

CSS 정의 방법

 

클래스 선택자

예쁜 옷을 입힐려고 쓰는태그
특정 태그를 골라내는 방법?
- CLASS 를 다르게 입혀 원하는 태그의 자료를 크롤링 할 수 있음

ID 선택자

클래스 선택자와 유사하지만 화면 하나에 한번만 기술 할 수 있도록 함
즉, 한 문서에 한번만 사용할 수 있다는 차이점이  존재

태그가 아이디도 있고 클래스 도 있으면 ID 로 추출하면 더 정확하게 해당 정보를 가져올 수 있다.

 

그 외

동적 정적

마우스 우클릭 -> 페이지 소스 보기 하고

동적 : HTML 태그가 보이지 않음
정적 : 화면 안에 태그등의 정보가 보임


html 들어가있는 정보가 없을 수 있다
확인 후 정보가 있으면 -> 정적 -> 코랩에서 크롤링 가능
정보가 없으면 -> 동적 -> 아나콘다를 통해 스파이더 툴 사용 ( 어려움 ) 

코랩으로 데이터 수집

데이터 시간이 중요하다

datetime 은 미국시간 기준

위에서 아래로 데이터 수집
-> 데이터 수집 순서를 잘 정해야함 

크롤링할때의 핵심 중 하나
'keep going'
하나의 오류로 했던 크롤링들을 망치면 시간이 너무 낭비된다

오류가 나더라도 다음 자료, 또는 다음 페이지 등으로 예외처리 후 넘어가는게 필요하다


네이버 언론사별 랭킹 뉴스 데이터 수집 및 시각화

패키지 인스톨 및 임포트

!pip install konlpy
!pip install koreanize-matplotlib

from urllib.request import urlopen
from bs4 import BeautifulSoup # 태그로 정보를 추출할떄 (파씽할때) 사용함

import pandas as pd
import datetime
from pytz import timezone # datetime은 미국 시간이기 때문에 timezone을 통해 대한민국 시간 가져오기

import warnings
warnings.filterwarnings('ignore')

# -- 데이터 시각화 --
import matplotlib.pyplot as plt
import seaborn as sns
import koreanize_matplotlib

import konlpy
from wordcloud import WordCloud

많이 본 뉴스 크롤링

기본적인 임포트가 끝났으면 이제 본격적인 크롤링의 시작이다

# 1) 데이터 프레임 생성
data = pd.DataFrame(columns=['언론사명','순위','기사제목','기사링크','수집일자'])

# 2) 네이버 언론사별 랭킹 뉴스 URL
url = 'https://news.naver.com/main/ranking/popularDay.naver'

# 3) url 접속하여 html 가져오기
html = urlopen(url)

# 4) HTML 태그 파싱하여 변환
soup = BeautifulSoup(html, 'html.parser') # html parser 규칙으로 파싱한다

# 5) 네이버 랭킹 정보가 있는 div만 추출 -> rankingnews_box 가져오기
div = soup .find_all('div',{'class': 'rankingnews_box'})
#find_all 은 해당 박스를 다 가져옴 / find 는 하나만 찾음
# print(len(div)) 랭킹뉴스 박스만 80개

# 6) 네이버 랭킹 언론사명, 기사제목 등 데이터 수집

for index_div in range(0,len(div)):

  # 언론사명 추출
  strong = div[index_div].find('strong',{'class':'rankingnews_name'}) # 최초의 태그가 통째로 담긴다
  press = strong.text # 안의 값을 press에 넣는다

  # 5개의 순위 기사 정보 추출
  ul = div[index_div].find_all('ul',{'class':'rankingnews_list'})

  for index_r in range(0, len(ul)):
    li = ul[index_r].find_all('li')

    for index_l in range(0,len(li)):
      try:
        rank = li[index_l].find('em',{'class':'list_ranking_num'}).text
        title = li[index_l].find('a').text
        link = li[index_l].find('a').attrs['href'] # href 속성을 가지고 있는 애들만 가져온다

        # 임시(Temporaly) DataFrame
        temp_df = pd.DataFrame({'언론사명':press,
                                '순위':rank,
                                '기사제목':title,
                                '기사링크':link,
                                '수집일자':datetime.datetime.now(timezone('Asia/Seoul'))},
                               index=['순위'])
        # print(temp_df)
        data = pd.concat([data,temp_df],ignore_index=True)

      except:
        pass # try except 는 실제 데이터를 수집하는 구간에서 사용하는 것이 좋음 ( 예외처리 )
      print('Complets of ' + rank + ' : ' + title)

print('-'*50)
print(data.info())

 

이후에 data 에 대한 이상이 없는지 tail 과 head 함수를 사용하여 데이터 검사를 실시한다.

위 / 아래 : data.tail() / data.head()

data.to_csv('네이버랭킹뉴스_많이본뉴스_20240708.csv',encoding='utf-8-sig',index=False)

 

이후 데이터를 혹시 모를 상황에 대비해 배업 및 크롤링한 자료를 중간중간 백업한다


수집한 데이터 시각화

다음은 워드 클라우드를 위한 텍스트 전처리와 시각화 과정이다.

# 기사 제목을 활용한 워드 클라우드 -> text(텍스트 뭉치) 형태로 변환
text = ' '.join(li for li in data['기사제목'].astype(str)) # object 형태이기 때문에 str 형태로 변환함

# 워드 클라우드 시각화
font_path = '/content/BMDOHYEON_ttf.ttf'

plt.subplots(figsize=(25, 15))

wc=WordCloud(width=1000, height=700, font_path=font_path).generate(text)

plt.axis('off') # 눈금을 삭제시켜 차트를 이미지형식으로 보이게
plt.imshow(wc,interpolation='bilinear')
plt.show()

240708 기준 네이버 언론사 많이 본 순 위드 클라우드 시각화


많이 본 뉴스와 댓글이 많은 뉴스 비교

이번에는 위에 코드를 참고하여 data, text 변수를 각각 re_data, re_text로 바꾼뒤

url 을 네이버 댓글 랭킹 순으로 바꾸어준다.

 

# 1) 데이터 프레임 생성
re_data = pd.DataFrame(columns=['언론사명','순위','기사제목','기사링크','수집일자'])

# 2) 네이버 언론사별 랭킹 뉴스 URL - 댓글이 많은 뉴
url = 'https://news.naver.com/main/ranking/popularMemo.naver'

# 3번 이후는 re_data 에 대한 코드로 모두 같게 실행한다
re_text = ' '.join(li for li in re_data['기사제목'].astype(str))

 

전처리 과정이 끝나면 이제 두 개의 워드 클라우드를 한 화면에 띄운 다음 비교해보자

fig = plt.figure(figsize=(15,5))

rows = 1
cols = 2

ax1 = fig.add_subplot(rows, cols, 1)
ax1.imshow(wc,interpolation='bilinear')
ax1.set_title('언론사별 많이 본 뉴스')
ax1.axis('off')

ax2 = fig.add_subplot(rows, cols, 2)
ax2.imshow(re_wc,interpolation='bilinear')
ax2.set_title('언론사별 댓글이 많은 뉴스')
ax2.axis('off')

plt.show()


네이버TV연예 공감별 랭킹 뉴스 데이터 수집 및 시각화

새로운 노트북파일에(코랩) 이번에는 네이버 TV 연예 랭킹 뉴스 탭에서 공감별 데이터를 수집 및 시각화 해보자

 

패키지 설치 및 임포트 과정은 동일 하므로 넘어가겠다

 

 

랭킹 :: 네이버 TV연예

지금 가장 핫한 연예 이야기

entertain.naver.com

크롤링 하는 사이트들은 다음과 같다

위에서 부터 [ 좋아요 응원해요 축하해요 기대해요 놀랐어요 슬퍼요 ] 순으로 되어있다.

# 1) 데이터 프레임 생성
data = pd.DataFrame(columns=['순위', '공감종류', '기사제목', '기사링크', '기사내용', '공감수', '수집일자'])

# 2) 네이버 TV연예 랭킹 뉴스 URL
# https://entertain.naver.com/ranking/sympathy
# https://entertain.naver.com/ranking/sympathy/cheer
# https://entertain.naver.com/ranking/sympathy/congrats
# https://entertain.naver.com/ranking/sympathy/expect
# https://entertain.naver.com/ranking/sympathy/surprise
# https://entertain.naver.com/ranking/sympathy/sad
url_list = ['', '/cheer', '/congrats','/expect','/surprise','/sad']

for n in url_list:
  # 공감별 url 준비
  url = 'https://entertain.naver.com/ranking/sympathy' + n
  print(url)

  # 공감 종류
  sympathy = 'love'

  if n != '': # n이 비어있지 않으면
    sympathy = n.replace('/', '')

  # 3) html 가져오기
  html = urlopen(url)

  # 4) url 접속하여 html 가져오기
  soup = BeautifulSoup(html, 'html.parser')

  # 5) 랭킹 정보가 있는 li 만 추출 -> _inc ~ reply 가져오기
  li = soup.find_all('li',{'class':'_inc_news_lst3_rank_reply'})

  # 6) 네이버 연예 랭킹 30위 데이터 수집
  for index_l in range(0,len(li)):
    rank = li[index_l].find('em',{'class':'blind'}).text

   # 뉴스 제목
    title = li[index_l].find('a',{'class':'tit'}).text

   # 뉴스 내용
    summary = li[index_l].find('p',{'class':'summary'}).text

    # 뉴스 링크
    link = li[index_l].find('a').attrs['href']

    # 공감수
    temp_cnt = li[index_l].find('a',{'class':'likeitnews_item_likeit'}).text.replace('\n','') # 공백 삭제를 위한 replace
    # likeitnews_item_likeit like 의 공백 뒤의 like는 없어도 됨
    cnt = re.sub('[^0-9]','', temp_cnt) # 패키지함수 // ('[^0-9]') -> 숫자 제외 모두 삭제
    # [] -> 정규식 규격 / ^ -> 아닌 애들 / , '' -> 찾아서 없애 / temp_cnt -> 여기에서

    temp_df = pd.DataFrame({'순위':rank,'공감종류':sympathy,
                            '기사제목':title,'기사링크':link,
                            '기사내용':summary,'공감수':cnt,
                            '수집일자':datetime.datetime.now(timezone('Asia/Seoul'))},
                           index=['순위'])
    data = pd.concat([data,temp_df],ignore_index=True)

    print('Complets of ' + rank + ' : ' + title)

print('-'*50)
print(data.info())
input_sympathy = input('보고 싶은 공감 랭킹 뉴스를 입력하세요' + str(data['공감종류'].unique().tolist()))

text = ' '.join(li for li in data[data['공감종류']==input_sympathy].기사제목.astype(str))
font_path = '/content/BMDOHYEON_ttf.ttf'

plt.subplots(figsize=(25, 15))

wc=WordCloud(width=1000, height=700, font_path=font_path).generate(text)

plt.axis('off') # 눈금을 삭제시켜 차트를 이미지형식으로 보이게
plt.imshow(wc,interpolation='bilinear')
plt.show()

input 으로 sad 에 대한 링크를 받는다
공감이 '슬픔'인 TV연예 랭킹 위드 클라우드


내일은 유튜브 댓글 크롤링하는 정적 크롤링에 대해 알아보도록 하자