!pip install pymed
!pip install pandas requests
# ===== 필수 라이브러리 임포트 =====
# Google의 생성형 AI API - 텍스트 분석용
import google.generativeai as genai
# HTTP 요청 처리용 - API 통신에 필요
import requests
# 데이터프레임 처리용 - 결과 데이터 구조화
import pandas as pd
# 주피터 노트북 UI 구성요소 - 대화형 인터페이스 구현
import ipywidgets as widgets
# 주피터 노트북 출력 제어 - 결과 표시 관리
from IPython.display import display, clear_output
# 시간 측정 및 API 요청 간 지연용
import time
# 타임스탬프 생성용 - 파일명에 사용
from datetime import datetime
# CrossRef API 클라이언트 - 논문 메타데이터 검색용
from habanero import Crossref
# 정규표현식 - 문자열 패턴 매칭 및 처리용
import re
# ===== API 설정 =====
# Google AI API 인증 설정
genai.configure(api_key='제미나이 API key 입력')
# Gemini Pro 모델 인스턴스 생성
model = genai.GenerativeModel('gemini-pro')
def extract_titles(input_text):
"""
입력된 텍스트에서 논문 제목을 추출하는 함수
작동 방식:
1. Gemini 모델에 프롬프트를 전송하여 논문 제목 추출 요청
2. 응답에서 번호가 매겨진 줄만 선택
3. 번호를 제거하고 실제 제목만 추출
Args:
input_text (str): 논문 제목을 포함하는 원문 텍스트
Returns:
list: 추출된 논문 제목들의 리스트
"""
# AI 모델용 프롬프트 구성 - 명확한 역할과 출력 형식 지정
prompt = """
* 역할: 텍스트에서 학술 논문 제목만 추출
* 추출 기준: 일반 텍스트나 다른 정보는 무시하고 논문 제목만 추출
* 출력 형식: 각 줄에 번호를 매겨 출력
* 텍스트: """ + input_text
# AI 모델로 텍스트 분석 실행
response = model.generate_content(prompt)
# 응답 텍스트 처리:
# 1. 줄 단위로 분리
# 2. 숫자가 포함된 줄만 선택 (번호가 매겨진 제목)
# 3. 번호와 구분자('. ') 제거
titles = [line.split('. ', 1)[1] if '. ' in line else line
for line in response.text.split('\n')
if line.strip() and any(c.isdigit() for c in line)]
return titles
def clean_abstract(abstract):
"""
논문 초록에서 XML/HTML 태그를 제거하고 정제하는 함수
처리 내용:
1. null/None 체크
2. JATS 형식의 제목 태그 제거
3. 기타 JATS 태그 제거
4. 앞뒤 공백 제거
Args:
abstract (str): 원본 초록 텍스트 (태그 포함)
Returns:
str: 정제된 초록 텍스트 또는 'Not found'
"""
# 초록이 없는 경우 처리
if not abstract:
return 'Not found'
# <jats:title>과 </jats:title> 사이의 모든 내용 제거
abstract = re.sub(r'<jats:title>.*?</jats:title>', '', abstract)
# 남은 모든 JATS 태그 제거 (<jats:로 시작하는 모든 태그)
abstract = re.sub(r'<jats:[^>]*>', '', abstract)
# 앞뒤 공백 제거 후 반환
return abstract.strip()
def create_not_found_entry(title):
"""
검색 결과가 없는 논문을 위한 기본 데이터 엔트리 생성
Args:
title (str): 검색했던 논문 제목
Returns:
dict: 모든 필드가 'Not found'로 설정된 논문 정보 딕셔너리
"""
return {
'Title': title,
'Abstract': 'Not found',
'Journal': 'Not found',
'Year': 'Not found',
'DOI': 'Not found',
'Authors': 'Not found'
}
def create_error_entry(title, error_msg):
"""
에러 발생 시 에러 정보를 포함한 데이터 엔트리 생성
Args:
title (str): 처리 중이던 논문 제목
error_msg (str): 발생한 에러 메시지
Returns:
dict: 모든 필드가 'Error'로 설정되고 초록에 에러 메시지가 포함된 딕셔너리
"""
return {
'Title': title,
'Abstract': f'Error: {error_msg}',
'Journal': 'Error',
'Year': 'Error',
'DOI': 'Error',
'Authors': 'Error'
}
def fetch_paper_info(titles):
"""
CrossRef API를 사용하여 논문 정보를 검색하는 함수
처리 과정:
1. 각 제목에 대해 CrossRef API 검색 실행
2. 검색 결과에서 필요한 정보 추출
3. 에러 처리 및 결과 없음 처리
4. API 요청 간 지연 적용
Args:
titles (list): 검색할 논문 제목들의 리스트
Returns:
pandas.DataFrame: 검색된 모든 논문 정보가 담긴 데이터프레임
"""
# CrossRef API 클라이언트 초기화
cr = Crossref()
# 검색 결과를 저장할 리스트
papers_info = []
# 각 논문 제목에 대해 검색 실행
for title in titles:
try:
# CrossRef API로 논문 검색 (최대 1개 결과)
works = cr.works(query=title, limit=1)
# 검색 결과가 있는 경우
if works['message']['items']:
paper = works['message']['items'][0]
# 논문 정보를 구조화하여 저장
papers_info.append({
'Title': title,
# 초록 정제하여 저장
'Abstract': clean_abstract(paper.get('abstract', 'Not found')),
# 저널명 (없으면 'Not found')
'Journal': paper.get('container-title', ['Not found'])[0],
# 출판 연도 (복잡한 중첩 구조에서 추출)
'Year': paper.get('published-print', {}).get('date-parts', [['']])[0][0],
# DOI (논문 고유 식별자)
'DOI': paper.get('DOI', 'Not found'),
# 저자명들을 쉼표로 구분하여 저장
'Authors': ', '.join([f"{a.get('given', '')} {a.get('family', '')}"
for a in paper.get('author', [])])
})
else:
# 검색 결과가 없는 경우
papers_info.append(create_not_found_entry(title))
# API 요청 간 1초 지연 (API 제한 준수)
time.sleep(1)
except Exception as e:
# 검색 중 에러 발생 시 에러 정보 저장
papers_info.append(create_error_entry(title, str(e)))
# 수집된 정보를 데이터프레임으로 변환하여 반환
return pd.DataFrame(papers_info)
def on_submit(button):
"""
검색 버튼 클릭 시 실행되는 콜백 함수
처리 과정:
1. 입력 텍스트에서 논문 제목 추출
2. 각 제목에 대해 논문 정보 검색
3. 결과를 CSV 파일로 저장
4. 실행 시간 계산 및 결과 표시
Args:
button: 클릭된 버튼 위젯 객체
"""
with output:
# 이전 출력 내용 삭제
clear_output()
# 시작 시간 기록
start_time = time.time()
# 1단계: 논문 제목 추출
print("논문 제목 추출 중...")
titles = extract_titles(text_input.value)
print("\n추출된 논문 제목:")
for i, title in enumerate(titles, 1):
print(f"{i}. {title}")
# 2단계: 논문 정보 검색
print("\n논문 정보 검색 중...")
df = fetch_paper_info(titles)
# 3단계: 결과 파일 저장
# 현재 시간을 파일명에 포함
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f'paper_info_{timestamp}.csv'
# UTF-8 BOM 인코딩으로 저장 (Excel 한글 호환)
df.to_csv(filename, index=False, encoding='utf-8-sig')
# 4단계: 실행 시간 계산
end_time = time.time()
elapsed = end_time - start_time
minutes = int(elapsed // 60)
seconds = int(elapsed % 60)
# 5단계: 결과 출력
print(f"\n소요 시간: {minutes}분 {seconds}초")
print(f"데이터가 {filename}로 저장되었습니다.")
display(df)
# ===== UI 구성 =====
# 텍스트 입력 영역 생성
text_input = widgets.Textarea(
placeholder='웹페이지 텍스트를 붙여넣으세요...',
layout={'width': '50%', 'height': '100px'}
)
# 검색 버튼 생성
submit_button = widgets.Button(description='논문 정보 검색')
# 버튼 클릭 시 on_submit 함수 실행되도록 설정
submit_button.on_click(on_submit)
# 결과 출력 영역 생성
output = widgets.Output()
# UI 컴포넌트들을 화면에 표시
display(text_input)
display(submit_button)
display(output)