데이터 분석 입문

김현민·2022년 7월 26일
0

Data Analysis

목록 보기
2/2
post-thumbnail

캐글
kaggle.com
데이터셋 제공 및 분석 경쟁

구글 스프레드시트 활용
XML analysis toolpak 사용 - 상관계수 계산 가능

구글 Colab - 개발 환경
별도 언어 프로그램 설치 필요 없음

파이썬 기본 문법
주석
한줄 : #
여러줄 : '''
기본으로 마지막줄 연산만 출력
print 사용하여 코드 제대로 출력되는지 확인

변수 할당
변수의 종류 자료형
자료형 확인 type()
1) 정수형 int
2) 실수형 float
3) 문자형(따옴표필수) str
4) 리스트는 여러개를 담아놓은 자료형 list
ex.
list1 = ['a','b','c','d']
print(list1[0])

리스트의 활용
1) 리스트는 순서가 0부터 시작한다.
2) append - 추가
ex. list1.append(6)
3) extend - 리스트 합치기
ex. list1.extend(list2)

for 문
1) for 문 끝에는 :을 붙여야 된다
for문 안에 넣으려면 indent(들여쓰기) 해줘야 함
indent 안하면 for문 밖으로 나감
for와 range 함수 같이 쓴다.
range 함수 끝숫자는 포함 안됨
ex. range(0,5) = [0,1,2,3,4]

if문 (else 뒤에는 조건문 없음)

비교연산자
!= 같지 않다
== 같다

ex.
if <조건문>:
수행할 문장1
수행할 문장2

elif <조건문>:
수행할 문장 a
수행할 문장 b

else :
수행할 문장A
수행할 문장B

함수 세팅
def 함수이름(함수인자1, 함수인자2 ...):
수행할 코드
return 최종 결과

ex.
def volume(width, height, length):
return widthheightlength - return은 결과값을 나중에 또 사용할 수 있도록 조치 하는 것임
volume(5,8,2)

이렇게 치면 원래는 출력이 안되는게 정상인데
colab에서 사용자 편의를 위해 출력이 된다.
따라서 print 꼭 감싸줘야 출력되는 것

return을 써야만 다른 변수에다가 결과값을 저장할 수 있다.
print는 출력만 되는 것이다.

Class와 예외처리
클래스
ex.
붕어빵 틀(기계) = 클래스
붕어빵 = 인스턴스(객체)

클래스안에 함수 정의하면 해당 객체만 함수 사용 가능
ex.
객체명.함수명(인자1, 인자2...) 방식으로 사용

클래스 개념을 이해해야 하는 이유는 앞으로 패키지나 모듈에서
제공하는 기능을 이용하게 될 것이기 때문

예외처리
데이터들이 일관되지 않을때 try ~ except문을 이용해서
가공

패키지와 데이터 프레임
패키지(모듈이라고도 함) : 누군가 이미 만들어놓은 함수, 클래스 덩어리
import라는 명령어로 바로 사용 가능
ex.
import pandas as pd

판다스는 파이썬 데이터 분석을 위한 필수 패키지 중 하나
판다스가 제공하는 데이터프레임 매우 중요
엑셀시트 같은 표를 파이썬에서 구현한게 데이터 프레임이다
라고 이해하면 될듯

df.head() # 상위 5개행만 출력
df.tail() # 하위 5개행만 출력
df.sample(n) # 무작위 n개 sample 출력

concat([df1,df2]) # 데이터 합치기

데이터 프레임 저장해놓고 또 사용하고 싶은 경우에는
파일로 저장
total_df.to_csv('data.csv', index=false) 실행
index=false 부분은 맨 처음 unnamed 부분 제거
파일로 불러오기
new_df = pd.read_table('data.csv', sep=',') # sep 부분은 어떤것(,)을 기준으로 열을 나누겠다

웹과 웹스크래핑 패키지 이해하기
크롤링이란 웹 페이지로부터 데이터를 추출하는 행위를 말함
크롤러(crawler) : 크롤링하는 소프트웨어

웹사이트는 텍스트 덩어리(HTML)
html은 <태그>라는걸 이용해서 작성
head 태그 한쌍, body 태그 한쌍

<html>
	<head>
  		<title> HTML 문서 </title>
	</head>
	<body>
  		<h1> 이것은 HTML 문서입니다! </h1>
    </body>
</html>

웹페이지 오른쪽 클릭 - 소스코드 보기 가능
꺽쇠<>로 이루어진 걸 '태그'라고 한다.

네이버 뉴스 크롤링 실습

오늘 실습 과정에 대한 간략한 설명!

  1. 네이버 뉴스 페이지에서, 각각의 뉴스가 가진 URL을 리스트 형태로 만들어서 저장해놓는다.
  2. 각각의 뉴스가 가진 URL 에 접근하면 제목과 본문 내용이 표시가 될텐데, 그 제목과 본문 내용을 newspaper3k 패키지를 이용해서 크롤링을 진행한다.
  3. 크롤링을 할 때마다 크롤링 되어온 내용을 데이터프레임에 계속 이어붙인다.
  4. 크롤링이 끝났다면, 정보가 저장되어있는 데이터프레임을 csv 파일로 저장한다.

크롤러를 만들기 전에 우선 필요한 도구들을 임포트합니다.

!pip install newspaper3k

import requests
import pandas as pd
from newspaper import Article
from bs4 import BeautifulSoup 입력하세요

변수를 이용해서 URL을 완성하는 연습을 해봅시다. 앞으로 자동으로 네이버 뉴스 URL에 접속하게 될텐데, 이 중 카테고리 번호, 날짜, 페이지 번호의 경우만 바꾸면 해당 카테고리, 날짜, 페이지의 URL에 접근할 수 있습니다.

페이지번호를 의미하는 변수로 page_num을 선언하고, 카테고리를 의미하는 변수로 code를 선언하고, 날짜를 의미하는 변수로 date를 선언합시다. 숫자로 변수를 선언하면 정수형 자료형이 되지만, URL이라는 것은 실제로는 문자열입니다.

그래서 조합하기 전에 정수형 변수에 str(변수명)을 해주게 되면, 해당 변수는 문자열 자료형으로 변경되므로 다른 문자열들과 조합이 가능하게 됩니다. 조합 방법은 더하기 기호인 +로 해주시면 됩니다. 자, 그러면 2020년 5월 6일경제(101번 카테고리) 뉴스란의 1페이지 URL을 만들어볼까요?

page_num = 1
code = 101
date = 20200506

문자열은 더하면 이어붙이기가 된다
문자열과 숫자는 더할 수 없다 -> 숫자를 문자열로 바꿔주는 작업을 해야한다

url = 'https://news.naver.com/main/list.nhn?mode=LSD&mid=sec&sid1='+str(code)+'&date='+str(date)+'&page='+str(page_num) 
print(url)

완성된 URL 클릭하여 실제 네이버 뉴스 페이지로 이동해봅시다.

변수를 이용해서 URL을 만드는 법을 이해했다면, 이제 해당 URL에 있는 HTML 소스 코드를 가져오는 방법을 이해해야 합니다. 이는 requests라는 패키지의 get이라는 모듈을 통해서 가능합니다.

requests라는 패키지에 있는 get이라는 모듈(함수)에 url을 입력으로 하고, 이를 변수에 저장한 후에 변수.content를 하면 해당 URL의 HTML 코드를 받아올 수 있습니다. 앞서 만든 URL로 테스트를 해봅시다.

news = requests.get(url)
news.content

어라? HTML 코드가 나오기는 하는데, 우리가 예상한 HTML 코드가 아니라 굉장히 짧은 HTML 코드가 출력됩니다. 그리고 중간에 'Access Denied'와 'Request is denied' 라는 문장이 적혀져 있는데요. 네. 저희는 접근을 차단당했습니다. 네이버가 보기에 방금 접근이 크롤링 행위를 하는 것으로 추정되어서 네이버가 저희를 차단한 것인데요. 이런 경우에는 일종의 편법을 사용해서 사용자인척 해야합니다.

이에 대한 자세한 내용은 아래의 링크를 통해 확인할 수 있습니다.

https://hogni.tistory.com/64

위의 링크에서 설명하고 있듯이, 유저 에이전트를 인자로 넣어서 get 함수를 사용하면 위와 같은 차단을 뚫고 크롤링을 할 수 있습니다. 한 번 다시 해볼까요? 아래의 headers는 강사의 브라우저에서 추출한 값으로 여러분들은 여러분들의 에이전트 정보를 추출해서 사용하셔도 됩니다!

headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36'}

news = requests.get(url, headers=headers)
news.content

이번에는 정상적으로 해당 페이지의 HTML 코드가 추출된 것을 확인할 수 있습니다! 이제 이를 이용해서 원하는 날짜의, 원하는 카테고리의, 원하는 페이지에 접속해서 해당 페이지에 있는 뉴스 기사들의 URL을 전부 뽑아내는 함수를 만들어봅시다.

다음은 BeautifulSoup를 이용해서 원하는 페이지 수와, 카테고리 번호, 날짜를 입력하면 해당 URL에 접속하여 뉴스 url 리스트를 리턴하는 함수입니다.

각 기사의 URL 주소를 가져와서 리스트에다가 담아주는 함수

  # 크롤링할 페이지 수, 카테고리, 날짜
def make_urllist(page_num, code, date): 
 각각 기사들이 가진 url을 저장하기 위한 빈 리스트를 하나 만들어놓는다.
  urllist= []

  # 1 ~ page_num까지 정해진 페이지만큼 반복.
  for i in range(1, page_num + 1):

		# 함수의 입력으로 된 변수들로 주소를 조합
    url = 'https://news.naver.com/main/list.nhn?mode=LSD&mid=sec&sid1='+str(code)+'&date='+str(date)+'&page='+str(i)   

		# requets 패키지의 모듈(함수)을 호출
		# import 패키지이름 으로 '임포트'했다면 '패키지이름.모듈이름' 으로 모듈을 호출합니다.
    news = requests.get(url, headers=headers)
    news.content

    # BeautifulSoup 모듈을 사용하여 HTML 페이지를 분석
    soup = BeautifulSoup(news.content, 'html.parser')

    # 해당 페이지에 있는 각각의 뉴스가 news_list 라는 리스트로 저장됩니다!
    news_list = soup.select('.type06_headline li dl')
    news_list.extend(soup.select('.type06 li dl'))

    # 뉴스 리스트에 있는 각 뉴스로부터 a 태그인 <a href ='주소'> 에서 '주소'만을 가져온다.
    for line in news_list:
        urllist.append(line.a.get('href'))
  return urllist

어디 한 번 함수를 실행해볼까요? 저는 2020년 5월 6일, 경제 기사(코드로 101번)를 2페이지까지 탐색해서 URL 리스트를 받아오겠습니다. 한 페이지당 뉴스 기사가 20개가 있으니까, 2페이지까지 탐색하면 총 40개의 URL 리스트를 받아와야 합니다.

url_list = make_urllist(2, 101, 20200506)
print('뉴스 기사의 개수 :',len(url_list))

총 40개의 URL이 존재하는 것을 확인할 수 있습니다. 5개만 출력해봅시다.

url_list[:5]

뉴스 URL들이 잘 저장되어져 있는 것을 확인할 수 있습니다. 뉴스 URL 리스트를 받아올 수 있다면, 이제 newspaper3k를 이용해서 뉴스 기사들을 파이썬을 저장할 수 있겠군요!

앞으로 결과를 확인할 때 코드로부터 바로 어떤 카테고리인지 확인하기 쉽도록 code를 key, 실제 카테고리를 value로 가지는 dictionary를 만들어두겠습니다. 이번 실습에 사용할 카테고리들에 대해서만 생성했습니다.

참고 : https://wikidocs.net/16

파이썬의 dictionarys는 선언 할 때는 'key' : 'value'의 형태로 선언을 합니다.

idx2word = {'101' : '경제', '102' : '사회', '103' : '생활/문화', '105' : 'IT/과학'}

선언 후에는 key 값을 넣으면 맵핑되어져 있는 value 값이 리턴됩니다.

idx2word['101']
idx2word['105']

이제 idx2word라는 딕셔너리를 통해서 이 코드 번호가 무슨 카테고리였지? 하고 생각이 안 날때마다 idx2word에 입력을 넣어서 확인할 수 있습니다.

다음은 newspaper3k를 통해서 만들어진 함수로 url 리스트와 해당 url이 어떤 카테고리인지 코드를 알려주면 이를 통해 데이터프레임을 생성하는 함수입니다.

url과 카테고리를 알려주면 url로부터 본문을 추출하여 데이터프레임을 생성하는 함수.

def make_data(urllist, code):
본문을 저장하기 위한 빈 리스트를 만들어놓는다.
  text_list = []
  
  for url in urllist:
    article = Article(url, language='ko')
    article.download()
    article.parse()
    text_list.append(article.text)

  df = pd.DataFrame({'news': text_list})
  df['code'] = idx2word[str(code)] 
  return df

앞서 저장해둔 경제 카테고리(코드 101)40개의 url 리스트로부터 데이터프레임을 생성합니다.

data = make_data(url_list, 101)
data[:10] # 상위 10개 출력

이번에는 하나의 카테고리만이 아니라 다른 카테고리의 뉴스들에 대해서도 수집을 하고자 합니다. 가령, 특정 날짜의 사회, 생활/문화, IT/과학의 뉴스들을 수집해본다고 해볼게요. 수집을 원하는 카테고리 코드들을 저장한 리스트를 만들어둡니다.

code_list = [102, 103, 105]

이 코드 리스트, 그리고 날짜, 페이지 수를 입력으로 받는 make_total_data라는 함수를 만듭니다. 이 함수는 내부적으로 앞서 만든 make_urllist 함수와 make_data 함수를 호출하도록 합니다.

원하는 날짜, 원하는 페이지 수, 수집을 원하는 카테고리를 입력.

def make_total_data(page_num, code_list, date):
아무것도 들어있지 않은 데이터 프레임을 만든다 
  df = None

  # 각 카테고리에 대해서 아래의 코드를 수행.
  for code in code_list:

    # make_urllist 함수 : 페이지 수, 카테고리, 날짜를 입력하면 url을 추출하는 함수.
    url_list = make_urllist(page_num, code, date)

    # make_data 함수: url과 카테고리를 알려주면 데이터프레임을 만들어주는 함수
    df_temp = make_data(url_list, code)
    print(str(code)+'번 코드에 대한 데이터를 만들었습니다.')

    if df is not None: # df가 채워져있나요?
      # for문 내에서 데이터프레임이 새로 만들어질 때마다
      # 이전 데이터프레임과 지속적으로 결합
      df = pd.concat([df, df_temp])
    else:
      df = df_temp

  return df

5월 6일 기사에 대해서 2페이지까지, 102번, 103번, 105번 코드를 가지는 카테고리의 뉴스들을 수집해서 데이터프레임에 저장해보겠습니다.

df = make_total_data(2, code_list, 20200506)

1페이지당 20개의 뉴스 기사가 존재하므로, 총 120개의 뉴스기사가 수집되어야 합니다.
2(페이지 수) x 20(페이지당 url) x 3(카테고리 수) = 120

print('뉴스 기사의 개수 :',len(df)) # len은 데이터 길이를 출력하는 함수

120개의 뉴스 기사가 수집되었네요. 임의로 10개의 샘플을 선택하여 출력해보겠습니다.

(임의 출력이므로 아래 결과는 사람마다 다를 수 있습니다.)

df.sample(10)

10개의 샘플이 출력해보았는데, 3개의 카테고리가 전부 존재하는 것을 확인할 수 있습니다. df.sample(10)을 여러번 호출하면서 달라지는 결과를 확인해보세요. 수집한 데이터의 샘플들을 랜덤으로 여러번 출력해보는 것은 데이터를 파악하는데 꽤 큰 도움이 됩니다.

이제 뉴스 크롤러가 완성되었습니다! 이번에는 좀 더 많은 데이터를 수집해볼까요? 이번에는 3개의 카테고리에 대해서 총 10개의 페이지에 대해서 크롤링하겠습니다.

크롤링해서 수집되는 전체 뉴스의 양은 네이버 측의 상황에 따라서 코드를 실행하는 날에 따라 다소 다를 수 있음이 확인되었습니다. 향후 실습에서 제가 최종 수집한 뉴스기사와 여러분들이 동일한 코드를 실행해서 얻은 뉴스 기사의 개수가 다소 다르더라도 신경쓰지 않으셔도 됩니다.

df = make_total_data(10, code_list, 20200506)

위 코드는 최소 수십분의 시간이 걸립니다. 혹시나 빠르게 진행하고 싶으시다면 첫번째 인자에 10이 아니라 1을 넣으셔도 괜찮습니다.

df

df.to_csv('news_data.csv', index=False)

df = pd.read_table('news_data.csv', sep=',')

df

2주차
1. 코드는 다 외울 필요 없다
2. 리스트컴프리헨션이라는 코드는 매우 어렵다 (불용어 제거)
개념만 얼추 기억
3. 실습 난이도는 신입 데이터 분석가가 실제 현업에서
수행할만한 난이도임
4. 반복학습 추천

텍스트 마이닝 전 세팅하는 법
Colab에는 기본적으로 한글폰트가 깔려있지 않기때문에
Colab 실행할때마다 한글 폰트를 깔아줘야 한다.

import matplotlib as mpl
import matplotlib.pyplot as plt
 
%config InlineBackend.figure_format = 'retina'
 
!apt -qq -y install fonts-nanum
 
import matplotlib.font_manager as fm
fontpath = '/usr/share/fonts/truetype/nanum/NanumBarunGothic.ttf'
font = fm.FontProperties(fname=fontpath, size=9)
plt.rc('font', family='NanumBarunGothic') 
mpl.font_manager._rebuild()```
코드를 입력하세요
실행 후에 '런타임'-'런타임 다시시작'
위 코드는 한글 폰트 등록하는 코드로 그냥 복붙해서 쓰면 된다

Colab 껐다 켜면 꼭 다시 폰트 깔고 CSV 불러와줘야한다. 
df.isnull() # 빈행이 있어 ?
print(df.isnull().sum()) # true가 몇개 있어?

#빈행 = 결측치

df('news').unique()
#특별한 값 출력
df('news').nunique() # 갯수

unique는 중복된것들은 제외하고 하나씩만 존재하는 값들 출력해줘

중복된 열들 지우는 코드

df.drop_duplicates(subset=['news'])
#이렇게 되면 중복제거된 데이터 프레임 저장이 안된다. 
#저장 하려면 다른 데이터프레임 즉,
df2 = df.drop_duplicates(subset=['news'])
#라고 선언 해줘야함, 그러나
df.drop_duplicates(subset=['news'],inplace=True)
#위대로 입력하면 기존 데이터프레임에 중복제거된 값들을 저장한다.

len(df) 해보면 개수 확인 가능 

#중복제거된 기사들 카테고리별 개수 세고 싶다 
df['code].value_counts()
#개수가 바로 출력된다
df['code].value_counts().plot(kind='bar')
#막대 그래프 형태로 출력된다

불용어 제거
ex.
뉴스에 나오는 '뉴스', '최근' 등 필요없는 단어 제거

형태소 분석기 사용한다. - '토큰화'라고 부름
형태소 분석기 패키지 - KoNlPy

!pip install konlpy
from konlpy.tag import Okt
#Okt 함수 이름은 개발자가 정한것이다

tokenizer = Okt() #명사분석기를 tokenizer라는 이름으로 사용

kor_text = '밤에 귀가하던 여성에게 범죄를 시도한 대 남성이 구속됐다서울 제주경찰서는 \
            상해 혐의로 씨를 구속해 수사하고 있다고 일 밝혔다씨는 지난달 일 피해 여성을 \
            인근 지하철 역에서부터 따라가 폭행을 시도하려다가 도망간 혐의를 받는다피해 \
            여성이 저항하자 놀란 씨는 도망갔으며 신고를 받고 주변을 수색하던 경찰에 \
            체포됐다피해 여성은 이 과정에서 경미한 부상을 입은 것으로 전해졌다'
#'/'를 붙임으로써 줄이 계속 연결되고 있다고 파이썬한테 알려주는거임

print(tokenizer.noun(kor_text))

# 제가 이 데이터의 뉴스들의 토큰화 된 결과를 지속적으로 출력해보면서 지속적으로 추가한 단어들입니다.
stop_words = ['기자', '제공', '무단', '배포', '무단배포', '배포금지', '이번', '위해', '라며', '금지', '뉴스', '통해', '오늘', '지난달', '지난', '대한', '경우', '관련', '뉴시스', '현재', '지난해', '때문', '지금', '또한', '만큼', '최근', '당시', '올해', '대해', '다시', '모두']

df['tokenized'] = df['news'].apply(tokenizer.nouns)
# apply 사용하게 되면 바로 열마다 각각 적용되서 실행
# 토큰화된 열을 추가한 것임

리스트 컴프리헨션 연산 예시

test_list = ['경찰서', '상해', '혐의', '씨', '구속', '수사', '일']
remove_word_list = ['경찰서', '구속']
test_list = [item for item in test_list if item not in remove_word_list and len(item) > 1]

print(test_list)

df['tokenized'] = df['tokenized'].apply(lamda x: [item for item in test_list if item not in remove_word_list and len(item) > 1])
# 이 하나의 행을 x로 일단 저장하고 

코드 분석을 좀 해보자면,
test_list = [item for item in test_list]
우리가 item 이라는 요소를 갖는 리스트를 만들긴 만드는데
어떤 아이템이냐하면 for문 돌려서 하나씩 갖고 와라
실행 시키면
test_list와 동일 출력물
하지만 뒤에 if문을 담으로써 해당조건에 fit 한 애들만 담을꺼야 가 되는 것임

위에 우리가 함수로 정의한게 아니고
조건문을 통해서 연산형태로 만들어놨기 때문에
lamda를 써야 한다.

df[df['code']=='사회']['tokenized'].values
#values 함수로 행 전체를 하나로 연결 

#numpy 패키지의 hstack 함수 쓰면 나눠진 리스트를 하나로 합쳐줌 

import numpy as np

temp_list = [['a','b','c'],['i','j']]

result = np.hstack(temp_list)
print(result)

# ['a','b','c','i','j']
import numpy as np
soical_news = np.hsack(df[df['code']=='사회']['tokenized'].values)
print(len(social_news))
#list에 속한 인자 개수 

from collections import Counter

#counter 함수는 리스트에서 각 인자들 개수를 세어주느 함수 


#워드 클라우드 패키지 다운로드 필요 

from wordcloud import WordCloud

''.join(soical_news)
# ''는 단어 사이에 뭘 넣을건지 

# 사용하고자 하는 폰트의 경로. Colab에서는 이 경로를 사용하시면 됩니다.
fontpath = '/usr/share/fonts/truetype/nanum/NanumBarunGothic.ttf'

plt.figure(figsize = (15,15)) #이미지 표시하기 위한 코드

wc = WordCloud(max_words = 사용할 단어의 수 , width = 가로, height = 세로, font_path = fontpath).generate('입력 문자열')
plt.imshow(wc, interpolation = 'bilinear')
profile
Better late than never

0개의 댓글