💻

Chapter1. 금융용어사전

 
 
 
 
 

1.1 금융용어사전 소개

 
금융용어사전(https://fine.fss.or.kr/fine/fnctip/fncDicary/list.do?menuNo=900021)은 금융소비자정보포털 파인(FINE)에서 제공하는 다양한 금융 용어와 관련된 사전입니다. 이 사전은 금융 용어와 그 용어에 대한 설명을 제공하여 소비자들이 금융 용어의 의미를 쉽게 파악하고 금융 관련 정보를 이해할 수 있도록 도와줍니다. 다양한 금융 분야에서 사용되는 용어들이 포함되어 있어 다양한 주제에 대한 용어를 검색할 수 있습니다.
 
 
 

1.2 금융용어사전 크롤링 목적

 
금융용어사전에서 다양한 금융 용어와 그 설명에 대한 데이터를 수집하고자 합니다. 이를 통해 금융용어사전 페이지에 직접 접속하지 않고도 필요한 용어만 필터링해서 찾아볼 수 있도록 합니다.
 
따라서, 금융용어사전에서 제공하는 모든 용어와 각 용어에 대한 정의를 크롤링 하여 MySQL DB에 저장한 후, 알고자 하는 단어가 포함된 데이터만 필터링할 수 있는 기능을 직접 만들어 사용해 보겠습니다.
 
이번 챕터에서는 동적 크롤링을 사용하지 않고, 정적 크롤링만을 사용하도록 하겠습니다. 정적 크롤링을 도와주는 requests와 BeautifulSoup 라이브러리를 사용하여 원하는 데이터를 파싱하고 추출하는 코드를 작성해보도록 하겠습니다.
 
 

1.3 크롤링 가능 여부 확인

 
크롤링을 시작하기에 앞서 웹 페이지 주소 창에 https://fine.fss.or.kr/robots.txt 를 입력하여 크롤링 가능 여부를 확인합니다.
User-agent : Yeti Disallow: /bos/ Disallow: /upload/ Disallow: /fss/cvpl/stepNotice/ Disallow: /fss/cvpl/extincNotice/
 
금융용어사전을 제공하는 파인(Fine)의 robots.txt를 살펴보니, 검색로봇 Yeti에 대해서 아래 4가지 경로만 불허(Disallow) 되어 있습니다. 크롤링을 하고자 하는 금융용어사전 사이트는 위 4가지 경로에 해당하지 않으므로 크롤링을 진행할 수 있습니다.
 
📢
robots.txt를 통해 크롤링이 허용된 것을 확인했다 하더라도 상업적으로 이용하거나, 웹 서비스 제작자의 의도에 반하면 법적 책임을 질 수 있어 주의가 필요합니다.
 

1.4 금융용어사전 사이트 탐색

 
먼저, 금융용어사전 페이지에서 총 2개의 데이터를 가져와 활용하려고 합니다. 각 데이터의 구체적인 정보와 활용 방안은 아래와 같습니다.
 
 
  1. 전체 페이지 수
웹 사이트를 확인해보니, 전체 페이지 수 데이터가 포함되어 있는 부분을 찾을 수 있었습니다. 해당 데이터는 id가 ‘content’인 요소 중 class가 ‘count-total’인 요소 안에 있습니다. class가 ‘count-total’인 요소 중 전체 페이지 수를 나타내는 숫자 데이터만 추출해서 사용하고자 합니다.
 
  1. 용어 및 내용
용어와 각 용어의 내용 데이터가 포함되어 있는 요소는 id가 ‘content’인 요소 아래의 div 요소 중 class가 ‘bd-list result-list’인 dl 요소에 들어가 있습니다. 해당 요소의 텍스트 값 중 용어 및 각 용어의 내용 부분만 추출해서 사용하고자 합니다.
 
 

1.5 금융용어사전 크롤링

 
위에서 살펴본 데이터들을 가지고 크롤링을 진행하겠습니다.
 

1.5.1 전체 페이지 수 구하기

먼저, 정적 크롤링에 필요한 request 와 BeautifulSoup4 라이브러리, 그리고 DataFrame을 다루는데 필요한 pandas 라이브러리를 함께 불러옵니다.
import requests from bs4 import BeautifulSoup as bs import pandas as pd
 
다음으로, 모든 페이지를 돌며 금융용어사전에 있는 모든 용어를 크롤링 하기 위해 전체 페이지 수를 파악하도록 하겠습니다. request 라이브러리의 get() 메서드를 사용하여 주어진 URL에 GET 요청을 보냅니다. 이를 통해 웹 페이지의 HTML 내용을 가져오고, 해당 내용을 response 변수에 저장을 합니다. 이후 BeautifulSoup 라이브러리를 사용하여 HTML 내용을 파싱 합니다. ‘html.parser’ 파서를 사용하여 soup 객체를 생성하면 웹 페이지의 구조를 탐색하고 원하는 요소를 추출할 수 있습니다.
url = 'https://fine.fss.or.kr/fine/fnctip/fncDicary/list.do?menuNo=900021&pageIndex=1&src=&kind=&searchCnd=1&searchStr=' response = requests.get(url) html = response.text soup = bs(html, 'html.parser')
 
웹 페이지에서 특정 요소를 선택하기 위해 soup.select_one() 메서드를 사용합니다. id가 'content'인 요소 아래의 div 요소 중 class가 'count-total'인 요소에 페이지 수가 포함되어 있기 때문에 해당 요소를 선택합니다. 그리고 해당 결과는 result 변수에 저장하도록 하겠습니다.
이후 선택한 요소의 텍스트 값을 가져와서 페이지 수를 나타내는 숫자만을 가져오도록 하겠습니다. result.text를 통해 텍스트 값을 먼저 가져온 후 뒤에서 8번째부터 6번째까지의 문자열을 가져옵니다. 추출된 문자열은 정수로 변환하여 page_total 변수에 저장하도록 하겠습니다. page_total 값을 출력하면, URL에 있는 총 페이지 개수를 확인할 수 있습니다.
# 페이지 수 구하기 result = soup.select_one('#content > div.count-total') page_total = int(result.text[-8:-6]) print(page_total)
➡️
결과
54
 

1.5.2 전체 용어 크롤링

 
완성된 크롤링 코드
아래 코드는 1.5.2 코드의 완성본입니다.
# 크롤링한 데이터를 저장할 DataFrame df = pd.DataFrame(columns=['term','content']) for i in range(page_total): url = 'https://fine.fss.or.kr/fine/fnctip/fncDicary/list.do?menuNo=900021&pageIndex='+str(i+1)+'&src=&kind=&searchCnd=1&searchStr=' print(url) response = requests.get(url) html = response.text soup = bs(html, 'html.parser') results = soup.select('#content > div.bd-list.result-list > dl') for result in results: title_split = result.find('dt').text.split('.') # index index = '{0:03d}'.format(int(title_split[0].replace('\r','').replace('\n','').replace('\t',''))) # term term = title_split[1].replace('\r','').replace('\n','').replace('\t','') # content content = result.find('dd').text # DataFrame에 저장 df.loc[index] = [term, content]
 
Dataframe 생성
크롤링을 통해 2가지 데이터를 저장할 Dataframe과 Column을 생성합니다.
# 크롤링한 데이터를 저장할 DataFrame df = pd.DataFrame(columns=['term','content'])
 
모든 페이지를 순회하며 크롤링 할 요소들 가져오기
앞서 1.5.1에서 구한 총 페이지 수를 활용해 모든 페이지를 순회하며 금융용어사전의 모든 용어와 각 용어의 내용을 가져오도록 하겠습니다. 페이지 수를 가져올 때와 동일하게 이번에는 용어와 용어의 내용이 담겨 있는 요소를 파악해 보도록 하겠습니다. 여기에서는 id가 ‘content’인 요소 아래의 div 요소 중 class가 ‘bd-list result-list’인 dl 요소들에 들어가 있기 때문에 해당 부분을 선택하여 results 변수에 저장합니다.
for i in range(page_total): url = 'https://fine.fss.or.kr/fine/fnctip/fncDicary/list.do?menuNo=900021&pageIndex='+str(i+1)+'&src=&kind=&searchCnd=1&searchStr=' response = requests.get(url) html = response.text soup = bs(html, 'html.parser') results = soup.select('#content > div.bd-list.result-list > dl')
 
각 요소 내에서 용어와 용어의 내용만 추출하기
이후 results의 각 요소를 순회하여 필요한 데이터들을 추출하도록 하겠습니다. 먼저 result에서 dt 요소를 찾고, 해당 요소의 텍스트를 가져온 후 마침표(.)를 기준으로 분할합니다. 이를 통해 용어의 번호와 용어를 분리하여 index와 term 변수에 각각 할당을 해주고, replace() 메서드를 활용하여 불필요한 요소들을 제거합니다.
마지막으로, result에서 dd 요소를 찾고 해당 요소의 텍스트를 가져와 용어의 내용을 content 변수에 할당합니다.
이렇게 추출한 term, content 값을 사용하여 DataFrame에 저장을 해줍니다.
for result in results: title_split = result.find('dt').text.split('.') # index index = '{0:03d}'.format(int(title_split[0].replace('\r','').replace('\n','').replace('\t',''))) # term term = title_split[1].replace('\r','').replace('\n','').replace('\t','') # content content = result.find('dd').text # DataFrame에 저장 df.loc[index] = [term, content]
Dataframe 확인
df를 출력해 크롤링 된 데이터와 그 형태를 확인합니다.
df
➡️
결과
notion imagenotion image
 
 

1.6 데이터 전처리

 
Dataframe에 저장된 데이터의 전처리를 진행하겠습니다.
 
데이터 원본 복사
본격적인 전처리를 시작하기에 앞서, 데이터 원본 새로운 변수에 저장합니다. Dataframe 원본을 복사해두는 이유는 전처리 과정에서 오류가 나거나, 실수를 했을 경우를 대비하는 것입니다.
df_save = df.copy()
 
만약 전처리 과정에서 오류가 난다면, 아래 코드를 실행하여 데이터 원본을 불러와 사용할 수 있습니다.
# 전처리 과정 중에 데이터 원본이 다시 필요한 경우 아래 코드 실행 df = df_save.copy()
 

1.6.1 한국어 / 영어 단어 분리

 
term_en Column 생성 및 값 저장
term Column에는 한국어 단어와 대괄호([]) 안에 영어 단어로 표기되어 있는 것을 확인할 수 있습니다. 이 term Column을 분할하여 한국어 단어와 영어 단어를 따로 저장해 보도록 하겠습니다. 두 단어는 열린 대괄호([)를 기준으로 구분되어 있습니다. 따라서 열린 대괄호([)를 기준으로 구분하여 term_split에 저장해 줍니다.
만약, 한국어 단어만 존재할 경우, term_split의 길이는 1이 되게 됩니다. 이러한 경우에는 영어 단어를 빈 문자열(””)로 DataFrame에 넣어줍니다. 반면, 한국어 단어와 영어 단어가 모두 존재할 경우에는, 영어 단어에서 마지막 문자인 닫힌 대괄호(])를 제거하기 위해 마지막 문자열을 제외한 나머지 문자열을 DataFrame에 넣어줍니다. 또한 한국어 단어와 영어 단어 공통적으로 모두 strip() 메서드를 사용하여 앞뒤 공백을 제거해 주었습니다.
df['term_en'] = '' for index, row in df.iterrows(): term = row.copy()['term'] # SettingWithCopyWarning 발생 방지를 위한 copy term_split = term.split('[') if len(term_split) == 1: df.loc[index, 'term'] = term_split[0].strip() df.loc[index, 'term_en'] = '' else: df.loc[index, 'term'] = term_split[0].strip() df.loc[index, 'term_en'] = term_split[1][:-1].strip()
 
df를 출력해 전처리가 정상적으로 실행되었는지 확인합니다.
df
➡️
결과
notion imagenotion image
 
 

1.7 MySQL DB에 데이터 적재

 
금융용어사전에서 크롤링 하여 전처리까지 완료한 데이터를 MySQL DB에 저장해 보겠습니다.
 

1.7.1 DB 생성

먼저, root 계정에 ‘financial_terms’ DB를 생성하겠습니다.
 
MySQL DB와 연동을 도와줄 pymysql 라이브러리를 import 하고, root 계정의 password를 변수에 저장합니다.
import pymysql # password 입력 password = '0000'
 
앞서 저장한 password를 이용하여 MySQL DB에 root 계정으로 접속하는 Connection 객체를 생성합니다.
# MySQL Connection conn = pymysql.connect(host = '127.0.0.1', port = 3306, user = 'root', password = password, charset='utf8')
 
Connection 객체의 커서(cusor) 객체를 생성하고, ‘financial_terms’ DB를 생성하는 CREATE문을 실행합니다. 실행한 SQL문을 실제 DB에 적용하기 위해 commit을 실행합니다. with 절을 이용하였기에 Connection과 커서는 모든 작업을 끝낸 후 자동으로 close 됩니다.
# SQL문 실행 - DB 생성 with conn: with conn.cursor() as cur: cur.execute('CREATE DATABASE financial_terms') conn.commit()
 

1.7.2 테이블 생성

앞에서 생성한 ‘financial_terms’ DB에 접속하여 테이블을 생성하고 크롤링 한 데이터를 저장해 보겠습니다.
 
먼저 ‘financial_terms’ DB에 root 계정으로 접속하는 Connection 객체를 생성합니다.
# MySQL DB Connection conn = pymysql.connect(host = '127.0.0.1', port = 3306, user = 'root', password = password, charset='utf8', database='financial_terms
 
아래는 사전의 4개 속성을 저장할 테이블을 생성하는 SQL문입니다. 각 용어를 구분할 수 있는 num 값을 primary key로 설정하고, 용어(term), 영어 단어(term_en)는 varchar형(문자형)으로, 용어의 내용(content)은 대규모 텍스트 데이터 저장이나 긴 텍스트 필드에 사용되는 text형으로 설정하겠습니다.
# 테이블 생성 SQL문 sql = '''create table terms ( num varchar(5) primary key, term varchar(50), term_en varchar(100), content text);'''
 
Connection 객체의 커서(cusor) 객체를 생성하고, ‘financial_terms’ DB에 ‘terms’ 테이블을 생성하는 SQL문을 실행합니다. 실행한 SQL문을 실제 DB에 적용하기 위해 commit을 실행합니다. with 절을 이용하였기에 커서는 SQL문을 실행한 후 자동으로 close 됩니다.
# SQL문 실행 - 테이블 생성 with conn.cursor() as cur: cur.execute(sql) conn.commit()
 

1.7.3 데이터 저장

앞서 생성한 ‘terms’ 테이블에 크롤링 한 데이터를 저장하겠습니다.
 
Connection 객체의 커서(cusor) 객체를 생성하고, INSERT문을 실행하여 각 상품의 12개 속성들을 테이블에 저장합니다. 실행한 SQL문을 영구적으로 저장하기 위해 commit을 실행합니다. with 절을 이용하였기에 커서는 SQL문을 실행한 후 자동으로 close 됩니다.
# SQL문 실행 - 데이터 삽입 with conn.cursor() as cursor: for index, row in df.iterrows(): sql = ''' INSERT INTO terms (num, term, term_en, content) VALUES (%s, %s, %s, %s) ''' values = ( index, row['term'], row['term_en'], row['content'] ) cursor.execute(sql, values) conn.commit()
 
테이블 생성 및 데이터 저장 작업을 모두 마쳤으니 ‘financial_terms’ DB에 연결시켜둔 Connection 객체를 close 합니다.
# MySQL DB Connection 종료 conn.close()
 

1.7.4 MySQL에서 저장된 데이터 확인하기

MySQL Workbench 프로그램을 실행하여 생성한 ‘financial_terms’ DB와 ‘terms’ 테이블과 테이블에 저장된 데이터들을 확인해 보겠습니다.
 
아래 코드를 실행시키면 MySQL DB에 생성되어 있는 모든 Database를 확인할 수 있습니다. 앞서 생성한 ‘financial_terms’ DB가 존재하는 것을 확인할 수 있습니다.
SHOW DATABASES;
➡️
결과
notion imagenotion image
 
아래 코드를 실행시키면 ‘financial_terms’ DB를 선택하여 사용할 수 있습니다.
USE financial_terms;
 
아래 코드를 실행시키면 현재 선택한 DB(’financial_terms’ DB)에 생성되어 있는 모든 테이블을 확인할 수 있습니다. 앞서 생성한 ‘terms’ 테이블이 존재하는 것을 확인할 수 있습니다.
SHOW TABLES;
➡️
결과
notion imagenotion image
 
아래 코드를 실행시키면 INSERT문으로 삽입한 데이터들이 모두 정상적으로 저장된 것을 확인할 수 있습니다.
select * from terms;
➡️
결과
notion imagenotion image
 
 

1.8 DB 활용 - 필터링

 
필터링을 사용하여 DB에 저장한 데이터들에서 조건에 만족하는 제품을 찾아보겠습니다.
 
먼저 ‘financial_terms’ DB에 root 계정으로 접속하는 Connection 객체를 생성합니다.
# MySQL DB Connection conn = pymysql.connect(host = '127.0.0.1', port = 3306, user = 'root', password = password, charset='utf8', database='financial_terms')
 

1.8.1 필터링1 - 채권 관련 용어

용어의 내용에서 “채권”이라는 용어가 들어간 용어를 조회하고, 해당 용어들을 번호 순으로 정렬하는 SQL문입니다.
# 필터링1 조건에 만족하는 데이터를 조회하는 SQL문 query = '''SELECT * FROM terms WHERE content LIKE '%채권%' ORDER BY num;'''
 
Connection 객체의 커서(cusor) 객체를 생성하고, 조건을 만족하는 상품을 조회하는 SQL문을 실행합니다. 조건을 만족하는 상품들을 데이터를 fetchall 메서드를 통해 받아옵니다. with 절을 이용하였기에 커서는 SQL문을 실행한 후 자동으로 close 됩니다.
# SQL문 실행 - 데이터 조회 with conn.cursor() as cursor: cursor.execute(query) result = cursor.fetchall()
 
조건을 만족하는 데이터가 총 몇 개인지 len 메서드를 통해 확인했습니다. 또한 상품의 전체 데이터를 데이터프레임으로 변환하여 확인했습니다.
# 결과 출력 print(f'조건에 일치하는 상품: 총 {len(result)}개') result_df = pd.DataFrame(result, columns=['num', 'term', 'term_en', 'content']) result_df
➡️
결과
notion imagenotion image
 
가장 위에 있는 금융 용어의 content를 출력해서 확인해 보겠습니다.
print(result_df[result_df['num'] == '005']['content'][0])
➡️
결과
감리위원회는 외부감사 및 회계 등과 관련하여 「주식회사의 외부감사에 관한 법률」 및 동법 시행령의 규정과 기타 다른 법령에서 정하고 있는 금융위원회ㆍ증권선물위원회 의 업무를 전문적이고 효율적으로 수행하기 위해 증권선물위원회 내에 두는 전문심의 기구이다. 감리위원회는 ① 증권선물위원회 상임위원, ② 금융위원회 자본시장국장 또는 금융위원회 3급이상 공무원 중에서 금융위원장이 지명하는 자 1명, ③ 금융감독원 전문 심의위원, ④ 한국공인회계사회 회계감사위탁감리위원회위원장, ⑤ 기업회계 또는 회계감 사에 관하여 전문지식과 실무경험이 있는 자로서 한국상장회사협의회 회장이 추천하는 자 1인, ⑥ 기업회계업무에 대한 전문지식을 가진 변호사 1인, ⑦ 채권자 또는 소비자보호 단체 등 회계정보이용자 대표 1인, ⑧ 기업회계와 회계감사 또는 관련 법률 등에 관하여 학식과 경험이 풍부한 자 2인 등 총 9명의 위원으로 구성된다.
 

1.8.2 필터링2 - 이자 관련 용어

용어의 내용에서 “이자”라는 용어가 들어간 용어를 조회하고, 해당 용어들을 번호 순으로 정렬하는 SQL문입니다. 여기서 주의해야 할 점은, 일정한 자격과 함께 다른 자격이 있음을 나타내는 연결 어미 ‘-이자’도 함께 추출되는 것을 방지하여 명사 ‘이자’만 추출되도록 하기 위해 앞에 공백 한 칸을 포함하여 추출해 보도록 하겠습니다.
# 필터링2 조건에 만족하는 데이터를 조회하는 SQL문 query = '''SELECT * FROM terms WHERE content LIKE '% 이자%' # 명사 '이자'만 추출 ORDER BY num;'''
 
Connection 객체의 커서(cusor) 객체를 생성하고, 조건을 만족하는 상품을 조회하는 SQL문을 실행합니다. 조건을 만족하는 상품들을 데이터를 fetchall 메서드를 통해 받아옵니다. with 절을 이용하였기에 커서는 SQL문을 실행한 후 자동으로 close 됩니다.
# SQL문 실행 - 데이터 조회 with conn.cursor() as cursor: cursor.execute(query) result = cursor.fetchall()
 
조건을 만족하는 데이터가 총 몇 개인지 len 메서드를 통해 확인했습니다. 또한 상품의 전체 데이터를 데이터프레임으로 변환하여 확인했습니다.
# 결과 출력 print(f'조건에 일치하는 상품: 총 {len(result)}개') result_df = pd.DataFrame(result, columns=['num', 'term', 'term_en', 'content']) result_df
➡️
결과
notion imagenotion image
 
가장 위에 있는 금융 용어의 content를 출력해서 확인해 보겠습니다.
print(result_df[result_df['num'] == '118']['content'][0])
➡️
결과
대부업의 등록 및 감독에 관하여 필요한 사항을 규정하고, 대부업자와 여신금융기관의 이자율 제한, 불법적 채권추심행위 등을 규제하는 법률로서 대부업의 건전한 발전을 도모하고 대부업자 및 여신금융기관의 거래상대방을 보호하는 것을 목적으로 2002년 8월26일 최초로 제정되어 시행되고 있다. 동법은 ①대부업 등의 정의에 관한 사항, ②대부업의 등록에 관한 사항, ③대부계약의 체결에 관한 사항, ④이자율의 제한에 관한 사항, ⑤대부업자의 검사에 관한 사항, ⑥행정조치에 관한 사항, ⑦벌칙 및 과태료에 관한 사항 등을 규정하고 있다. 동법 제정 이후 나타난 미비점을 보완하고 미등록 대부업체에 의한 불법적인 영업행태 및 불법적 채권추심행위의 근절 등을 위해 필요한 규제를 강화 하고 대부업의 양성화를 지속적으로 추진하기 위하여 2016.7.25. 대형 대부업자를 중심으로 금융위원회에 등록하도록 등록 및 감독체계를 개편하였고, 등록요건에 재정적 건전성을 확인할 수 있는 기준을 마련하며, 대부업자 등의 내부통제를 강화하기 위하여 대부업 이용자 보호기준을 마련하도록 하였으며, 대기업 계열사의 우회지원을 방지하기 위해 신용공여한도를 설정하면서, 이를 위반시 부당이득에 대한 과징금을 부과하고, 과도한 대부채권추심을 예방하고 대부업 이용자를 보호하기 위한 손해배상 보장 제도 등를 도입하였다.
 
데이터 조회 작업을 모두 마쳤으니 ‘financial_terms’ DB에 연결해둔 Connection 객체를 close 합니다.
# MySQL DB Connection 종료 conn.close()