본문 바로가기

파이썬

[파이썬] 네이버증권 테마종목 크롤링(2-1) beautifulsoup4 크롤링

 

1. 테마 번호 추출

 

지난 글에서 종목을 찾은 것처럼 테마별 시세에서 각각의 테마를 의미하는 값을 보니 <td class=”col_type1”>안에 테마 넘버가 있다.

 

import requests
from bs4 import BeautifulSoup as bs

url = "<https://finance.naver.com/sise/theme.naver>"

page_url = "<https://finance.naver.com/sise/theme.naver?&page=>"

num = []
urls = []
name = []

now = page_url+"1"
res = requests.get(now)
soup = bs(res.text, 'lxml')

theme_list = soup.find_all('td', attrs={"class": "col_type1"})

for i in theme_list:
    print(i)
   

 

현재 보고 있는 첫 페이지(page=1)만 처리하는 코드

 

url = “naver.com/(생략)&page=1” 이다

theme_list = soup.find_all('td', attrs={"class": "col_type1"})

그리고 거기서 아까 찾아놓은 위치 <td class=”col_type1”>을 find_all을 이용해서 모두 찾으면 아래와 같은 결과를 얻을 수 있다.

for i in theme_list:
    print(i)

--<print result>--

<td class="col_type1"><a href="/sise/sise_group_detail.naver?type=theme&amp;no=36">해운</a></td>
<td class="col_type1"><a href="/sise/sise_group_detail.naver?type=theme&amp;no=557">유리 기판</a></td>
<td class="col_type1"><a href="/sise/sise_group_detail.naver?type=theme&amp;no=228">국내 상장 중국기업</a></td>
(생략)
<td class="col_type1"><a href="/sise/sise_group_detail.naver?type=theme&amp;no=374">4대강 복원</a></td>
<td class="col_type1"><a href="/sise/sise_group_detail.naver?type=theme&amp;no=237">GTX(수도권 광역급행철도)</a></td>

 

내가 필요한 것은 테마명과 테마 넘버이다.

 

나머지를 더 효율적으로 없앨 함수를 bs4에서 지원할 것 같은데 못 찾아서 아래와 같이 스트링처리 했다.

 

name = []
num = []

for i in theme_name_s:
    front = 'amp;no='
    
    #i to string
    a = str(i)
    
    #front에서부터 4자리 추출
    theme_num = str(a[a.find(front)+len(front):a.find(front)+len(front)+4])
		
		#4자리에서 숫자가 아닌 것 제거
    theme_num = re.sub(r'[^0-9]', '', theme_num)
		
    front = 'no=' + str(theme_num) + '">'
    end = '</a>'
		
    theme_name = str(a[a.find(front)+len(front):a.find(end)])
    #print(theme_num)
    num.append(theme_num)
    name.append(theme_name)

 

이것도 좀 이쁘게 하고 싶은데 코드가 추접스럽다.

 

theme_name_s의 자료형은 bs4.element.Tag 인데 이를 string으로 바꾸고,

 

테마 넘버를 찾기 위해 ‘amp;no=’라는 문자를 찾아서 그 길이만큼 더한 뒤에 넘버는 1~4자리 숫자니까 4를 더한 뒤에 숫자가 아닌 것은 뺐다.

 

테마 네임은 ‘amp;no=237">’ 다음부터 ‘</a>’ 전까지의 문자이니 그렇게 했다.

 

설명하는 것조차 부끄럽다.

 

이 코드는 치명적인 결함도 있는데 [no=374">4대강 복원] 과 같이 테마 이름에 숫자가 들어가고, 넘버가 한자리 수라면 오류가 날 것이다.

 

다행히(?) 그런 경우는 없었고 난 이렇게 처리했고 그 결과이다.

 

# print(name)

['해운', '유리 기판', '2024 상반기 신규상장', 'NI(네트워크통합)', '국내 상장 중국기업', '홈쇼핑', '케이블TV SO/MSO', '종합 물류', '원격진료/비대면진료(U-Healthcare)', '피팅(관이음쇠)/밸브', '제4이동통신', '의
료AI', '인터넷 대표주', '환율하락 수혜', '조선', '건설 대표주', '면세점', '윤활유', '미용기기', 'LNG(액화천연가스)', '4대강 복원', '재택근무/스마트워크', '마이데이터', '마이크로 LED', '모듈러주택', '조선기자 
재', '수자원(양적/질적 개선)', '코로나19(음압병실/음압구급차)', '보안주(정보)', '도시가스', '주류업(주정, 에탄올 등)', '시멘트/레미콘', '쿠팡(coupang)', '네옴시티', 'GTX(수도권 광역급행철도)', '해저터널(지하 
화/지하도로 등)', '기업인수목적회사(SPAC)', '아프리카 돼지열병(ASF)', '보톡스(보툴리눔톡신)', '엔터테인먼트']

# print(num)

['36', '557', '549', '56', '228', '38', '70', '141', '520', '49', '292', '149', '380', '30', '31', '374', '527',
 '94', '543', '154', '507', '435', '452', '390', '513', '524', '213', '184', '86', '176', '381', '55', '310', 
 '417', '519', '237', '363', '322', '44', '284']

 

 


8페이지까지 전체를 크롤링하는 코드는 아래와 같다.

import re
import requests
from bs4 import BeautifulSoup as bs
import time

url = "<https://finance.naver.com/sise/theme.naver>"
page_url = "<https://finance.naver.com/sise/theme.naver?&page=>"

num = []
urls = []
name = []

for j in range(1,9):
    now = page_url+str(j)
    res = requests.get(now)
    soup = bs(res.text, 'lxml')

    theme_name_s = soup.find_all('td', attrs={"class": "col_type1"})
    #theme_name = theme_name_s.find_all('a')

    for i in theme_name_s:
        front = 'amp;no='
        a = str(i)
        
        theme_num = str(a[a.find(front)+len(front):a.find(front)+len(front)+4])

        theme_num = re.sub(r'[^0-9]', '', theme_num)

        front = 'no=' + str(theme_num) + '">'
        end = ''

        theme_name = str(a[a.find(front)+len(front):a.find(end)])
        #print(theme_num)
        num.append(theme_num)
        name.append(theme_name)

    time.sleep(1)
    print(j, ": done")
print("--------------------------------")

j를 이용하여 page만 바꿔주었고 한 페이지 조회가 끝날 때마다 done을 출력하였다.

 

 

**sleep에 대한 설명

time.sleep()

import time을 활용하여 time.sleep(1)을 추가하였다.

1초 동안 코드의 진행을 멈추고 쉬는 코드이다.

반복문을 통해서 너무 빠르게 반복해서 서버를 호출하면 트래픽 공격이 되어버릴 수도 있고, 

더 중요한 것은 내 IP를 naver에서 차단해버릴 수도 있다.

request 수는 각 회사의 규정에 따라 다르다. 

OpendartReader는 분당 1천회를 기준으로 하고,  

네이버는 일일 25,000회라고 한다고 하는데 옛날 기준이라 지금은 다를 수 있다.

나는 단지 8개의 페이지를 조회할 뿐이지만 1초의 텀을 두었다.

다음 코드에서는 280개의 페이지를 조회할 것이기 때문에 반드시 sleep을 걸어서 서버를 배려해야한다.