Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
Tags
- urllib
- Unity
- IOS
- MS-SQL
- tensorflow
- 유니티
- 리눅스
- python
- 날짜
- ubuntu
- Excel
- PyQt
- GIT
- 함수
- sqlite
- 맛집
- node.js
- 다이어트
- pandas
- swift
- mssql
- MySQL
- flutter
- PER
- port
- PyQt5
- javascript
- ASP
- Linux
- 라즈베리파이
Archives
아미(아름다운미소)
재무재표개선 본문
import pandas as pd
import requests
from typing import Optional, Dict, Any
import warnings
warnings.filterwarnings('ignore')
class FinancialAnalyzer:
"""네이버 금융 재무제표 분석기"""
def __init__(self):
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
def get_financial_statement(self, ticker: str) -> Optional[pd.DataFrame]:
"""
네이버 금융에서 손익계산서 데이터를 가져오는 함수
Args:
ticker (str): 종목 코드 (예: '005930')
Returns:
pd.DataFrame or None: 재무제표 데이터
"""
url = f'https://finance.naver.com/item/main.naver?code={ticker}'
try:
# requests로 먼저 페이지 확인
response = requests.get(url, headers=self.headers, timeout=10)
if response.status_code != 200:
print(f"⚠️ 페이지 접근 실패: HTTP {response.status_code}")
return None
# 한글 인코딩 처리 개선
tables = pd.read_html(url, encoding='euc-kr', header=0)
except requests.exceptions.RequestException as e:
print(f"⚠️ 네트워크 오류: {e}")
return None
except Exception as e:
print(f"⚠️ 데이터 파싱 오류: {e}")
return None
# 손익계산서 테이블 찾기
for i, table in enumerate(tables):
if table.shape[1] >= 3 and len(table) > 5:
# 매출액이 포함된 테이블 찾기
first_col = table.iloc[:, 0].astype(str).str.strip()
if any('매출' in cell for cell in first_col):
print(f"✅ 재무제표 발견 (테이블 #{i+1})")
return table
print("⚠️ 손익계산서 테이블을 찾을 수 없습니다")
return None
def clean_financial_df(self, df: pd.DataFrame) -> pd.DataFrame:
"""
데이터프레임 전처리: 인덱싱, 숫자 변환, 결측치 처리 등
Args:
df (pd.DataFrame): 원본 재무제표 데이터
Returns:
pd.DataFrame: 정제된 데이터
"""
df_copy = df.copy()
# 첫 번째 컬럼을 인덱스로 설정
df_copy.set_index(df_copy.columns[0], inplace=True)
# 결측치 및 특수 문자 처리
df_copy = df_copy.replace(['-', '/', 'N/A', '', ' '], '0')
# 숫자 변환 함수
def convert_to_number(x):
if pd.isna(x) or x == '':
return 0
try:
# 문자열인 경우 콤마 제거 후 숫자 변환
if isinstance(x, str):
cleaned = x.replace(',', '').replace('(', '-').replace(')', '').strip()
return float(cleaned) if cleaned else 0
return float(x)
except (ValueError, TypeError):
return 0
# 모든 컬럼에 숫자 변환 적용
for col in df_copy.columns:
df_copy[col] = df_copy[col].apply(convert_to_number)
return df_copy
def find_row_name(self, df: pd.DataFrame, candidates: list) -> str:
"""
유연한 행 이름 매칭
Args:
df (pd.DataFrame): 데이터프레임
candidates (list): 후보 행 이름들
Returns:
str: 매칭된 행 이름
"""
index_str = df.index.astype(str).str.strip()
for candidate in candidates:
# 정확히 일치하는 경우
if candidate in index_str.values:
return candidate
# 부분 매칭 (포함 관계)
matches = index_str[index_str.str.contains(candidate, na=False)]
if len(matches) > 0:
return matches.iloc[0]
raise KeyError(f"다음 항목을 찾을 수 없습니다: {candidates}")
def calculate_growth_rate(self, current: float, previous: float) -> str:
"""
성장률 계산
Args:
current (float): 현재 값
previous (float): 이전 값
Returns:
str: 성장률 (%)
"""
try:
if previous == 0:
return "N/A (이전값 0)"
growth_rate = ((current - previous) / abs(previous)) * 100
return f"{growth_rate:.2f}%"
except (ZeroDivisionError, TypeError):
return "N/A"
def analyze_financials(self, df: pd.DataFrame) -> Dict[str, Any]:
"""
주요 지표 분석 및 해석 제공
Args:
df (pd.DataFrame): 재무제표 데이터
Returns:
dict: 분석 결과
"""
analysis = {}
try:
df_cleaned = self.clean_financial_df(df)
# 최신 두 기간 선택
if len(df_cleaned.columns) < 2:
raise ValueError("비교할 데이터가 충분하지 않습니다 (최소 2개 기간 필요)")
latest = df_cleaned.columns[-1]
prev = df_cleaned.columns[-2]
print(f"📊 비교 기간: {prev} vs {latest}")
# 주요 항목 찾기
try:
row_revenue = self.find_row_name(df_cleaned, ['매출액', '수익(매출액)', '총매출액'])
row_operating = self.find_row_name(df_cleaned, ['영업이익', '영업이익(손실)', '영업손익'])
row_net = self.find_row_name(df_cleaned, ['당기순이익', '당기순이익(손실)', '순이익', '당기순손익'])
except KeyError as e:
analysis['오류'] = str(e)
return analysis
# 데이터 추출 (백만원 단위)
revenue_current = df_cleaned.loc[row_revenue, latest]
revenue_previous = df_cleaned.loc[row_revenue, prev]
operating_current = df_cleaned.loc[row_operating, latest]
operating_previous = df_cleaned.loc[row_operating, prev]
net_current = df_cleaned.loc[row_net, latest]
net_previous = df_cleaned.loc[row_net, prev]
# 기본 정보
analysis['📈 매출액 현재'] = f"{revenue_current:,.0f}백만원"
analysis['📈 매출액 이전'] = f"{revenue_previous:,.0f}백만원"
analysis['📈 매출액 변화'] = '증가' if revenue_current > revenue_previous else '감소'
analysis['📈 매출액 증감률'] = self.calculate_growth_rate(revenue_current, revenue_previous)
analysis['💼 영업이익 현재'] = f"{operating_current:,.0f}백만원"
analysis['💼 영업이익 이전'] = f"{operating_previous:,.0f}백만원"
analysis['💼 영업이익 변화'] = '증가' if operating_current > operating_previous else '감소'
analysis['💼 영업이익 증감률'] = self.calculate_growth_rate(operating_current, operating_previous)
analysis['💰 순이익 현재'] = f"{net_current:,.0f}백만원"
analysis['💰 순이익 이전'] = f"{net_previous:,.0f}백만원"
analysis['💰 순이익 변화'] = '증가' if net_current > net_previous else '감소'
analysis['💰 순이익 증감률'] = self.calculate_growth_rate(net_current, net_previous)
# 수익성 지표 계산
if revenue_current != 0:
operating_margin = (operating_current / revenue_current) * 100
net_margin = (net_current / revenue_current) * 100
analysis['📊 영업이익률'] = f"{operating_margin:.2f}%"
analysis['📊 순이익률'] = f"{net_margin:.2f}%"
# 영업이익률 평가
if operating_margin >= 15:
analysis['⭐ 영업이익률 평가'] = '매우 우수'
elif operating_margin >= 10:
analysis['⭐ 영업이익률 평가'] = '우수'
elif operating_margin >= 5:
analysis['⭐ 영업이익률 평가'] = '보통'
elif operating_margin >= 0:
analysis['⭐ 영업이익률 평가'] = '낮음'
else:
analysis['⭐ 영업이익률 평가'] = '적자'
# 종합 평가
positive_signals = 0
if revenue_current > revenue_previous:
positive_signals += 1
if operating_current > operating_previous:
positive_signals += 1
if net_current > net_previous:
positive_signals += 1
if positive_signals == 3:
analysis['🎯 종합평가'] = '매우 긍정적 (3/3 지표 개선)'
elif positive_signals == 2:
analysis['🎯 종합평가'] = '긍정적 (2/3 지표 개선)'
elif positive_signals == 1:
analysis['🎯 종합평가'] = '혼조 (1/3 지표 개선)'
else:
analysis['🎯 종합평가'] = '부정적 (모든 지표 악화)'
# 데이터 유형 추정
column_names = ' '.join(df_cleaned.columns.astype(str))
if any(keyword in column_names for keyword in ['년', 'Year', '연간']):
analysis['📅 데이터 유형'] = '연간 실적'
else:
analysis['📅 데이터 유형'] = '분기별 실적'
except Exception as e:
analysis['❌ 분석 오류'] = str(e)
return analysis
def print_analysis(self, ticker: str):
"""
재무 분석 결과 출력
Args:
ticker (str): 종목 코드
"""
print(f"\n{'='*50}")
print(f"📋 [{ticker}] 재무제표 분석 보고서")
print(f"{'='*50}")
df = self.get_financial_statement(ticker)
if df is None:
print("❌ 재무제표 데이터를 가져올 수 없습니다")
print(" - 종목 코드가 올바른지 확인해주세요")
print(" - 네트워크 연결 상태를 확인해주세요")
return
print(f"✅ 데이터 로드 완료 (행: {len(df)}, 열: {len(df.columns)})")
analysis_result = self.analyze_financials(df)
print(f"\n📊 분석 결과:")
print("-" * 40)
for key, value in analysis_result.items():
print(f"{key}: {value}")
print(f"\n{'='*50}")
# 사용 예시
def main():
analyzer = FinancialAnalyzer()
# 여러 종목 분석 예시
tickers = [
"005930", # 삼성전자
"000660", # SK하이닉스
"035420", # NAVER
"005380", # 현대차
]
for ticker in tickers:
try:
analyzer.print_analysis(ticker)
print("\n" + "="*60 + "\n")
except KeyboardInterrupt:
print("\n사용자에 의해 중단되었습니다.")
break
except Exception as e:
print(f"❌ {ticker} 분석 중 오류: {e}")
continue
if __name__ == "__main__":
# 단일 종목 분석
analyzer = FinancialAnalyzer()
analyzer.print_analysis("005930") # 삼성전자
# 또는 여러 종목 분석
# main()
'랭귀지 > python' 카테고리의 다른 글
재무재표심화 (1) | 2025.08.06 |
---|---|
재무제표 분석 (0) | 2025.08.06 |
안전하게 삭제하는 코드 예시 (0) | 2025.06.26 |
git merge (0) | 2025.06.26 |
파일에 최대 5줄까지만 유지하면서 새로운 내용을 추가 (0) | 2025.02.19 |
Comments