학교 공부/졸업 프로젝트 (RAG)

[python / colab] 증권사 리포트 pdf 파일 크롤링하기

iinana 2025. 4. 24. 10:14

 대표적으로 증권사 리포트를 확인할 수 있는 사이트인 한경컨센서스와 네이버금융리서치를 크롤링하여 리포트 pdf를 구글 드라이브에 저장하는 작업을 코랩에서 수행해 봤다.

 

 공통 단계 

  • 사전 준비
# 구글 드라이브 마운트 (구글 드라이브와 코드 연결)
from google.colab import drive
drive.mount('/content/drive')
# selenium 설치
!pip install selenium
!apt-get update
# 구글 드라이브에 choremdriver 설치 (최초 1회)
!apt install chromium-chromedriver
!cp /usr/bin/chromedriver "/" #chorme driver 설치할 dir
!pip install chromedriver-autoinstaller
# 그 외 필요한 패키지들 다운
!pip install requests
!pip install PyPDF2

 

  • 실행 준비
# 라이브러리 임포트
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import sys
from selenium.webdriver.common.keys import Keys
import urllib.request
import os
from urllib.request import urlretrieve

import time
import pandas as pd
import chromedriver_autoinstaller  # setup chrome options
# chrome_options 설정 (크롬드라이버 실행 옵션)
## chromedriver 경로 명시
chrome_path = "" # chrome driver dir
sys.path.insert(0,chrome_path)

chrome_options = webdriver.ChromeOptions()
## colab은 새 창을 지원하지 않기 때문에 --headless 옵션 필요 (브라우저 창을 띄우지 않고 백그라운드에서 실행)
chrome_options.add_argument('--headless')
## 보안 샌드박스 없이 실행 (제한된 환경인 colab에서 필요한 설정)
chrome_options.add_argument('--no-sandbox')
## colab의 용량 제한으로 인한 오류 방지
chrome_options.add_argument('--disable-dev-shm-usage')
## chrome 언어 한국어로 설정
chrome_options.add_argument('lang=ko_KR')

## set the target URL
chromedriver_autoinstaller.install()

  위 옵션 설정에서 중요한 부분은 argument 중 하나인 --headless는 colab이 새 창을 지원하지 않기 때문에 필요한 옵션으로, 만약 로컬 환경을 사용한다면 추가하지 않아도 된다. 이러한 특성 때문에 실제로 크롤링이 돌아가는 창을 확인하며 내가 의도한 대로 실행되는지 확인할 수 없다는 단점이 있다. 나는 그래서 디버깅을 할 때 아래 코드를 이용해 문제가 된다고 예상되는 파트에서 백그라운드로 실행 중인 화면의 스크린숏을 남겨 확인했다.

driver.save_screenshot("screen.png")
from IPython.display import Image
Image("screen.png")

 

 크롤링 - 한경 컨센서스 

https://markets.hankyung.com/consensus

 

한경 컨센서스 | 한국경제

주간 목표 상향 TOP10 리포트 조회 검색결과가 없습니다. 코리아마켓 금융 정보는 각 콘텐츠 제공업체로부터 받는 투자 참고사항이며, 오류가 발생하거나 지연될 수 있습니다. 한경닷컴과 콘텐츠

markets.hankyung.com

 

# url과 driver set
main_url = "https://markets.hankyung.com/consensus"
login_url = "https://id.hankyung.com/login/login.do"
driver =  webdriver.Chrome(options=chrome_options)

 

 한경 컨센서스의 경우 로그인을 해야 리포트 파일에 접근 가능하기 때문에 본격적인 크롤링 전 꼭 로그인 단계를 원격으로 실행해줘야 한다.

# 한경 컨센서스 로그인
driver.get(login_url)
user_id = driver.find_element(By.ID, 'userId')
user_id.send_keys("") #한경컨센서스 아이디 입력

user_pw = driver.find_element(By.ID, 'userPass')
user_pw.send_keys("") #한경컨센서스 비밀번호 입력
user_pw.send_keys(Keys.RETURN)

 

 리포트를 순회하면서 가져오는 방식은 현재 페이지에서 가장 첫 번째 리포트의 id를 가져오고 맨 끝 페이지로 이동해서 맨 마지막 리포트의 id를 가져와서 순회하는 식이다. 가장 첫 리포트부터 마지막 리포트까지 하나씩 감소하면서 id가 부여되는 방식이며 리포트 pdf의 url이 base_url + id의 형식을 지켰기에 가능한 방식이었다. 

 또한, 마지막 리포트에 접근할 때는 검색 탭에서 기간을 설정하여 접근 가능하도록 코드를 작성했다.

# report 시작 id 가져오기
import re

driver.get(main_url)
time.sleep(3)

a_tags = driver.find_elements(By.CSS_SELECTOR, 'a.ellip')
href = a_tags[0].get_attribute('href')
start_id = re.search(r'/consensus/view/(\d+)', href).group(1)
start_id = int(start_id)
# report 끝 id 가져오기
from selenium.webdriver.support.ui import Select

driver.get(main_url)
time.sleep(3)

## 기간 선택 (시작 id에서는 불필요)
dropdown_arrow = driver.find_element(By.CLASS_NAME, 'select2-selection__arrow')
dropdown_arrow.click()
time.sleep(1)

# "최근 7일", "1개월", "6개월", "1년"
recent_7 = driver.find_element(By.XPATH, '//li[contains(text(), "최근 7일")]')
recent_7.click()
time.sleep(1)

search_button = driver.find_element(By.CLASS_NAME, "btn-primary")
search_button.click() # 검색 버튼 클릭
time.sleep(1)

## 끝 페이지로 이동
end_page=driver.find_element(By.CLASS_NAME, 'btn-page-end')
end_page.click()

a_tags = driver.find_elements(By.CSS_SELECTOR, 'a.ellip')
href = a_tags[-1].get_attribute('href')
end_id = re.search(r'/consensus/view/(\d+)', href).group(1)
end_id = int(end_id)

 

 마지막으로 마지막 리포트의 id ~ 첫 리포트의 id를 순회하면서 pdf를 지정된 구글 드라이브 directory에 저장하며 끝낸다. 

import requests
from io import BytesIO
from PyPDF2 import PdfReader

base_url = "https://markets.hankyung.com/consensus/view/"
# Selenium 세션에서 쿠키 가져오기
cookies = driver.get_cookies()

# requests 세션 생성
session = requests.Session()
for cookie in cookies:
    session.cookies.set(cookie['name'], cookie['value'])

for i in range(end_id, start_id):
  driver.get(base_url + str(i))
  time.sleep(3)

  title=driver.find_element(By.CLASS_NAME, "report-tit")
  title=title.text.strip()
  title=re.sub(r'[\\/*?:"<>|]', "", title)

  file_url=driver.find_element(By.CSS_SELECTOR, 'a.btn.btn-default').get_attribute('href')
  driver.get(file_url)
  time.sleep(3)

  response = session.get(file_url)
  if response.status_code == 200:
      with open("#pdf 저장할 dir"+title+".pdf", "wb") as f:
          f.write(response.content)
      print("PDF 다운로드 완료.")
  else:
      print("PDF 파일 다운로드 실패.")

 

 크롤링 - 네이버 증권 리서치 

https://finance.naver.com/research/

 

리서치 : 네이버페이 증권

관심종목의 실시간 주가를 가장 빠르게 확인하는 곳

finance.naver.com

 

 네이버 증권 리서치의 장점은 로그인 없이 리포트 pdf 파일에 접근할 수 있다는 점이다. 한경 컨센서스처럼 별도로 원격 로그인 하는 코드를 쓸 필요가 없다. 다만, 종목/산업/시황/투자정보 리포트가 약간 다른 형태의 html로 주어져서 관련해서 살짝만 수정해 주면 된다. 또, 종목/산업 리포트끼리는 형식이 같고, 시황/투자정보 리포트끼리도 형식이 같다. 나는 종목/산업 분석 리포트를 중심으로 작성했다. 사실 시황/투자정보라고 하더라도 인덱스만 확인하고 살짝 수정해주면 된다.

# url과 driver set
url = "https://finance.naver.com/research/company_list.naver?&page=" #종목분석 리포트
#url = "https://finance.naver.com/research/industry_list.naver?page=" #산업분석 리포트

save_dir = "/" # pdf 저장할 directory
driver =  webdriver.Chrome(options=chrome_options)

 

 앞선 한경 컨센서스 크롤링에서는 검색탭에 주어지는 기간 설정을 이용했지만, 네이버 금융 리서치의 경우 그냥 직접 날짜를 확인하는 편이 더 간편해 보여서 그렇게 진행했다.

# 크롤링 기간 설정
from datetime import datetime, timedelta

end_date = datetime.now() # 오늘 날짜
start_date = end_date - timedelta(days=7) # n일 전 날짜
import requests
from io import BytesIO
from PyPDF2 import PdfReader

# Selenium 세션에서 쿠키 가져오기
cookies = driver.get_cookies()

# requests 세션 생성
session = requests.Session()
for cookie in cookies:
    session.cookies.set(cookie['name'], cookie['value'])

page = 1
while True:
  try:
    driver.get(url + str(page))
    time.sleep(3)
  except Exception:
    continue

  reports_tab = driver.find_element(By.XPATH, "/html/body/div[3]/div[2]/div[2]/div[1]/div[2]/table[1]/tbody")
  reports = reports_tab.find_elements(By.TAG_NAME, "tr") # 리포트 목록
  
  for r in reports:
    tds = r.find_elements(By.TAG_NAME, "td")
    if len(tds) < 4:
       continue

    company = tds[0].text.strip() # 회사 이름
    date = tds[4].text.strip() # 작성일

    ## 기간 확인
    try:
      report_date = datetime.strptime(date, "%y.%m.%d")
    except ValueError:
            continue
    if not (start_date <= report_date <= end_date):
        continue  # 원하는 날짜 범위가 아니면 스킵

    ## pdf 파일 url 추출
    file = tds[3].find_element(By.TAG_NAME, "a").get_attribute("href")
    driver.get(file)
    time.sleep(3)
    
    ## pdf 파일 저장
    response = session.get(file)
    time.sleep(3)
    if response.status_code == 200:
        with open(save_dir+company+"_"+date+".pdf", "wb") as f:
            f.write(response.content)
        print("PDF 다운로드 완료.")
    else:
        print("PDF 파일 다운로드 실패.")

  page += 1
728x90
반응형