코린이 성장일기

[멋사AI스쿨8기] WIL : 데이터 수집 기초 본문

멋사 AI스쿨8기

[멋사AI스쿨8기] WIL : 데이터 수집 기초

JJouni 2023. 1. 12. 17:43

파이썬을 제대로 배운지는 어느새 2주차,

멋사를 시작한지는 어느새 3주차에 접어들었다.

빠르면 빠르고 느리면 느린 시간?

하루하루 수업량 따라가느라 벅차서 느린데,

언제 3주차가 되었디야,,,,

 

멋사에는 두 가지 팀이 존재한다. 으샤으샤팀하고 재잘재잘팀.

아직 으샤으샤팀은 구성되지 않았고, 어떻게 진행되는지 모르겠다.

재잘재잘팀과도 만난지 어느새 3주차가 되어간다.

한 주 힘들게 수업하고 만나면 위로가 되는 사람들.

그리고 함께 공부할 수 있어서 더 많이 배우고 응원이 된다.

 

나는 8조, 하지만 빨랐조? 이다

 


 

이번주는 박조은 강사님과 함께 파이썬 판다스를 통해 데이터를 수집하는 것을 배웠다. 

강사님 성함이 나랑 같아서 내적 친밀감이 많이 생겼달까.. 더 열심히 하고 싶어졌다. 

 

어찌 되었든, 이번 주간의 배운 것을 돌아보고자 한다. 

 

먼저, 판다스의 자료구조를 배웠다. 여기엔 Series와 DataFrame이 있다.

간단히 말해 행열이다. 우리는 데이터들을 엑셀처럼 정리하게 될 예정이니 DataFrame을 많이 사용했다.

 

관련 함수들을 살펴보자.

데이터 프레임 생성
df = pd.DataFrame()

컬럼추가
df[”새로운 제목“] = 넣을 내용

컬럼 가져오기
df[”컬럼명”]

두 개 이상 컬럼명 가져오기
df[["컬럼명1", "컬럼명2"]]

값 가져오기
레이블기반 : df.loc['행', '열']
정수기반 : df.iloc['행', '열']

행/열 삭제하기
df.drop(labels=None, axis=0, index=None, columns=None, level=None, inplace=False, errors='raise')

위/아래에서 5개 보기 (중괄호 안에 숫자를 넣어주면 그만큼 볼 수 있다. default =5)
df.head() /df.tail()

데이터 프레임 정보 보기
df.info()

데이터 프레임 크기 출력(행, 열)
df.shape

데이터의 타입만 보기
df.dtypes

수치형 데이터의 기술통계 값 보기
df.describe()

정렬하기
df.sort_values(by=[ ], ascending=[ ])

파일로 저장하기
df.to_csv("이름.csv", index=False) 
     - 인덱싱 저장 안함
     - 저장 중에 한글이 깨질 경우, encoding="cp949" 사용

파일 읽어오기
pd.read_csv("이름.csv") 

판다스에 자료구조에 대해서 간략하게 배워봤다. 

관련해서 Pandas.DataFrame에 대해 더 공부하고자 하면 아래 사이트 활용하면 좋겠다.

https://wikidocs.net/151013

 

Pandas.DataFrame 클래스 기본

기본적인 DataFrame 구조에 대해 공부하고 DataFrame 객체를 만들어보도록 하겠습니다. ## Pandas.DataFrame 클래스 구조 pandas datafram…

wikidocs.net

 

 

 

그리고 이를 활용해서 FinanceDataReader를 살펴보았다.

한국 주식 가격, 미국 주식 가격, 지수, 환율, 암호화폐 가격, 종목 리스팅 등 금융 데이터 수집라이브러리를 의미한다.

강사님께서 공유해주신 공부하면 좋을 사이트들

 

GitHub - financedata-org/FinanceDataReader: Financial data reader

Financial data reader. Contribute to financedata-org/FinanceDataReader development by creating an account on GitHub.

github.com

 

FinanceDataReader 사용자 안내서

FinanceDataReader 사용자 안내서

financedata.github.io

 

Data Readers — pandas-datareader 0.10.0 documentation

 

pandas-datareader.readthedocs.io

 

 

내가 금융에 대해 무지하니,, 정말 이게 뭘 설명하는지 그냥 암기식으로 집어넣었다..

그래서 종목들을 불러오는 방법들을 익혀보았다.

KRX : KRX 종목 전체
KOSPI : KOSPI 종목
KOSDAQ : KOSDAQ 종목
KONEX : KONEX 종목
NASDAQ : 나스닥 종목
NYSE : 뉴욕증권거래소 종목
SP500 : S&P500 종목

여기서 한국거래소 상장종목을 한번 가져와 본다

# 데이터 분석을 위해 pandas 불러오기
import pandas as pd

# FinanceDataReader 를 fdr 별칭으로 불러옵니다.
# 라이브러리의 version을 확인하고 싶을 때는 .__version__ 으로 확인합니다. 
import FinanceDataReader as fdr

url = 'http://kind.krx.co.kr/corpgeneral/corpList.do?method=download&searchType=13'
df_listing = pd.read_html(url, header=0, flavor='bs4', encoding='EUC-KR')[0]
cols_ren = {'회사명':'Name', '종목코드':'Symbol', '업종':'Sector', '주요제품':'Industry', 
                    '상장일':'ListingDate', '결산월':'SettleMonth',  '대표자명':'Representative', 
                    '홈페이지':'HomePage', '지역':'Region', }
df_listing = df_listing.rename(columns = cols_ren)
df_listing

# 한국거래소 상장종목 전체 가져오기
df = fdr.StockListing("KRX")
df

관련해서 기술통계값도 요약해본다.

output을 보면 숫자가 많은데, 여기 중에

2.498210e+07

이런 숫자가 있는데, 이를 과학적 기수법, 과학적 표기법이라고 한다. 저 숫자는 앞의 값의 10의 7승이라는 의미이다.

 

 

Pandas만으로 인터넷의 데이터를 수집해보았다. 

수집할 때엔 URL을 잘 가져오는 것이 중요하다고 하셨다. 즉 검색 값을 설정하는 것!

그러기 위해서 쿼리스트링을 잘 알아야하는 것 같다. 웹사이트에서 검색한 URL 안에 우리의 검색어가 들어있는데, 이렇게 검색어를 URL에 붙여서 넘기는 문구를 쿼리스트링이라고 한다. 요 문자열을 검색어에 맞게 잘 변경해주면 된다. 

 

쿼리스트링이란?

쿼리 문자열은 지정된 매개변수에 값을 할당하는 Uniform Resource Locator의 일부입니다. 쿼리 문자열에는 일반적으로 웹 브라우저나 기타 클라이언트 응용 프로그램에서 기본 URL에 추가한 필드가 포함됩니다.

https://hostname"port번호"/folder/file?변수1=값1&변수2=값2

 

쿼리스트링을 잘 받아서, 판다스의 read_html로 정보를 받아 네이버 금융기사 수집해보자.

수집 전 알아두면 좋을 함수들

  • html 파일 읽어오기
    • pd.read_html(url, encoding="cp949")
  • 결측 데이터 제거하기(axis 0:행, 1:열)
    • table[0].dropna()
  • 데이터 프레임 합치기
    • pd.concat([df1, df2, df3])
  • 중복데이터 제거
    • df.drop_duplicates()
  • 인덱싱 다시하기(drop=True는 기존 인덱스 제거)
    •  .reset_index(drop=True)
  • 날짜 column의 첫 row값 확인
    • date = df.iloc[0]["날짜"]

 

 

네이버 금융 뉴스기사 한 페이지를 수집하는 함수를 만들고 원하는 만큼의 정보를 가져올 수 있다. 

import pandas as pd
import time
from tqdm import tqdm, trange

# get_one_page_news 함수 만들기
def get_one_page_news(item_code, page_no):
    """
    get_url 에 item_code, page_no 를 넘겨 url 을 받아오고
    뉴스 한 페이지를 수집하는 함수
    1) URL 을 받아옴
    2) read_html 로 테이블 정보를 받아옴
    3) 데이터프레임 컬럼명을 ["제목", "정보제공", "날짜"]로 변경
    4) tmp_list 에 데이터프레임을 추가
    5) concat 으로 리스트 병합하여 하나의 데이터프레임으로 만들기
    6) 결측치 제거
    7) 연관기사 제거
    8) 중복데이터 제거
    9) 데이터프레임 반환
    """
    # 1) URL 을 받아옴
    news_url = get_url(item_code, page_no)
    # 2) read_html 로 테이블 정보를 받아옴
    temp_table = pd.read_html(news_url)
    # tmp_list = []
    tmp_list = list()
    for tmp in temp_table[:-1]:
        # 3) 데이터프레임 컬럼명을 ["제목", "정보제공", "날짜"]로 변경
        tmp.columns = temp_table[0].columns
        # tmp_list 에 데이터프레임을 추가
        tmp_list.append(tmp)
    # 5) concat 으로 리스트 병합하여 하나의 데이터프레임으로 만들기
    df_one = pd.concat(tmp_list)
    # 6) 결측치 제거
    df_one = df_one.dropna()
    # 7) 연관기사 제거
    df_one = df_one[~df_one["제목"].str.contains("연관기사")]
    # 8) 중복데이터 제거
    df_one = df_one.drop_duplicates()
    return df_one
    
    
item_code = "005930"
page_no = 10
news_list = []

# for no in tqdm(range(1, page_no + 1)):으로도 쓸 수 있음
for no in trange(1, page_no + 1):
    df_one_news = get_one_page_news(item_code, no)
    news_list.append(df_one_news)
    # 수집 대상 서버의 부담을 줄이기 위해 쉬었다가 가져오기
    time.sleep(0.2)
    
# 수집한 페이지마다 인덱스가 0부터 시작을 하는데 
# 전체 인덱스를 다시 부여하고자 할 때 .reset_index(drop=True) 를 사용
# reset_index() 에서 drop=True 를 사용하면 기존 인덱스는 제거합니다.
df_news = pd.concat(news_list)
df_news = df_news.reset_index(drop=True)
df_news

 

 

네이버 금융 기사는 read_html만으로도 수집할 수 있었지만, 그렇지 않은 경우들이 있다. 서버에서 봇의 접근이나 악성 접근을 막아두었을 수 있기 때문인데, 이 때는 requests를 통해 URL에 접근하자. 

 

read_html을 썼는데 No table 오류가 나면 requests를 통해서 HTTP 요청을 하자

HTTP ?
HTML 문서와 같은 리소스들을 가져올 수 있도록 해주는 프로토콜이다. HTTP는 웹에서 이루어지는 모든 데이터의 교환의 기초이며, 클라이언트-서버 프로토콜이기도 하다.

참고 - https://developer.mozilla.org/ko/docs/Web/HTTP/Overview

 

그래서 요청시 headers를 선언해주어야 정보를 받아올 수 있다. 

 

이번에는 네이버 금융의 일자별 시세를 가져오는 함수를 만들고 적용해보자.

# 라이브러리 로드
import pandas as pd
import requests
from bs4 import BeautifulSoup as bs

def get_day_list(item_code, page_no):
    """일자별 시세를 페이지별로 수집
    1) URL 을 만들기
    2) requests 로 요청하기
    (BS은 사용하지 않고 데이터프레임으로 만들겠습니다.)
    3) pd.read_html(response.text) 로 데이터프레임 만들기
    4) 결측치 제거하기
    5) 데이터프레임 반환하기
    """ 
    # 1) URL 을 만들기
    url = f"https://finance.naver.com/item/sise_day.naver"
    url = f"{url}?code={item_code}&page={page_no}"
    # 2) requests 로 요청하기
    headers = {"user-agent": "Mozilla/5.0"}
    response = requests.get(url, headers=headers)
    # 3) pd.read_html(response.text) 로 데이터프레임 만들기
    table = pd.read_html(response.text)[0]
    # 4) 결측치 제거하기
    table = table.dropna()
    # 5) 데이터프레임 반환하기
    return table
    
# 함수가 잘 만들어졌는지 확인하기
page_no = 12
get_day_list(item_code, page_no)

 

보아하니, 날짜를 지정해서 최근~지정날짜까지 불러오면 잘 활용할 것 같아 보인다. 

위에서 만든 함수를 활용해서, 원하는 날짜 (예, 2022.07.01)까지 불러와보자

# 함수를 만들어 보는 목적은 지금까지 했던 내용을 하나로 정리해 보기 위함입니다.
def get_item_list(item_code, item_name):
    """ 일별 시세를 수집하는 함수
    반복문을 작성
    조건문을 사용해서 마지막 행의 날짜가 특정 날짜 이전이면 멈추기(break)
    페이지번호를 하나씩 증가, 데이터프레임을 하나로 만들고,
    종목명, 종목코드를 파생변수로 생성하기, 컬럼명 순서 변경하기
    중복데이터 제거하기, 최근 날짜 구해서 파일 저장하기
    저장한 파일 읽어와서 해당 데이터프레임 반환하기
    """
    page_no = 1
    item_list = []
    while True:
        # 한페이지를 수집하는 함수 
        df_day = get_day_list(item_code, page_no)
        item_list.append(df_day)
        # 마지막 행의 날짜 구하기 .iloc[행, 열]
        first_day = df_day.iloc[-1, 0]
        # 날짜가 2022년 7월 1일 이전이라면 멈추기
        print(page_no, first_day)
        if first_day <= "2022.07.01":
            break
        time.sleep(0.01)
        page_no = page_no + 1

    # 수집된 데이터 병합하기, 인덱싱 새로해주기
    df = pd.concat(item_list).reset_index(drop=True)
    # 2022.07.01까지만 불러오기
    df = df.iloc[:df[df['날짜'] == '2022.07.01'].index[0] + 1]
    # 종목코드, 종목명 파생변수 만들기
    df["종목코드"] = item_code
    df["종목명"] = item_name
    # 컬럼 순서 변경하기
    cols = ['종목코드', '종목명', '날짜', '종가', '전일비', '시가', '고가', '저가', '거래량']
    df_tmp = df[cols]
    # 중복 데이터 제거하기
    df_tmp = df_tmp.drop_duplicates()
    # 최근 날짜 추출하기
    date = df_tmp.iloc[0]["날짜"]
    file_name = f"{item_name}-{item_code}-{date}.csv"
    # 파일로 저장하기
    # 저장한 파일 읽어오기
    df_tmp.to_csv(file_name, index=False)
    df_new = pd.read_csv(file_name)
    # 읽어온 데이터프레임 반환하기
    return df_new

 

WIL 회고

데이터 수집에 대해 많이 배웠다. 박조은 강사님과 처음 배우고, 그 날엔 DataFrame이 뭔지도 몰랐는데, 이젠 대략 감이 잡혔다. 사실 모르는 게 많아 매일이 버겁지만, 모르기 때문에 하루하루를 끝내다보면 아주 성취감이 크다. 하루하루 성장하는 기분이 좋다. 아직 모르는 건 이제 배워가면 되니까~! 멘탈관리가 중요하다고 했다. 열심히 복습하고 코딩하면서 부족한 틈을 채워가다 보면 나도 대통령 연설문 수집하는 거 티비보면서 30분만에 할 수 있겠지!🥹 지금 순간에 최선을 다하기! 😊