ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 코스피200지수 배당수익률 구하기 : 파이썬 웹스크레핑
    금융퀀트/자산평가&프로그램매매 2023. 3. 2. 07:19
    반응형

    URL 접속 및 데이터 추출

    python을 이용한 정적 웹 페이지에서 정보 수집은 보통 requests 라이브러리를 사용해서 해당 url의 html 문서 데이터를 전부 가져오고 Beautifulsoup 라이브러리를 이용해서 requests로 가져온 데이터를 "html" 형식, "lxml" 형식 등으로 변환하는 것에서 시작한다. 라이브러리 설치는 에디터나 윈도우 cmd 창에서 아래와 같은 명령어를 치면 된다.

    pip install requests #requests 설치
    pip install beautifulsoup4 #Beautifulsoup 설치

    Beautifulsoup과 requests를 import 하고 특정 url의 html 문서를 lxml 형식으로 soup에 저장하는 함수를 아래와 같이 만들 수 있다.

    from bs4 import BeautifulSoup
    import requests
    
    # URL 접속해서 soup 에 저장
    def returnUrlXml(url):
        # requests 라이브러리 get 함수를 통해서 url 데이터 "GET"
        res = requests.get(url)
        # 수신 에러 체크
        res.raise_for_status()
        # Beautifulsoup 을 이용해서 url 데이터 텍스트를 "lxml" 형식으로 만들고
        # soup 에 저장
        soup = BeautifulSoup(res.text, "lxml")
        # soup 값 반환
        return soup

    코스피 200 구성종목 입수

    사이트 구조

    코스피 200 구성종목에 대한 정보는 url 주소 "https://finance.naver.com/sise/entryJongmok.naver?&page=1" 에서 제공한다. 

    그림1: 코스피200 편입종목 정보(https://finance.naver.com/sise/entryJongmok.naver?&page=1 2023-03-02 AM0603)

    사이트를 보면 한 페이지당 10 개의 종목 정보를 보여주고, 그런 페이지가 20 개로 구성되어 있는 것을 알 수 있다. 페이지 구분은 URL 맨 뒤에 넘버링으로 하고 있다. 따라서 URL의 넘버링을 바꿔가면서 정보를 수집한다는 콘셉트로 접근하면 된다.   

    데이터 추출

    각 페이지별로 그림1 에서 볼 수 있는 것처럼 브라우저의 "검사" 기능을 통해서 각 주식의 종목명과 주식코드의 위치를 찾을 수 있다. 정리하면 URL 끝의 넘버링을 바꿔가면서 페이지별로 종목코드와 종목명을 추출하면 코스피 200 구성종목 Data를 구축할 수 있게 되는 것이다. 그 과정은 아래와 같다.

    import pandas as pd
    from bs4 import BeautifulSoup
    import requests
    
    # 데이터프레임 설정
    df = pd.DataFrame(columns=['code', 'name', 'dvr', 'marketsum'])
    
    for page in range(1,21):
        # url 끝에 page 번호 1~ 20 까지 붙이기
        url = "https://finance.naver.com/sise/entryJongmok.naver?&page={}".format(page)
        # 위에서 만든 returnUrlXml 함수로 해당 URL 에서 정보 받아오기
        soup = returnUrlXml(url)
        codes = soup.find_all("td", attrs={"class":"ctg"})
        for linkname in codes:
            # a 태그의 link 정보 추출
            codelink = linkname.a['href']
            # 링크 정보가 "/item/main.naver?code=코드명" 으로 되어 있으므로
            # "=" 위치 찾아서 코드명만 슬라이싱
            code = str(codelink[codelink.find("=")+1:len(codelink)])
            # 태그 사이에 있는 text 추출(기업명)
            name = linkname.get_text()
            # 기존 df 데이터 프레임에 추가할 새로운 데이터 프레임 생성
            newdf = pd.DataFrame({'code':[code], 'name':[name]})
            # 데이터 프레임 추가
            df = pd.concat([df, newdf])

    개별종목의 배당수익률 및 시가총액 입수

    사이트 구조

    개별종목의 배당수익률 정보는 url 주소 "https://finance.naver.com/item/main.naver?code=종목코드"에서 얻을 수 있다.

    그림2: 개별종목 정보파악(https://finance.naver.com/item/main.naver?code=373220 2023-03-02 AM0652)

    사이트 구조를 보면 URL 맨 뒤에 종목코드가 붙어 있는 것을 알 수 있다. 코스피 200 목록 꺼내올 때 페이지 넘버링을 바꿔가면서 정보수집을 했듯이 여기서는 페이지 맨 끝에 종목코드를 바꿔가면서 데이터를 수집하는 방향으로 접근하면 된다.

    데이터 추출

    코스피 200목록은 이미 df라는 Dataframe에 넣어 놓았다. Dataframe에서 종목코드를 하나씩 뽑아서 url에 접근하고 각 페이지에서 위에서 말한 "검사" 기능을 통해서 그림 2에 표시한 배당수익률과 시가총액의 위치를 찾으면 된다. 여기서는 데이터가 내 입맛에 맞지 않게 추출되는 경우를 생각해 줘야 한다. 배당수익률은 그림 2의 예시처럼 N/A 인 경우가 있어서 이 경우 0.00으로 처리해 줘야 한다.(아래 divNull 함수) 시가총액의 경우 추출된 데이터에 띄어쓰기, 줄 바꿈이 되어 있어서 이를 제거해 주어야 한다. 그리고 "2 조 20 억"처럼 제시된 숫자는 "조"를 떼고 "20,020 억"의 숫자료 변환해 주어야 추출된 데이터로 각종 계산을 하기 쉽다.(아래 toInt 함수) 지금까지 말한 내용을 코드로 짜면 아래와 같다.

    import pandas as pd
    from bs4 import BeautifulSoup
    import requests
    
    def divNull(div):
        # div 값이 없어서 null 이 뜨면 0.00 반환
        # 아니면 text 부분 반환
        if div == None:
            dvr = 0.00
        else: dvr = div.get_text()
        return dvr
    
    def toInt(mslink):
        # 입수된 데이터에서 탭, 줄바꿈 부분 제거
        amt = str(mslink.replace("\t","").replace("\n",""))
        # 금액에 "조"가 있을 경우 처리
        if amt.find("조") >= 0:
            #"조" 라는 단어를 중심으로 split 처리
            amt = amt.split("조")
            amthead = amt[0]
            #뒷 부분"," 제거
            amttail = amt[1].replace(",","")
            #"조"뒷 부분 네 자리가 아니면 "0" 앞에 채우기
            if len(amttail) == 0:
                amttail = "0000"
            elif len(amttail) == 1:
                amttail = "000" + amttail
            elif len(amttail) == 2:
                amttail = "00" + amttail
            elif len(amttail) == 3:
                amttail = "0" + amttail
            else: amttail = amttail
            #"조" 앞부분 숫자와 뒷부분 숫자 합쳐서 숫자 형식으로 반환
            amt = int(amthead + amttail)
        else: 
            #"," 제거
            amt = int(amt.replace(",",""))
        return amt
    
    # 개별 종목의 배당율 입수
    for idx in df['code']:
        # 위에서 만든 df 에서 code 값을 하나씩 뽑아내서 url 뒤에 붙이기
        url = "https://finance.naver.com/item/main.naver?code={}".format(idx)
        # 위에서 만든 returnUrlXml 함수로 해당 URL 에서 정보 받아오기
        soup = returnUrlXml(url)
        # em 태그의 id _dvr 정보 추출(배당수익률 정보)
        dvrlink = soup.find("em", attrs={"id":"_dvr"})
        # em 태그의 id _market_sum 정보 추출(시가총액 정보)
        mslink = soup.find("em", attrs={"id":"_market_sum"}).get_text()
        # 배당금 데이터 null 일 경우 처리 위에서 만든 divNull 함수 사용
        dvr = divNull(dvrlink)
        # 시가총액 데이터 숫자형식으로 전환 위에서 만든 toInt 함수 사용
        mssum = toInt(mslink)
        # 기존 df dataframe 에 비어있던 배당정보, 시가총액 정보 업데이트
        df.loc[df['code']==idx,'dvr'] = dvr
        df.loc[df['code']==idx,'marketsum'] = mssum

    KOSPI 200 지수 구성 종목들의 배당수익률 및 시가총액

    만들어진 df 는 Dataframe 형식의 데이터로 엑셀로 바로 전환해도 문제가 없다. 아래와 같은 코드를 사용하면 "kospi200.xlsx" 이라는 이름으로 현재 파이썬 코드를 실행하는 경로에 엑셀 파일이 생성된다. 

    # excel_writer="원하는 파일이름.양식" 입력
    df.to_excel(excel_writer='kospi200.xlsx')

    엑셀 파일 생성을 포함한 전체 코드는 아래와 같다.

    import pandas as pd
    from bs4 import BeautifulSoup
    import requests
    
    # 데이터프레임 설정
    df = pd.DataFrame(columns=['code', 'name', 'dvr', 'marketsum'])
    
    # URL 접속해서 soup 에 저장
    def returnUrlXml(url):
        # requests 라이브러리 get 함수를 통해서 url 데이터 "GET"
        res = requests.get(url)
        # 수신 에러 체크
        res.raise_for_status()
        # Beautifulsoup 을 이용해서 url 데이터 텍스트를 "lxml" 형식으로 만들고
        # soup 에 저장
        soup = BeautifulSoup(res.text, "lxml")
        # soup 값 반환
        return soup
    
    def divNull(div):
        # div 값이 없어서 null 이 뜨면 0.00 반환
        # 아니면 text 부분 반환
        if div == None:
            dvr = 0.00
        else: dvr = div.get_text()
        return dvr
    
    def toInt(mslink):
        # 입수된 데이터에서 탭, 줄바꿈 부분 제거
        amt = str(mslink.replace("\t","").replace("\n",""))
        # 금액에 "조"가 있을 경우 처리
        if amt.find("조") >= 0:
            #"조" 라는 단어를 중심으로 split 처리
            amt = amt.split("조")
            amthead = amt[0]
            #뒷 부분"," 제거
            amttail = amt[1].replace(",","")
            #"조"뒷 부분 네 자리가 아니면 "0" 앞에 채우기
            if len(amttail) == 0:
                amttail = "0000"
            elif len(amttail) == 1:
                amttail = "000" + amttail
            elif len(amttail) == 2:
                amttail = "00" + amttail
            elif len(amttail) == 3:
                amttail = "0" + amttail
            else: amttail = amttail
            #"조" 앞부분 숫자와 뒷부분 숫자 합쳐서 숫자 형식으로 반환
            amt = int(amthead + amttail)
        else: 
            #"," 제거
            amt = int(amt.replace(",",""))
        return amt
    
    for page in range(1,21):
        # url 끝에 page 번호 1~ 20 까지 붙이기
        url = "https://finance.naver.com/sise/entryJongmok.naver?&page={}".format(page)
        # 위에서 만든 returnUrlXml 함수로 해당 URL 에서 정보 받아오기
        soup = returnUrlXml(url)
        codes = soup.find_all("td", attrs={"class":"ctg"})
        for linkname in codes:
            # a 태그의 link 정보 추출
            codelink = linkname.a['href']
            # 링크 정보가 "/item/main.naver?code=코드명" 으로 되어 있으므로
            # "=" 위치 찾아서 코드명만 슬라이싱
            code = str(codelink[codelink.find("=")+1:len(codelink)])
            # 태그 사이에 있는 text 추출(기업명)
            name = linkname.get_text()
            # 기존 df 데이터 프레임에 추가할 새로운 데이터 프레임 생성
            newdf = pd.DataFrame({'code':[code], 'name':[name]})
            # 데이터 프레임 추가
            df = pd.concat([df, newdf])
    
    # 개별 종목의 배당율 입수
    for idx in df['code']:
        # 위에서 만든 df 에서 code 값을 하나씩 뽑아내서 url 뒤에 붙이기
        url = "https://finance.naver.com/item/main.naver?code={}".format(idx)
        # 위에서 만든 returnUrlXml 함수로 해당 URL 에서 정보 받아오기
        soup = returnUrlXml(url)
        # em 태그의 id _dvr 정보 추출(배당수익률 정보)
        dvrlink = soup.find("em", attrs={"id":"_dvr"})
        # em 태그의 id _market_sum 정보 추출(시가총액 정보)
        mslink = soup.find("em", attrs={"id":"_market_sum"}).get_text()
        # 배당금 데이터 null 일 경우 처리 위에서 만든 divNull 함수 사용
        dvr = divNull(dvrlink)
        # 시가총액 데이터 숫자형식으로 전환 위에서 만든 toInt 함수 사용
        mssum = toInt(mslink)
        # 기존 df dataframe 에 비어있던 배당정보, 시가총액 정보 업데이트
        df.loc[df['code']==idx,'dvr'] = dvr
        df.loc[df['code']==idx,'marketsum'] = mssum
        
    df.to_excel(excel_writer='kospi200.xlsx')

     

    반응형
Designed by Tistory.