220928 0202 실습파일 - 반복문으로 데이터 수집 / 0203 실습파일 - requests, BeautifulSoup, 판다스코드로 데이터 수집, 함수 만들기 / 0204 실습파일 - ETF데이터 JSON으로 수집(tqdm, trange, import time, concat, import requests, GET, POST, HTTP상태코드, BeautifulSoup, 파싱, response문서, html.a, html.find_all, html.select, 일별 시세 수집, user_agent, 파생변수, get_day_list 함수, ETF, JSON, XML 등)
멋쟁이 사자처럼 AI스쿨 7기, 박조은 강사님 강의
✅ 0202 실습 파일
반복문을 사용해 10페이지까지 수집
tqdm, trange
- 프로세스 진행 상태를 보여주는 시스템
- range는 파이썬 표준 라이브러리에서 제공하는 기능, tqdm에서 제공하는 trange. 원래는 별도 설치해야 하지만 코랩에서는 설치되어 있어서 import 하면 된다.
- tqdm 공식 문서 https://github.com/tqdm/tqdm
- import time
import time
from tqdm import trange
page_no = 1
news_list = []
for page_no in trange(1,11):
temp = get_one_page_news(item_code, page_no)
news_list.append(temp)
time.sleep(0.01)
concat
- 하나의 데이터프레임으로 만들기
- 데이터는 news_list라는 변수에 들어있음
- df_news = pd.concat(news_list)
df_news.shape
저장하기
- filename = f"news{itemcode}{item_name}.csv"
- df_news.to_csv(file_name, index=False)
- pd.read_csv(file_name)
✅ 0203번 실습파일/ 06번 이론파일
네이버 금융 국내증시 https://finance.naver.com/sise/ 에서 url을 가져온다.
inspect-network : css는 스타일, doc 등 클릭해보면서 원하는 데이터를 찾을 수 있다.
라이브러리 로드
import pandas as pd
import numpy as np
import requests
URL
item_code =
item_name =
page_no = 1
url = f"https://finance.naver.com/item/sise_day.naver?code={item_code}&page={page_no}"
2-1. pd.read_html(url)
테이블을 찾을 수 없다고 오류가 난다.
read_html은 테이블 태그가 있어야 불러올 수 있다.
url 들어가서 inspect 해보면 테이블 태그가 있는데도 불러올 수 없다. 이유는 접근 권한을 막아놔서
requests
- import requests : 06번 파일 12쪽
- HTTP for Humans
- 소스코드만 보여줌
- GET, HEAD, POST 등과 같은 HTTP 메소드 자료 : 06번 파일 13쪽
- GET : 쿼리스트링에 담아 보내는 방식/ Payload가면 쿼리스트링
- POST : 폼 전송. 검색어 입력해서 보내는 방식/ Payload가면 Form Data
- HTTP 상태 코드 : 200 OK(성공) / 4xx(클라이언트 오류)
- response = requests.get(url, headers = {"user-agenet" : "Mozilla/5.0"})
- requests.get( , 헤더스 설정)
- response.text : 방문하시려는 페이지의 주소가 잘못 입력되었거나 페이지의 주소가 변경 혹은 삭제되어 요청하신 페이지를 찾을 수 없습니다.
- 위 headers 설정하고 나면 실행되면서 전체 소스 코드를 다 가져온다. 가격, 날짜 등등
- inspect-header -> user-agent
- __requests.get(url, headers = {"key" : "value"})
BeautifulSoup
https://www.crummy.com/software/BeautifulSoup/bs4/doc/
HTML 정보를 파싱해서 원하는 정보를 찾아온다.
BeautifulSoup(html_doc)해주면 구조화되어 있지 않던 response 출력 결과 문서를 보기 좋게 정렬해준다.
함수 생성에 beautifulsoup을 사용 안하는 이유는? pd.readhtml에 테이블 태그 기능이 내장되어 있기 때문
- from bs4 import BeautifulSoup as bs
- bs(response.text)를 html에 넣어서 불러서 사용하자. : html = bs(response.text)
- 공식 사이트의 매서드를 보자 : html.title/ .table / pd.DataFrame(html.table)
- html.a : 링크 태그 하나만 가져온다.
- html.find_all("a")[:3] : html.find("a")은 하나만 가져오고, html.find_all("a")은 링크태그 다 가져온다.
- 슬라이싱 넣어서 앞에서 3개만 가져올 수 있다.
- find_all과 select의 차이
- find_all은 attribute 지정을 해야 한다.
- select는 콕 집어서 필요한 링크만 가져와달라고 지정할 수 있다.
- find는 soup.find('div').find('p')
- select는 soup.select_one('div > p')
- inspect->copy->copy selector
- select 지정 순서
- html.select("body > table") : body에서 table을 찾는다. 구조 지정. hierarchy
- "table > tr > td"
- body > table.type2
- html.select("table.type2")
- html.select("body > table.type2") 해도 된다.
- 일별 시세를 셀렉트해서 가져오는 것
판다스 코드로 데이터 수집하기
- pd.read_html
- 네이버 일별 시세는 cp949 인코딩으로 불러 올 수 있다. 기본 인코딩 설정은 utf-8
- table = pd.read_html(response.text)
- 출력해보면 NaN 결측치 값과, 맨 밑의 페이징 부분도 같이 출력되어서 제거가 필요하다.
- 페이징 테이블 제거
- table[0]와 table[1]을 확인해보면 tabel[0]에 필요한 데이터가 있다.
- temp = table[0]
- table[1] 은 페이징 테이블
- .dropna()
- table[0] 했을 때 나오는 NaN을 dropna를 통해 결측치가 들어있는 row를 제거한다.
- temp.dropna()
일자별 시세를 페이지별로 수집하는 함수 만들기
뉴스 기사 수집과 일별 시세 수집에서의 차이? - 테이블 접근 권한 때문에 user_agent를 썼다.
def get_day_list(item_code, page_no):
- url을 만든다.
url = f"https://finance.naver.com/item/sise_day.naver?code={item_code}&page={page_no}" - requests를 통해 html 문서를 받아온다.
response = request.get(url, headers = {"user-agent" : "Mozilla/5.0"}) - read_html을 통해 table 태그를 읽어온다.
table = pd.read_html(response.text) - 결측행을 제거
temp = table[0]
temp.dropna() - 데이터 프레임을 반환
return temp
-> 조은님 코드 : 한줄로 해주는 경우
df_table = pd.read_html(response.text)[0].dropna()
return df_table
반복문을 통한 전체 일자 데이터 수집하기
쏘카도 데이터가 적어서 실습했다. item_code = "403550" , item_name = "쏘카"
for문은 갯수가 정해져 있을 때.(1~10페이지까지 수집하겠다.)
마지막 페이지를 모를 경우 while문
import time
item_code = "373220"
item_code = "LG에너지솔루션"
page_no = 1
# 데이터를 저장할 빈 변수 선언
item_list=[]
curr_day = ""
# curr_day = None해도됨
# last_day랑 비교할 초기값을 아무거나 설정해주어야 함.
while True:
# 한 페이지의 일별 시세 수집
df_item = get_day_list(item_code, page_no)
# 해당 데이터의 마지막 날짜를 가져옴
last_day = df_item.iloc[-1]["날짜"]
print(page_no, curr_day, last_day)
# 해당 데이터의 마지막 날짜와 이전 데이터의 마지막 날짜 비교
# 맨 첫 데이터에는 이전 데이터 날짜가 없기 때문에 반복문 밖에서 초기화를 해주었음.
# 이전 데이터의 마지막 날짜와 현재 데이터의 마지막 날짜가 같다면 반복문을 빠져나감.
if last_day == curr_day:
break
# 현재 데이터의 날짜를 다음 턴에서 비교할 수 있게 변수에 값을 넣어줌.
curr_day = last_day
# 수집한 일별 시세를 리스트에 추가
item_list.append(df_item)
# 다음 페이지를 수집하기 위해 페이지 번호 증가
page_no = page_no + 1
time.sleep(0.01)
concat
df_day = pd.concat(item_list)
파생변수 만들기
df_day["종목코드"]=item_code
df_day["종목명"]=item_name
컬럼 순서 변경하기
cols = ['종목코드', '종목명', '날짜', '종가', '전일비', '시가', '고가', '저가', '거래량']
df_day = df_day[cols]
df_day.head(2)
중복데이터 제거
df_day = df_day.drop_duplicates()
파일명 만들고, 저장
- 날짜 column의 첫 row 값 확인
date = df_day.iloc[0]["날짜"]
date - 종목명, 종목코드, 날짜를 이름으로 하는 csv 파일명 만들기
filename = f"{item_name}{itemcode}{date}.csv"
file_name - 파일로 저장
df_day.to_csv(file_name, index=False)
제대로 저장되었는지 read로 확인
pd.read_csv(file_name)
한 개의 함수
def get_day_list(item_code, page_no):
page_no = 1
item_list = []
prev_day = None
while True:
df_item = get_day_list(item_code, page_no)
last_day = df_item.iloc[-1]["날짜"]
print(page_no, prev_day, last_day)
if last_day == prev_day:
break
prev_day = last_day
item_list.append(df_item)
page_no = page_no + 1
time.sleep(0.01)
df_day = pd.concat(item_list)
df_day["종목코드"] = item_code
df_day["종목명"] = item_name
cols = ['종목코드', '종목명', '날짜', '종가', '전일비', '시가', '고가', '저가', '거래량']
df_day = df_day[cols]
date = df_day.iloc[0]["날짜"]
file_name = f"{item_name}_{item_code}_{date}.csv"
df_day.to_csv(file_name, index=False)
return file_name
✅ 0204 실습 파일, 06번 파일 61쪽
ETF 네이버 금융 사이트의 데이터 수집 https://finance.naver.com/sise/etf.nhn
ETF
1-1. 라이브러리
판다스, 넘파이, requests 불러온다.
1-2. url
url = "https://finance.naver.com/sise/etf.nhn"
1-3. pd. read_html(url)
JSON
지금까지 한 것은 웹스크래핑 방식으로 한 것이고, 지금은 API를 통해 가져오는 것을 실습
키워드 : 키-값 쌍 / XML
- requests
url = "https://finance.naver.com/api/sise/etfItemList.nhn?etfType=0&targetColumn=market_sum&sortOrder=desc"
response = requests.get(url) - pd.read_html(response.text)
- requests의 응답을 json 타입으로 받기
etf_json = response.json() - result > etfItemList
etfItemList = etf_json["result"]["etfItemList"]
print(len(etfItemList))
etfItemList[-1]
여기 len() 해주는 거랑 이해 안된다 - 데이터프레임 형태로 만들기
df = pd.DataFrame(etfItemList) - 파생변수 만들기
df["브랜드"] = df["itemname"].str.split(expand = True)[0]
- expand=True 리스트 형식이 아니라, 데이터프레임 형태로 반환해달라는 의미
- 그냥 split()만 하면 요소가 리스트 형식이 된다.
저장
- 파일명 만들기
from datetime import datetime
today = datetime.today().strftime("%Y-%m-%d")
file_name = f"dtf-{today}_raw.csv
- 저장하고 불러오기
df.to_csv(file_name, index=False)
pd.read_csv(file_name, dtype={"itemcode" : np.object})
- itemcode 숫자 앞의 0이 지워져서 출력됨. dtype으로 지정해주면 문자형태로 읽어옴.
퀴즈
10/12문제
1. 틀린 것
- 3번문제:이라 표현된 태그 안의 텍스트를 수집하고자 했을 때, HTMl 태그를 찾는 적절한 BeautifulSoup 코드는? html.select("td.title")
html.select("td > title")했는데.
CSS 셀렉터 ID 값은 앞에 #이 붙고, 클래스 값은 앞에 . 이 붙는다고 한다. - 9번 문제 : #content > div > div.view-content > div > table > tbody > tr:nth-child(1) > td.data-title.aLeft > a 셀렉터 값에서 a태그만 찾고자 할 때.
- html.select("#content > div > div.view-content > div > table > tbody > tr > td > a")
- html.table.select("a")
- html.select("table > tbody > tr > td > a")
- html.select("a")는 아니다.
- 맞았는데 애매한 것
- 4번 문제 : HTML태그에서 하이퍼 링크를 의미하는 것 __
- 7번 문제 : 인덱스 값을 다시 만들고 기존 인덱스를 제거하는 코드 : df.reset_index(drop=True)
해결 과제
- item_code = df_krx.loc[df_krx["Name"]=="넷마블", "Symbol"].values[0] 이 코드에서 .values[0]가 의미하는 것 까먹었다. 다시 찾아보기
- iloc와 loc 다시 구분 찾아보기
- df.reset_index(drop=True) 를 어디서 썼는지 실습 파일 찾아보기
댓글