본문 바로가기
코딩 연습/코딩배우기

파이썬 크롤링(Crawling) 연습 - find(), find_all() 사용한 네이버 증시 주가 추출

by good4me 2020. 10. 29.

goodthings4me.tistory.com

 

■ 파이썬(Python) BeautifulSoup의 객체 관련 함수인 find(), find_all()를 활용한 네이버 금융의 국내증시 주가(시고저종) 부분 크롤링

  • url, headers, params를 인자로 전달하면 BeautifulSoup 객체를 return하는 function 만들어서 사용
  • 종목코드를 입력하면 오늘, 전일, 고가(상한가), 거래량, 시가, 저가(하한가), 거래대금 추출할 수 있음
  • 네이버 금융>국내증시의 페이지 소스 보기와 개발자도구(F12)에서 소스 코드 내용이 상이한 부분이 있음(<em> 태그와 <span> 태그 중 보이지 않거나 속성이 변경되는 경우가 있음)

 

▷ 페이지 소스

<div id="chart_area" class="spot">
<div class="rate_info">
    <dl class="blind">
        <dt><strong>삼성전자</strong></dt>
        <dd>오늘의시세 58,100 포인트</dd>
        <dd>900 포인트 하락</dd>
        <dd>1.53% 마이너스</dd>
    </dl>
    <div class="today">
        <p class="no_today">
            <em class="no_down">
                <span class="blind">58,100</span>
                <span class="no5">5</span><span class="no8">8</span><span class="shim">,</span><span
                    class="no1">1</span><span class="no0">0</span><span class="no0">0</span>
            </em>
        </p>
        <p class="no_exday">
            <span class="sptxt sp_txt1">전일대비</span>
            <em class="no_down">
                <span class="ico down">하락</span>
                <span class="blind">900</span>
                <span class="no9">9</span><span class="no0">0</span><span class="no0">0</span>
            </em>
            <span class="bar">l</span>
            <em class="no_down">
                <span class="ico minus">-</span>
                <span class="blind">1.53</span>
                <span class="no1">1</span><span class="jum">.</span><span class="no5">5</span><span
                    class="no3">3</span>
                <span class="per">%</span>
            </em>
        </p>
    </div>

    <table summary="주요 시세(전일종가, 시고저가, 거래량, 거래대금)을 제공합니다." class="no_info">
        <caption>주요 시세</caption>
        <colgroup>
            <col>
            <col width="214">
            <col width="157">
        </colgroup>
        <tr>
            <td class="first">
                <span class="sptxt sp_txt2">전일</span>
                <em>
                    <span class="blind">59,000</span>
                    <span class="no5">5</span><span class="no9">9</span><span class="shim">,</span><span
                        class="no0">0</span><span class="no0">0</span><span class="no0">0</span>
                </em>
            </td>
            <td>
                <span class="sptxt sp_txt4">고가</span>
                <em class="no_down">
                    <span class="blind">58,500</span><span class="no5">5</span><span class="no8">8</span><span
                        class="shim">,</span><span class="no5">5</span><span class="no0">0</span><span
                        class="no0">0</span>
                </em>
                <span class="sptxt sp_txt6">(상한가</span>
                <em class="no_cha">
                    <span class="blind">76,700</span><span class="no7">7</span><span class="no6">6</span><span
                        class="shim">,</span><span class="no7">7</span><span class="no0">0</span><span
                        class="no0">0</span>
                </em>
                <span class="sptxt sp_txt8">)</span>
            </td>
            <td>
                <span class="sptxt sp_txt9">거래량</span>
                <em>
                    <span class="blind">7,385,852</span>
                    <span class="no7">7</span><span class="shim">,</span><span class="no3">3</span><span
                        class="no8">8</span><span class="no5">5</span><span class="shim">,</span><span
                        class="no8">8</span><span class="no5">5</span><span class="no2">2</span>
                </em>
            </td>
        </tr>
        <tr>
            <td class="first">
                <span class="sptxt sp_txt3">시가</span>
                <em class="no_down">
                    <span class="blind">58,200</span><span class="no5">5</span><span class="no8">8</span><span
                        class="shim">,</span><span class="no2">2</span><span class="no0">0</span><span
                        class="no0">0</span>
                </em>
            </td>
            <td>
                <span class="sptxt sp_txt5">저가</span>
                <em class="no_down">
                    <span class="blind">58,000</span><span class="no5">5</span><span class="no8">8</span><span
                        class="shim">,</span><span class="no0">0</span><span class="no0">0</span><span
                        class="no0">0</span>

                </em>
                <span class="sptxt sp_txt7">(하한가</span>
                <em class="no_cha">
                    <span class="no4">4</span><span class="no1">1</span><span class="shim">,</span><span
                        class="no3">3</span><span class="no0">0</span><span class="no0">0</span>
                </em>
                <span class="sptxt sp_txt8">)</span>
            </td>
            <td>
                <span class="sptxt sp_txt10">거래대금</span>
                <em>
                    <span class="blind">430,148</span>
                    <span class="no4">4</span><span class="no3">3</span><span class="no0">0</span><span
                        class="shim">,</span><span class="no1">1</span><span class="no4">4</span><span
                        class="no8">8</span>
                </em>
                <span class="sptxt sp_txt11">백만</span>
            </td>
        </tr>
    </table>
</div>

good4me.co.kr

 

▷ 크롤링 연습 코드

import requests
from bs4 import BeautifulSoup

## BeautifulSoup 객체 반환 함수
def get_bs_obj(url, headers = None, params = None):
    result = requests.get(url, headers = headers, params = params)
    soup = BeautifulSoup(result.content, 'html.parser')
    return soup


## company_code를 받아서 price를 리턴하는 함수
def get_price(company_code):
    url = 'https://finance.naver.com/item/main.nhn' #?code=005930'
    params = {
        'code': company_code,
    }
    
    ## BeautifulSoup 객체 생성 (모듈 이용)
    soup = get_bs_obj(url, params = params)
    
    ## 찾는 종목코드가 없으면 None 리턴
    ## AttributeError: 'NoneType' object has no attribute 'find'
    try:
        company_name = soup.find('div', {'class':'h_company'}).find('h2')
        company_name = company_name.text  ## 회사명
    except:
        return None
    
    
    no_today = soup.find('p', {'class':'no_today'})
    #print(no_today)
    
    ## span 태그 사라지는 현상 대비하여 if문으로 분기
    bl = no_today.find('span', {'class':'blind'})
    if bl:
        price = bl.text.strip()  ## 주가 금액 부분
    else:
        price = no_today.em.text.strip()
    #print(price)
    
    
    # 전일, 고가, 시가, 저가 추출 위해서 
    # fina_all()은 단계적으로 접근함 (table만 추출 후, tr 2개 추출)

    table = soup.find('table', {'class':'no_info'})
    #print(table)
    trs = table.find_all('tr')  ## 
    #print(trs)
	
    ## 그러나, 종목코드별 주가 존재 여부에 따라 em, span 태그 수정되어서 일부 데이터 누락됨
#    close = trs[0].find_all('td')[0].find_all('span')[1].text  ## 전일
#    high = trs[0].find_all('td')[1].find_all('span')[1].text  ## 고가
#    if trs[0].find_all('td')[1].find('span', {'class':'blind'}):
#        upper = trs[0].find_all('td')[1].find_all('span')[9].text  ## 상한가
#    else:
#        upper = trs[0].find_all('td')[1].find_all('span')[10].text
#    trade_vol = trs[0].find_all('td')[2].find_all('span')[1].text  ## 거래량
#    current = trs[1].find_all('td')[0].find_all('span')[1].text  ## 시가
#    low = trs[1].find_all('td')[1].find_all('span')[1].text  ## 저가
#    lower = trs[1].find_all('td')[1].find('em', class_ = 'no_cha').get_text(strip=True, separator='')  ## 하한가
#    trade_val = trs[1].find_all('td')[2].find_all('span')[1].text  ## 거래대금
    
    

    ## 전일, 고가(상한가), 거래량
    
    bl01 = trs[0].find_all('td')[0].find('span', {'class':'blind'})
    if bl01:
        close = bl01.text.strip()
    else:
        close = trs[0].find_all('td')[0].em.text.strip()
    
    bl02 = trs[0].find_all('td')[1].find_all('em')[0].find('span', {'class':'blind'})
    if bl02:
        high = bl02.text.strip()
    else:
        high = trs[0].find_all('td')[1].find_all('em')[0].text.strip()
    
    bl03 = trs[0].find_all('td')[1].find_all('em')[1].find('span', {'class':'blind'})
    if bl03:
        upper = bl03.text.strip()
    else:
        upper = trs[0].find_all('td')[1].find_all('em')[1].text.strip()

    bl04 = trs[0].find_all('td')[2].find('span', {'class':'blind'})
    if bl04:
        trade_vol = bl04.text.strip()
    else:
        trade_vol = trs[0].find_all('td')[2].em.text.strip()

    
    ## 시가, 저가(하한가), 거래대금
    
    bl05 = trs[1].find_all('td')[0].find('span', {'class':'blind'})
    if bl05:
        current = bl05.text.strip()
    else:
        current = trs[1].find_all('td')[0].em.text.strip()
    
    bl06 = trs[1].find_all('td')[1].find_all('em')[0].find('span', {'class':'blind'})
    if bl06:
        low = bl06.text.strip()
    else:
        low = trs[1].find_all('td')[1].find_all('em')[0].text.strip()
    
    bl07 = trs[1].find_all('td')[1].find_all('em')[1].find('span', {'class':'blind'})
    if bl07:
        lower = bl07.text.strip()
    else:
        lower = trs[1].find_all('td')[1].find_all('em')[1].text.strip()

    bl08 = trs[1].find_all('td')[2].find('span', {'class':'blind'})
    if bl08:
        trade_val = bl08.text.strip()
    else:
        trade_val = trs[1].find_all('td')[2].em.text.strip()
    
    return {'company_name':company_name, 'price':price, 'close':close, 'high':high,\
            'upper':upper, 'trade_vol':trade_vol, 'current':current, 'low':low,\
            'lower':lower, 'trade_val':trade_val}
    

 

codes = ['005930', '051900', '051905', '003240', '000009', '051910']

## codes 리스트의 종목코드를 찾아 결과를 리턴한 내역을 for문으로 출력함
for code in codes:
    result = get_price(code)  ## 함수 호출
    
    ## 종목코드가 없을 경우 None 리턴
    if result == None:
        print('종목코도 {} : {}\n{}'.format(code, None, '=' * 50))
        continue
        
    print('\n회사명: {0}\n오늘: {1}\n전일: {2}\n고가: {3} (상한가 {4})\n거래량: {5}주\n시\
가: {6}\n저가: {7} (하한가 {8})\n거래대금: {9}백만\n'.\
    format(result['company_name'], result['price'], result['close'], result['high'], \
           result['upper'], result['trade_vol'], result['current'], result['low'], \
           result['lower'], result['trade_val']))

    print('=' * 50)

[실행 결과]

회사명: 삼성전자
오늘: 58,100
전일: 59,000
고가: 58,500 (상한가 76,700)
거래량: 20,259,626주
시가: 58,200
저가: 57,500 (하한가 41,300)
거래대금: 1,175,233백만

==================================================

회사명: LG생활건강
오늘: 1,509,000
전일: 1,524,000
고가: 1,522,000 (상한가 1,981,000)
거래량: 19,907주
시가: 1,521,000
저가: 1,496,000 (하한가 1,067,000)
거래대금: 29,972백만

==================================================

회사명: LG생활건강우
오늘: 699,000
전일: 706,000
고가: 704,000 (상한가 917,000)
거래량: 4,711주
시가: 704,000
저가: 695,000 (하한가 495,000)
거래대금: 3,294백만

==================================================

회사명: 태광산업
오늘: 696,000
전일: 707,000
고가: 702,000 (상한가 919,000)
거래량: 672주
시가: 701,000
저가: 693,000 (하한가 495,000)
거래대금: 467백만

==================================================
종목코도 000009 : None
==================================================

회사명: LG화학
오늘: 650,000
전일: 642,000
고가: 653,000 (상한가 834,000)
거래량: 314,790주
시가: 629,000
저가: 629,000 (하한가 450,000)
거래대금: 202,062백만

==================================================

 

 

[참고] 한입에 크롤링 - Kyeongrok Kim

 

댓글