IT

OpenDart API 인증서 오류와 임시 해결책 (OpenDartReader 사용 시)

오렌지사과키위 2024. 2. 16. 12:01

하루에 두 번씩 OpenDartReader를 이용하여 최근 7일 공시 내역을 조회하고 있습니다. 조회된 결과는 퀀트강의 슬기로운 주식 정보 사이트에서 제공하는 공시 정보의 입력 데이터로 사용합니다.

OpenDartReader 오류

오늘 2024년 2월 16일 장시작 전에 평소와 같이 공시 내역을 조회했습니다. 제대로 진행되지 않고 오류가 아래와 같은 오류가 발생했습니다.

urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='opendart.fss.or.kr', port=443): Max retries exceeded with url: /api/corpCode.xml?crtfc_key=[OPENDART_API_KEY] (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:992)')))

 

원인을 파악하기 위해 OpenDartReader 코드를 살펴보았습니다.

아래와 같이 API 키로 OpenDartReader(api_key)로 초기화할 때, 기본 기업 정보 목록을 불러옵니다.

class OpenDartReader():
    # init corp_codes (회사 고유번호 데이터)
    def __init__(self, api_key):
        # create cache directory if not exists
        docs_cache_dir = 'docs_cache'
        if not os.path.exists(docs_cache_dir):
            os.makedirs(docs_cache_dir)

        # read and return document if exists
        fn = 'opendartreader_corp_codes_{}.pkl'.format(datetime.today().strftime('%Y%m%d'))
        fn_cache = os.path.join(docs_cache_dir, fn)
        for fn_rm in glob.glob(os.path.join(docs_cache_dir, 'opendartreader_corp_codes_*')):
            if fn_rm == fn_cache:
                continue
            os.remove(fn_rm)
        if not os.path.exists(fn_cache):
            df = dart_list.corp_codes(api_key)
            df.to_pickle(fn_cache)

        self.corp_codes = pd.read_pickle(fn_cache)
        self.api_key = api_key

 

코드에서 docs_cache 디렉토리 밑에 opendartreader_corp_codes_[당일날짜].pkl 파일이 있는지 확인하고, 없으면 dart_list.corp_codes()를 불러 목록을 가져와서 저장합니다. 기업 목록은 매일 변할 수 있기에, 매일 새로 받는 것입니다. 당일 날짜가 아닌 과거 목록은 지웁니다.

기업 목록 파일은 다음과 같은 내용이 들어 있습니다.

import pickle
with open('opendartreader_corp_codes_20240216.pkl', 'rb') as f:
    df = pickle.load(f)
df
       corp_code           corp_name stock_code modify_date
0       00434003                  다코               20170630
1       00434456                일산약품               20170630
2       00430964               굿앤엘에스               20170630
3       00432403                한라판지               20170630
4       00388953   크레디피아제이십오차유동화전문회사               20170630
...          ...                 ...        ...         ...
103870  01525205                정담유통               20230228
103871  00646510  미래에셋파트너스사호사모투자전문회사               20230228
103872  01184822      미래에셋파트너스9호사모투자               20230228
103873  00755252                 시니안               20230228
103874  00227582               청호나이스               20230228

[103875 rows x 4 columns]

 

회사 코드와 종목 이름, 종목 코드, 수정 날짜가 들어 있습니다. 1만건이 넘는 회사 목록이 있는 것으로 봐서는, 상장 폐지된 종목도 포함한 듯합니다. 회사 코드와 종목 코드는 다릅니다. 종목 코드가 주식 코드입니다. OpenDart의 데이터는 회사 코드가 기준이므로, 이를 종목 코드로 바꾸어 주기 위해 기업 목록을 사용합니다.

기업업 목록을 가져오는 URL이 위에서 오류를 일으킨 https://opendart.fss.or.kr/api/corpCode.xml?crtfc_key=[OPENDART_API_KEY] 입니다.

문제의 원인 (인증서 문제)

모든 시스템에서 문제가 생기는 것은 아닙니다. 윈도우에서는 제대로 접근이 가능하고 결과를 받을 수 있습니다. 제 경우에는 윈도우에 WSL로 설치한 리눅스를 쓰는데 여기서는 문제가 발생했습니다.

wget 'https://opendart.fss.or.kr//api/corpCode.xml?crtfc_key=[OPENDART_API_KEY]'
--2024-02-16 11:08:25--  https://opendart.fss.or.kr//api/corpCode.xml?crtfc_key=[OPENDART_API_KEY]
Resolving opendart.fss.or.kr (opendart.fss.or.kr)... 61.73.60.206
Connecting to opendart.fss.or.kr (opendart.fss.or.kr)|61.73.60.206|:443... connected.
ERROR: The certificate of ‘opendart.fss.or.kr’ is not trusted.
ERROR: The certificate of ‘opendart.fss.or.kr’ doesn't have a known issuer.

 

이 문제는 https 프로토콜로 접속하려면 인증서가 있어야 하는데, 제가 사용하는 리눅스에서는 이를 확인하지 못하고 있는 것입니다.

인증서 관련 설정을 살펴보고 필요한 경우 업데이트 하는 것이 적절한 해결책입니다.

임시 해결책 (인증서 무시)

제 경우에는 OpenDartReader로 공시만 가져오고 있어, 일단은 다음과 같이 인증서 확인을 무시하고 데이터를 받아 오도록 수정했습니다.

import urllib3
import warnings

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
r = requests.get(url, params=params, verify=False)
warnings.resetwarnings()

 

urllib3의 requests.get()에 verify=False를 추가하면, 데이터를 가져올 수는 있지만, 인증서 관련 경고 메시지가 나타납니다. 여러 번 나타나는 이 경고 메시지를 안 보여주기 위해 urllib3.disable_warnings()을 사용하고, 데이터를 가져온 후 warings.resetWarnings()로 경고 메시지 무시 설정을 지웠습니다.

도움이 되었다면, 이 글을 친구와 공유하는 건 어떻까요?

facebook twitter kakaoTalk naver band