고객사 정보 관리 효율화 도전 (1) - 공시 데이터를 활용한 수집

비오박·2025년 2월 20일

고객사정보관리

목록 보기
1/3

고객사의 정보를 최신 상태로 유지하는 일은 많은 고객을 관리하는 담당자에게 끝없는 숙제다. 아침에 확인한 정보가 오후엔 구식이 되어버리는 현실 속에서, IT 기술을 활용해 이 문제를 해결할 수 있을까? 이런 고민을 바탕으로 새로운 도전을 시작해본다.

1. 고객사 정보를 최신 상태로 유지하려면?

기존 방식은 담당자가 직접 검색하고, 자료를 정리하는 형태이다. 하지만,

  • 시간이 오래 걸리고,
  • 사람마다 관리 방식이 다르며,
  • 최신 정보를 놓칠 가능성이 크다.

그래서 DART 전자공시 API를 활용하여, 고객사의 최신 공시 정보 및 보고서를 수집하는 방법이 효과적일지 테스트해 보기로 했다. 실제 업무에서 유용하게 활용할 수 있을지 확인하기 위해 여러 접근 방식을 시도해 보려고 한다.!

그리고... 여기에 생성형 AI 기술을 접목하면 어떨까? 향후 AI를 활용해 보고서를 자동으로 정리하는 실험도 해볼 계획이다. 🚀


2. 데이터 수집을 위한 탐색 과정

초기에는 네이버 금융, KIND(전자공시), DART(금융감독원 전자공시) 등 여러 사이트를 탐색하며 기업 정보를 자동으로 가져올 방법을 찾아봤다.
하지만, ChatGPT 및 기타 자동화 도구의 웹 스크래핑을 차단하는 사이트가 많았다! 😰

특히, 기대를 품고 웹 데이터를 긁어보려 했을 때 robots.txt에 의해 차단되었습니다라는 메시지를 마주했을 때의 허탈함이란... 결국, 데이터를 공식적으로 가져올 방법을 찾아야 했다.

그래서 DART에서 제공하는 Open API를 활용하는 것이 가장 현실적인 방법이라는 결론을 내렸다. 공식적으로 제공되는 데이터를 활용하면 안정성도 확보할 수 있고, 최신 정보를 효율적으로 가져올 수 있기 때문이다.


3. DART API를 활용한 기업정보 수집 과정

기업 정보를 수집하려면 DART(OpenDART) API를 활용하는 것이 가장 효율적이다.
공식 API 문서는 아래 URL에서 확인할 수 있다.

🔗 DART API 공식 문서: https://opendart.fss.or.kr/

DART API를 활용하면 기업의 기본 정보뿐만 아니라, 주요 공시 및 보고서까지 자동으로 수집 가능하다.
이번 포스팅에서는 "현대오토에버"를 예시 고객사로 설정하여,

  • 기업 고유번호를 조회하고,
  • 4개 주요 보고서(사업보고서, 반기보고서, 분기보고서, 감사보고서)를 수집하는 코드를 구현할 것이다.

4. DART API 활용한 최신 보고서 조회 (Python 구현)

이제, 실제로 DART API를 이용하여 기업 정보를 가져오고, 최신 보고서를 조회하는 과정을 살펴보자.

Step 1. 고객사 고유번호 가져오기

DART에서 기업 정보를 조회하려면 기업의 고유번호(corp_code)가 필요하다.
이를 위해 API를 통해 전체 기업 리스트를 가져오는 기능을 구현했다.

API_KEY = os.getenv("OPEN_DART_API_KEY")

# 기업 코드 리스트를 가져와 CSV 파일로 저장하는 함수
def fetch_corp_codes():
    url = "https://opendart.fss.or.kr/api/corpCode.xml"
    params = {"crtfc_key": API_KEY}
    
    response = requests.get(url, params=params)
    if response.status_code == 200:
        with zipfile.ZipFile(io.BytesIO(response.content)) as z:
            with z.open("CORPCODE.xml") as xml_file:
                tree = ET.parse(xml_file)
                root = tree.getroot()
    
        corp_list = [
            [corp.find("corp_code").text.strip(), corp.find("corp_name").text.strip()]
            for corp in root.findall("list")
        ]
        
        df = pd.DataFrame(corp_list, columns=["corp_code", "corp_name"])
        df.to_csv("corp_list.csv", index=False, encoding="utf-8-sig")
        
        print("✅ 기업 코드 목록 저장 완료: corp_list.csv")

이 코드는 DART에서 제공하는 전체 기업 목록을 XML 형태로 받아와 CSV 파일(corp_list.csv)로 저장한다.
이를 통해 기업명을 입력하면 해당 기업의 고유번호를 빠르게 찾을 수 있다.

Step 1 결과:

corp_list.csv 파일
현대오토에버 기업 코드: 00362441

Step 2. 고객사 최신 보고서 조회하기

고객사의 4대 보고서(사업보고서, 반기보고서, 분기보고서, 감사보고서)를 자동으로 수집하는 기능을 구현한다.

API_KEY = os.getenv("OPEN_DART_API_KEY")
if not API_KEY:
    print("환경 변수 OPEN_DART_API_KEY를 설정해야 합니다.")

def download_reports(company_name: str) -> (str, dict):
    # corp_list.csv 파일에서 회사명을 검색하여 회사 코드 찾기
    corp_list_file = "corp_list.csv"

    # dtype=str을 사용하여 고유번호 앞의 "00"이 유지되도록 함
    df = pd.read_csv(corp_list_file, encoding="utf-8-sig", dtype=str)

    # 1. 회사명이 완전히 일치하는 경우 우선 선택
    exact_match = df[df["corp_name"] == company_name]

    if not exact_match.empty:
        corp_info = exact_match.iloc[0]
    else:
        # 2. 포함된 회사명을 찾음 (대소문자 무시)
        contains_match = df[df["corp_name"].str.contains(company_name, case=False, na=False)]

        if contains_match.empty:
            print(f"{company_name}에 대한 기업 정보가 없습니다.")
            exit()

        corp_info = contains_match.iloc[0]

    corp_code = corp_info["corp_code"]
    found_company = corp_info["corp_name"]
    print(f"검색된 기업: {found_company} (고유번호: {corp_code})")

    # Step 2: 최신 공시 목록 검색
    disclosure_url = "https://opendart.fss.or.kr/api/list.json"

    params = {
        "crtfc_key": API_KEY,
        "corp_code": corp_code,
        "bgn_de": "20230101",  # 시작일 (2023년 1월 1일)
        "page_no": "1",
        "page_count": "100"  # 최근 100개 공시 검색
    }

    response = requests.get(disclosure_url, params=params)
    data = response.json()

    # Step 3: 최신 공시 중 사업보고서, 반기보고서, 분기보고서 각 1개만 선택
    target_reports = {"사업보고서": None, "반기보고서": None, "분기보고서": None, "감사보고서": None}

    if "list" in data:
        for report in data["list"]:
            report_nm = report["report_nm"]
            for target in target_reports.keys():
                if target in report_nm and target_reports[target] is None:
                    target_reports[target] = report  # 가장 최신 보고서만 저장

    # Step 4: 원본 파일(ZIP) 다운로드 및 압축 해제
    download_folder = f"dart_reports/{found_company}"
    os.makedirs(download_folder, exist_ok=True)

    # 보고서 내용 저장 변수
    report_contents = {"사업보고서": "", "반기보고서": "", "분기보고서": "", "감사보고서": ""}

    for report_type, report in target_reports.items():
        if report:
            rcp_no = report["rcept_no"]
            report_nm = report["report_nm"]
            report_url = f"https://dart.fss.or.kr/dsaf001/main.do?rcpNo={rcp_no}"  # 원본 확인 URL

            # 공시 원본 파일 다운로드 API
            document_url = "https://opendart.fss.or.kr/api/document.xml"
            document_params = {
                "crtfc_key": API_KEY,
                "rcept_no": rcp_no
            }

            doc_response = requests.get(document_url, params=document_params)

            if doc_response.status_code == 200:
                zip_path = os.path.join(download_folder, f"{report_nm}_{rcp_no}.zip")
                with open(zip_path, "wb") as f:
                    f.write(doc_response.content)

                # ZIP 압축 해제
                extract_path = os.path.join(download_folder, report_type)
                os.makedirs(extract_path, exist_ok=True)

                with zipfile.ZipFile(zip_path, 'r') as zip_ref:
                    zip_ref.extractall(extract_path)

                print(f"✅ {report_nm} 다운로드 완료")
                print(f"   📂 저장 폴더: {extract_path}")
                print(f"   🔗 원본 확인: {report_url}")

                # Step 5: XML 파일 읽기 및 내용 추출 (파싱 없이 단순 Read)
                xml_files = [f for f in os.listdir(extract_path) if f.endswith(".xml")]
                if (xml_files):
                    xml_path = os.path.join(extract_path, xml_files[0])
                    try:
                        # XML을 직접 텍스트로 읽기
                        with open(xml_path, "r", encoding="utf-8", errors="replace") as f:
                            text_content = f.read()

                        # 필요 없는 공백 및 개행 제거
                        text_content = text_content.replace("\n", " ").strip()

                        # 해당 보고서 유형의 변수에 저장
                        report_contents[report_type] = text_content

                    except Exception as e:
                        print(f"❌ {report_nm} XML 파일 읽기 실패: {e}")

Step 2 결과:

현대오토에버 기업 코드 : 00362441

[기재정정] 사업보고서 (2023.12) 다운로드 완료
📂 저장 폴더: dart_reports/현대오토에버/사업보고서
🔗 원본 확인: 링크

반기보고서 (2024.06) 다운로드 완료
📂 저장 폴더: dart_reports/현대오토에버/반기보고서
🔗 원본 확인: 링크

분기보고서 (2024.09) 다운로드 완료
📂 저장 폴더: dart_reports/현대오토에버/분기보고서
🔗 원본 확인: 링크

감사보고서 제출 다운로드 완료
📂 저장 폴더: dart_reports/현대오토에버/감사보고서
🔗 원본 확인: 링크


5. 다음 포스팅 예고 🔥

이제 보고서를 가져오는 기능은 구현했으니, 다음 단계는 고객사 정보를 자동으로 추출하고 요약하는 것!
다음 포스팅에서는 다음 내용을 다룰 예정이다.

API를 활용한 고객사 주요 정보 추출
ChatGPT를 이용한 보고서 요약 자동화

고객사 정보를 최신 상태로 유지하는 방법, 이제 자동화로 한 걸음 더 나아가 보자! 🚀

0개의 댓글