티스토리 뷰

728x90
반응형

 

 

 

이번에는 네이버 금융 사이트에 있는 국내 증시 시가총액 순위 1위부터 200위까지 가져오는 실습을 해볼 생각입니다.

가져오는 것으로 끝나지 않고 csv 모듈을 사용해서 csv 엑셀 파일로 저장해보도록 하겠습니다

 

우선 URL 링크는 다음과 같습니다. https://finance.naver.com/sise/sise_market_sum.naver?&page=1 

 

시가총액 : 네이버 금융

관심종목의 실시간 주가를 가장 빠르게 확인하는 곳

finance.naver.com

첫 페이지를 보시면 위와 같이 1위부터 아래로 쭉 50위까지 나열되어 있습니다. 그리고 다음 페이지인 2페이지로 넘어가면 다시 50위부터 100위까지 있습니다. 그 뒤로 여러 페이지가 존재하지만 이번에는 200위까지만 가져오는 코드를 작성해보겠습니다.

 

우선 액셀 파일에 최상단에는 각 컬럼의 제목이 들어가야 하므로 위 테이블 최상단에 있는 thread 부분, 즉 N, 종목명, 현재가, 전일비, 등략률, 액면가 ... 등등을 가져와야합니다. HTML 코드로 보겠습니다.

thead 태그 안에 tr 태그 내부에 th 태그들이 모두 제목에 해당합니다. 이 부분만 가져오는 코드는 아래와 같습니다.

import requests
from bs4 import BeautifulSoup

url = 'https://finance.naver.com/sise/sise_market_sum.naver?&page='

# table
res = requests.get(url)
soup = BeautifulSoup(res.text, 'lxml')
table = soup.find('table', attrs={'class':'type_2'})

# title row 가져오기
title_rows = table.find('thead').find_all('th')
titles = [title_row.get_text() for title_row in title_rows]
print(titles)
# ['N', '종목명', '현재가', '전일비', '등락률', '액면가', '시가총액', '상장주식수', '외국인비율', '거래량', 'PER', 'ROE', '토론실']

각 제목들은 리스트 형태로 가져왔습니다. 그 이유는 나중에 csv 파일에 쓸 때 리스트로 넣어주어야해서 그렇습니다.

 

이제 각 항목들을 가져와야합니다. 우선 HTML 코드부터 분석해보겠습니다.

빨간색 상자로 표시된 부분이 각 항목에 대한 HTML 태그들입니다.

다만 드문드문 정상 태그들 가운데에 공백에 해당하는 tr 태그들이 보입니다.

저는 이 부분을 위 이미지에서 파란색 상자로 표시했습니다.

이런 항목을 제외한 정상적인 내용만 포함하는 tr 태그들을 가져와야합니다.

이를 코드로 작성해보겠습니다.

 

import requests
from bs4 import BeautifulSoup

url = 'https://finance.naver.com/sise/sise_market_sum.naver?&page='

# rows 가져오기
for page in range(1, 5): # 1~4 페이지까지
	res = requests.get(url + str(page))
	res.raise_for_status() # 페이지 못 가져올 경우 프로그램 종료

	soup = BeautifulSoup(res.text, 'lxml')
	data_rows = soup.find('table', attrs={'class':'type_2'}).find('tbody').find_all('tr')
	for row in data_rows:
		columns = row.find_all('td')
		if len(columns) <= 1: # 의미 없는 공백 데이터는 건너뛰기
			continue
		data = [column,get_text().strip() for column in columns]
		print(data)

그리고 위 코드의 실행 결과는 아래와 같습니다.

['1', '삼성전자', '72,300', '0', '0.00%', '100', '4,316,153', '5,969,783', '51.39', '16,621,933', '14.01', '9.99', '']
['2', 'SK하이닉스', '116,000', '500', '+0.43%', '5,000', '844,483', '728,002', '48.45', '4,381,068', '10.49', '9.53', '']
['3', 'NAVER', '386,500', '3,000', '-0.77%', '100', '634,878', '164,263', '56.50', '455,336', '3.83', '15.22', '']
['4', '삼성바이오로직스', '884,000', '14,000', '+1.61%', '2,500', '584,899', '66,165', '10.70', '113,106', '142.49', 'N/A', '']
... 중략 ...
['198', '지누스', '79,800', '1,800', '-2.21%', '500', '12,609', '15,800', '21.26', '62,574', '35.15', '11.61', '']
['199', 'KODEX 단기채권PLUS', '103,290', '5', '0.00%', '0', '12,508', '12,110', '0.00', '1,619,658', 'N/A', 'N/A', '']
['200', 'KODEX 200TR', '12,550', '95', '-0.75%', '0', '12,349', '98,400', '0.05', '97,284', 'N/A', 'N/A', '']

 

보아하니 각 배열에서 제일 마지막에는 무조건 ''와 같이 의미없는 문자열이 들어가는 것 같습니다.

그 이유는 아래 토론실에 해당하는 tr 태그의 내용이 어떤 문자열도 없는 이미지 뿐인 상황이기 때문입니다.

그래서 저 부분을 제거해주기 위해서 코드를 아래와 같이 수정해주었습니다.

import requests
from bs4 import BeautifulSoup

url = 'https://finance.naver.com/sise/sise_market_sum.naver?&page='

# rows 가져오기
for page in range(1, 5): # 1~4 페이지까지
	res = requests.get(url + str(page))
	res.raise_for_status() # 페이지 못 가져올 경우 프로그램 종료

	soup = BeautifulSoup(res.text, 'lxml')
	data_rows = soup.find('table', attrs={'class':'type_2'}).find('tbody').find_all('tr')
	for row in data_rows:
		columns = row.find_all('td')
		if len(columns) <= 1: # 의미 없는 공백 데이터는 건너뛰기
			continue
		data = [column,get_text().strip() for column in columns[:-1]]
		print(data)

달라진 점으로는 columns[:-1] 밖에 없습니다. 제일 마지막 원소는 제외하고 가져온다는 의미이죠.

 

이제 표의 최상단 제목과 각 항목들을 모두 가져왔으니, 이 목록들을 모두 CSV 파일로 저장만 하면 됩니다.

csv 파일로 저장하기 위해서는 csv 모듈을 import 해주어야합니다.

 

기본적으로 csv 모듈 사용법은 아래와 같습니다.

import csv

filename = '시가총액1-200.csv'
f = open(filename, 'w', encoding='utf-8-sig', newline='')
writer = csv.writer(f)

writer.writerow(['N', '종목명', '현재가', '전일비', '등락률', '액면가', '시가총액', '상장주식수', '외국인비율', '거래량', 'PER', 'ROE', '토론실'])
writer.writerow(['1', '삼성전자', '72,300', '0', '0.00%', '100', '4,316,153', '5,969,783', '51.39', '16,621,933', '14.01', '9.99'])
writer.writerow(['2', 'SK하이닉스', '116,000', '500', '+0.43%', '5,000', '844,483', '728,002', '48.45', '4,381,068', '10.49', '9.53'])

그냥 단순히 쓰기할 파일을 writer 함수의 인자로 넘겨주고,

writerow 함수에 list를 인자로 놓어서 각 row를 작성해주면 됩니다.

 

이를 이제 위에서 작성했던 코드와 합쳐보면 아래와 같습니다.

 

import csv
import requests
from bs4 import BeautifulSoup

url = 'https://finance.naver.com/sise/sise_market_sum.naver?&page='

filename = '시가총액1-200.csv'
f = open(filename, 'w', encoding='utf-8-sig', newline='')
writer = csv.writer(f)

# table
res = requests.get(url)
soup = BeautifulSoup(res.text, 'lxml')
table = soup.find('table', attrs={'class':'type_2'})

# title row 가져오기
title_rows = table.find('thead').find_all('th')
titles = [title_row.get_text() for title_row in title_rows]
# print(titles)
writer.writerow(titles)

# rows 가져오기
for page in range(1, 5):
	res = requests.get(url + str(page))
	res.raise_for_status()

	soup = BeautifulSoup(res.text, 'lxml')
	data_rows = soup.find('table', attrs={'class':'type_2'}).find('tbody').find_all('tr')
	for row in data_rows:
		columns = row.find_all('td')
		if len(columns) <= 1: # 의미 없는 데이터는 skip
			continue
		data = [column.get_text().strip() for column in columns[:-1]]
		# print(data)
		writer.writerow(data)

그러면 아래와 같이 확장자가 csv인 파일이 하나 생깁니다.

이를 단순히 메모장으로 열게 되면 다음과 같이 콤마(,)를 구분으로 한 문자열이 나옵니다.

하지만 이를 한셀이나 Microsoft Excel 과 같은 엑셀 뷰어 프로그램으로 열게 되면 아래와 같이 나옵니다.

이렇게 웹 스크래핑/크롤링을 한 데이터를 CSV 액셀파일로 저장하는 법에 대해서 알아보았습니다.

 

추가로 파일을 저장할 때 encoding 부분이 utf8 이 아닌 utf-8-sig 으로 한 이유는 엑셀파일에서 한글이 깨져서입니다.

혹시 한글이 깨지신다면 utf-8-sig 로 해보시길 바랍니다.

 

그리고 newline='' 으로 한 이유는 Python csv 모듈 문서에도 나와있다시피, newline=''을 지정하지 않으면, 따옴표 처리된 필드에 포함된 줄 넘김 문자가 올바르게 해석되지 않으며, 줄 끝 표시에 \r\n을 사용하는 플랫폼에서 쓸 때 여분의 \r이 추가됩니다. csv 모듈은 자체 (유니버셜) 줄 넘김 처리를 하므로, newline='' 을 지정하는 것이 항상 안전하다고 합니다. 여기서 말하는 "줄 끝 표시에 \r\n을 사용하는 플랫폼" 이란 일반적으로 Windows를 의미하게 됩니다.

 

Python 의 csv 모듈에 대한 자세한 사항은 아래 링크를 참고하세요.

https://docs.python.org/ko/3/library/csv.html

 

csv — CSV 파일 읽기와 쓰기 — Python 3.10.0 문서

소위 CSV (Comma Separated Values – 쉼표로 구분된 값) 형식은 스프레드시트와 데이터베이스에 대한 가장 일반적인 가져오기 및 내보내기 형식입니다. CSV 형식은 RFC 4180에서 표준화된 방식으로 형식을

docs.python.org

- 끝 -

728x90
반응형
댓글