야후! 금융 재무 정보 얻기.

GHTS 2022. 10. 8. 13:46 Posted by UnHa Kim

다음 동영상에 '야후! 금융'에서 재무 정보를 추출하는 방법이 잘 설명되어 있다.

https://youtu.be/fw4gK-leExw

 

동영상 내용이 파이썬 기준이지만, 내용만 이해하면 Go언어에서도 구현은 쉽다.

chromedp(https://github.com/chromedp/chromedp) 모듈을 사용하면 자바스크립트를 이용해서 동적으로 생성되는 '야후! 금융' 웹페이지도 문제없이 불러들일 수 있다.

이렇게 읽어온 HTML에서 JSON 데이터 부분만 추출해서 map형태로 저장한 후 적절히 활용하면 된다.

애플의 재무 데이터를 읽어들이는 Go언어 예제 코드는 다음과 같다.

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/chromedp/chromedp"
	"log"
	"regexp"
	"testing"
	"time"
)

// 참고자료 : https://youtu.be/fw4gK-leExw
// 애플 재무 정보 수집 예제
func main() {
	const url템플릿 = `https://finance.yahoo.com/quote/%v/financials`
	종목코드 := "AAPL" // 애플

	// create chrome instance
	ctx, cancel := chromedp.NewContext(context.Background())
	defer cancel()

	// create a timeout
	ctx, cancel = context.WithTimeout(ctx, 30*time.Second)
	defer cancel()

	var html string
	url := fmt.Sprintf(url템플릿, 종목코드)

	if err := chromedp.Run(ctx,
		chromedp.Navigate(url),
		chromedp.InnerHTML("body", &html, chromedp.ByQuery),
	); err != nil {
		log.Fatal(err)
	}

	// HTML에서 JSON 데이터를 맵 형태로 추출
	html = regexp.MustCompile(`(?s)\s--\sData\s--\s.+?</script>`).FindString(html)
	html = regexp.MustCompile(`(?s)root.App.main.+</script>`).FindString(html)
	html = html[16 : len(html)-21]

	var 맵 map[string]interface{}

	json.Unmarshal([]byte(html), &맵)

	손익계산서_연도 := f2맵_모음(맵, []string{"context", "dispatcher", "stores", "QuoteSummaryStore", "incomeStatementHistory", "incomeStatementHistory"})
	f맵_모음_출력("손익계산서_연도", 손익계산서_연도)

	손익계산서_분기 := f2맵_모음(맵, []string{"context", "dispatcher", "stores", "QuoteSummaryStore", "incomeStatementHistoryQuarterly", "incomeStatementHistory"})
	f맵_모음_출력("손익계산서_분기", 손익계산서_분기)

	재무상태표_연도 := f2맵_모음(맵, []string{"context", "dispatcher", "stores", "QuoteSummaryStore", "balanceSheetHistory", "balanceSheetStatements"})
	f맵_모음_출력("재무상태표_연도", 재무상태표_연도)

	재무상태표_분기 := f2맵_모음(맵, []string{"context", "dispatcher", "stores", "QuoteSummaryStore", "balanceSheetHistoryQuarterly", "balanceSheetStatements"})
	f맵_모음_출력("재무상태표_분기", 재무상태표_분기)

	현금흐름표_연도 := f2맵_모음(맵, []string{"context", "dispatcher", "stores", "QuoteSummaryStore", "cashflowStatementHistory", "cashflowStatements"})
	f맵_모음_출력("현금흐름표_연도", 현금흐름표_연도)

	현금흐름표_분기 := f2맵_모음(맵, []string{"context", "dispatcher", "stores", "QuoteSummaryStore", "cashflowStatementHistoryQuarterly", "cashflowStatements"})
	f맵_모음_출력("현금흐름표_분기", 현금흐름표_분기)
}

func f2맵_모음(맵 map[string]interface{}, 키_모음 []string) (맵_모음 []map[string]interface{}) {
	for _, 키 := range 키_모음 {
		if 맵2, ok := 맵[키].(map[string]interface{}); ok {
			맵 = 맵2
			continue
		} else if 값_모음, ok := 맵[키].([]interface{}); ok {
			맵_모음 = make([]map[string]interface{}, len(값_모음))

			for i, 값 := range 값_모음 {
				맵_모음[i] = 값.(map[string]interface{})
			}

			return 맵_모음
		} else {
			panic(fmt.Errorf("예상하지 못한 자료형 %T\n", 맵[키]))
		}
	}

	return
}

func f맵_모음_출력(제목 string, 맵_모음 []map[string]interface{}) {
	for i, 맵 := range 맵_모음 {
		for 키, 값 := range 맵 {
			fmt.Printf("%v %v %v %v\n", 제목, i, 키, 값)
		}
	}
}