다음은 파이선에서 주식 데이터를 웹 페이지에서 실제로 파이썬으로 가져오는 작업이다. 이전에 한번 끝까지 실행하는 부분은 파이썬으로 데이터를 가져오는 부분과 동시에 csv 파일로 저장하였는데 이 부분을 분리하여 작업하였다.
1. 기존 코드 분석
기존 코드를 보면 맨 처음엔 웹 주소, 다음에는 for문에서 네이버 증권 종목들을 1페이지에서 45페이지까지 반복하여 데이터를 df에 저장하고 불필요한 데이터를 제거한다. f_name부터는 파이썬의 데이터를 csv에 저장하는 부분이다.
사실 나는 애초에도 for문 부분과 csv에 저장하는 이 두 부분을 따로 분리하여 작업하고 싶었지만 반복문에 그냥 적용하게 되면 df에 덮어쓰기로 진행되어 맨 마지막 반복문의 결과만 df에 나타났다.
데이터를 파이썬으로 불러오는 부분과 csv에 저장하는 부분을 따로 분리하고 싶었던 이유는 2가지 작업이 동시에 진행되면 둘 중 하나의 문제가 생겼지만 진행할 수 있기 때문이다.
하나의 작업 단위로 분리되어 있는 편이 향후에 문제가 발생했을 경우에 그 부분만 수정하면 되기 때문에 분리하여 작업하는 것이 좋다.
하지만 현재 코드의 장점은 파이썬은 메모리에 올려두기 때문에 df라는 같은 이름으로 1페이지씩 가져와서 csv에 저장한 뒤 다시 df에 2페이지의 정보만, 그 다음 반복에서는 3페이지의 정보만 들고있기 때문에 한페이지씩의 정보만큼의 메모리만 쓴다.
메모리가 심하게 부족한 성능의 PC라면 괜찮은 방법일 수 있지만 45페이지까지 모든 데이터를 다 가져와도 큰 무리가 없다고 판단해서 코드를 분리해서 작업하도록 변경하겠다.
이 부분을 아래에서 해결해 보자.
p_url = 'https://finance.naver.com/sise/sise_market_sum.naver?sosok=0&page='
#############################################
# (1) 1페이지부터 마지막 페이지까지 필요 테이블 불러오기
for idx in range(1, 46) : # 마지막 페이지까지 반복, mk_lspg_num 해당 시장 종목 페이지 마지막 번호
browser.get(p_url + str(idx)) # http 주소는 문자이기 때문에 str을 이용하여 숫자를 문자로 변경해줘야함
# df = pd.read_html(browser.page_source)
df = pd.read_html(browser.page_source)[1] # 아래에서 각 테이블 정보를 확인해서 필요한 정보가 들어있는 [1] 테이블의 정보를 가져옴
#############################################
# (2) 불필요한 데이터 정제
df.dropna(axis = 'index', how = 'all', inplace = True)
df.dropna(axis = 'columns', how = 'all', inplace = True) # columns는 컬럼 기준으로 적용
f_name = '/Users/hd/Documents/python/stock_pgm/01.raw_data/kospi.csv'
# 파일이 있다면 헤더 제외
if os.path.exists(f_name) :
print("파일이 존재합니다.")
#info_dt_table.to_csv(f_name, encoding = 'utf-8-sig', index = False, mode = 'w', header = True)
# 한글 자료로 인해 인코딩이 필요, encoding = 'utf-8-sig'
# index = False는 맨 왼쪽의 1,2,3,4.. 제외
# mode = 'a'는 자료를 아래로 append시킴
# header = False 헤더 제외
# 파일이 없는 경우 헤더 포함, 첫 파일 생성 시
else :
info_dt_table.to_csv(f_name, encoding = 'utf-8-sig', index = False, header = True)
2. 반복문 데이터 누적하여 생성하기
먼저 csv 파일을 저장하는 f_name부터는 삭제하고 데이터를 파이썬으로 가져오는 부분만 해결하자. 여기서 문제점은 반복 실행 시 해당 데이터가 덮어쓰기 된다는 것이다. 그러면 이 부분을 누적하여 처리할 수 있지 않을까라는 생각에 많은 자료를 찾아봤다.
꽤 오랜시간에 걸쳐 여러자료를 찾아보고 짜집기 해봐서 최종적으로 수정한 코드이다. 실제로 잘 실행되고 데이터가 잘 들어가 있는지까지 확인했다.
#########################################################################
# 함수 정의
# 04) 데이터 가져오기
#########################################################################
p_url = 'https://finance.naver.com/sise/sise_market_sum.naver?sosok=0&page='
# 반복문을 실행하면서 데이터를 저장할 데이터프레임
# 아래 반복문을 실행하면서 데이터를 저장할 빈 데이터프레임을 생성
info_dt_table = pd.DataFrame()
def data_info_func(mk_url) : # 시장별 페이지 마지막 번호, 시장별 주소
global info_dt_table # 함수 정의 외부에 있는 변수(테이블(데이터프레임))을 가져오는 것이기 때문에 전역변수로 지정해줘야 함
df_list = [] # 데이터프레임을 저장하기 위한 리스트,왜 필요한지 아래에서 보면서 확인해보자
#############################################
# (1) 1페이지부터 마지막 페이지까지 필요 테이블 불러오기
for idx in range(1, 46) : # 마지막 페이지까지 반복, mk_lspg_num 해당 시장 종목 페이지 마지막 번호
browser.get(mk_url + str(idx)) # http 주소는 문자이기 때문에 str을 이용하여 숫자를 문자로 변경해줘야함
df = pd.read_html(browser.page_source)[1] # 아래에서 각 테이블 정보를 확인해서 필요한 정보가 들어있는 [1] 테이블의 정보를 가져옴
df_list.append(df) # 리스트에 데이터프레임 추가
#############################################
# (2) 불필요한 데이터 정제
df.dropna(axis = 'index', how = 'all', inplace = True)
df.dropna(axis = 'columns', how = 'all', inplace = True) # columns는 컬럼 기준으로 적용
info_dt_table = pd.concat(df_list, ignore_index=True)
print(f'{idx} 페이지 완료') # 몇번째 인덱스(페이지)가 실행됐는지 확인
return info_dt_table
먼저 주식 종목이 있는 페이지를 한페이지씩 바꿔주기 위해 p_url 주소를 입력해준다. 다음은 페이지가 변경될 때 1페이지, 2페이지, 3페이지, ... 45페이지까지 정보를 데이터프레임에 저장하기 위해 빈 프레임을 하나 만들어준다.
빈 프레임을 만들어주는 이유는 아래에서 세번째 줄에 info_dt_table = pd.concat(df_list, ignore_index=True) 라는 코드가 있는데 info_dt_table에 뒤에 있는 정보를 입력하라는 뜻이다. 그러려면 info_dt_table라는 데이터프레임이 있어야 한다는 것이다.
쉽게 예를 들자면 담는 통이 있어야 그 안에 뭘 담을텐데 담는 통이 없기 때문에 미리 빈통을 만들어두는 것이다.
다음으로 def 부분을 보자면 이번에는 mk_url이라는 매개변수가 있는 함수를 만들었다. 코스피 종목만을 생성하면 크게 상관없지만 코스닥 정보까지 가져오기 위해 url 주소를 변경하기 위해 가져온 것이다. 또 추가로 해외종목을 가져오려고 한다면 그때도 사용될 수 있을 것 같다.
함수정의 첫 문장을 보면 global info_dt_table가 있는 것을 볼 수 있는데 이건 외부변수를 함수 내에서 사용하기 위해서다. 함수정의에서 사용될 수 있는 변수는 외부변수와 내부변수로 나눌 수 있는데 이는 아래 링크에서 확인하자.
그 다음은 df_list라는 이름으로 빈 리스트를 생성해준다. 리스트 형태의 자료는 어떤 형태의 자료든 담을 수 있다는 장점이 있다.
함수정의 안에 보면 df_list.append(df) 명령문이 중간에 추가된 것을 볼 수 있는데 df에서 생성한 종목 정보를 페이지를 누적하여 리스트에 저장한다.
다음으로 데이터 정제하는 부분은 이전과 동일한 내용이므로 생략하고 info_dt_table = pd.concat(df_list, ignore_index=True)은 함수 밖에서 정의한 info_dt_table이라는 이름의 데이터프레임에 df_list에 있는 정보를 결합한다.
여기서 pd.concat은 데이터를 결합하는데 사용되는 명령문이다. 자세한 내용은 아래 링크를 참고하길 바란다.
다음 print문은 45페이지까지 반복하기 때문에 어디까지 실행되고 있는지 확인하기 위해 한페이지씩 실행될때마다 몇페이지가 실행되고 있는지 체크하기 위한 문장이다.
문제없이 잘 실행되고 있을때는 확인할 필요가 없지만 중간에 웹 페이지가 끊기거나 오류가 발생되어 중단될 경우 어디까지 실행되었는지 확인할 수 있는 부분이다.
마지막으로 return info_dt_table이라는 명령문이 중요하고 또 처음에는 아무리 여러번 봐도 잘 이해가되지 않는 문장일 수 있다.
return문은 함수안에서 실행된 결과를 함수 외부에서 사용할 수 있는 명령문이다. 이렇게 얘기하면 앞에서 만들었던 체크박스 해제, 체크박스 선택 및 적용을 위한 함수에서는 왜 return문이 없을까?
여기서 생각해보면 체크박스 해제나 선택은 어떤 동작을 실행하는 것이지 그 안에서 새로운 데이터나 값들이 생성되는 것이 아니다. 만약 생성되는 새로운 값이 있더라도 함수 그 안에서 생성하는 것이 제 역할을 다하는 것이라면 return문이 필요없다.
return문은 함수 안에서 생성된 어떤 데이터나 값들을 함수 외부에서 불러와서 사용하기 위한 명령문이라고 생각하면 된다.
우리는 info_dt_table이라는 데이터프레임에 있는 값을 함수 외부에서 가져와 csv라는 파일에 저장해야하기 때문에 return문으로 info_dt_table이라는 결과를 return문으로 정의한 것이다.
그 외 함수 내부에서 만들어지는 정보 중 필요한 다른 정보가 또 있다면 return 정보1, 정보2, ... 이렇게 입력해주면 함수 외부에서 해당 정보를 사용할 수 있다.
return문에 대해 좀 더 자세하게 알아보고 싶다면 아래 링크를 참고해보자.
여기까지 데이터를 가져오는 부분에 대해 함수정의하는 것에 대해 알아보았다. 이번 내용은 조금 어려울 수도 있지만 혼자서 내가 만든다면 이라는 생각으로 하나 실행해보고, 변경해보고 하면 조금 더 이해하기 쉬울 것이다.
또 내가 만든 코드가 무조건 옳은 것이 아니기 때문에 더 좋고 쉬운 방법으로도 만들 수 있을것이다. 다음 내용에서는 csv 파일로 저장하는 부분을 함수정의하는 방법에 대해 알아보도록 하겠다.
'방구석코딩 > 주식 크롤링 분석' 카테고리의 다른 글
[파이썬] 주식 크롤링 매크로화(3) - 선택항목 체크(입력) 함수적용 (0) | 2024.07.31 |
---|---|
[파이썬] 주식 크롤링 매크로화(2) - 체크박스 해제 함수정의(코드분석) (0) | 2024.07.25 |
[파이썬] 주식 크롤링 매크로화(1) - 함수정의 및 사전작업 (4) | 2024.07.23 |
[파이썬] 주식 데이터 크롤링(8) - 전체 코드 및 향후 해결사항 (0) | 2024.07.18 |
[파이썬] 주식 데이터 크롤링(7) - csv로 저장 (2) | 2024.07.16 |
댓글