Nathaniel

8. Langchain Document_Loader, Parser 본문

AI

8. Langchain Document_Loader, Parser

Nathaniel1 2025. 3. 19. 13:56

Base Loader 받는 방법에 대해서 공부를 해야 하는데 그 부분은 나중에 다뤄볼 예정이다.

 

Document_Loader에서 가장 많이 사용하는 함수는 load()가 되는데, 문서를 로드해서 반환하는 List[Document]형태이다.

Data Type은 List이고, 로드된 문서 페이지 수와 로드한 문서의 페이지의 객체를 확인할 수 있다( metadata도 같이)

 

 load()

예시 사항은 아래 코드 및 출력을 확인해보면 알 수 있다.

from langchain_core.documents import Document

document = Document(page_content="안녕하세요? 이건 랭체인의 도큐먼드 입니다")

# 도큐먼트의 속성 확인
document.__dict__

# 출력
{'id': None,
 'metadata': {},
 'page_content': '안녕하세요? 이건 랭체인의 도큐먼드 입니다',
 'type': 'Document'}
# 메타데이터 추가
document.metadata["source"] = "Natty"
document.metadata["page"] = 1
document.metadata["author"] = "Natty"

# 도큐먼트의 속성 확인
document.metadata

# 출력
{'source': 'Natty', 'page': 1, 'author': 'Natty'}

 

# 예제 파일 경로
FILE_PATH = "./data/SPRI_AI_Brief_2023년12월호_F.pdf"

from langchain_community.document_loaders import PyPDFLoader, PDFPlumberLoader

# 로더 설정
loader = PyPDFLoader(FILE_PATH)

# PDF 로더
docs = loader.load()

# 로드된 문서의 수 확인
len(docs)

# 출력
23

# 10번 째 문서 확인
print(docs[10].metadata)

# 출력
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4',
'source': './data/SPRI_AI_Brief_2023년12월호_F.pdf', 'total_pages': 23, 'page': 10, 'page_label': '11'}

 

load_and_split()

그리고 많이 사용하는 함수는 load_and_split()인데, 문서 내부의 글자를 splitter로 사용해서 문서를 분할하고 반환하는 데

사용한다.

 

앞서, D1 코드 외우기 글에서 기본 RAG를 기준으로 사용하는 코드를 쓴 적이 있는데 그 부분에 사용되는 RecursiveCharacterTextSplitter라는 클래스이다. 이 클래스는 문서를 로드하면서 문서 내부에 있는 문자들을 chunking을 하는데, 이 Chunking은 문자 단위 한 글자씩 쪼개면서 해당 내용을 Vector 값을 주고 난 후 database에 저장하는 가장 중요한 부분이다. overlap은 하나의 문서를 chunk 단위로 자르면 그 다음 줄의 내용을 chunk로 자르지만, 나중에 내가 LLM을 사용해서 유사도 검색이라는 것을 했을 때 잘 검색하지 못하는 경우가 있다.

 

1. chunk_size : 문서를 몇 개의 토큰 단위로 나눌 것인지 정함

2. chunk_overlap : 분할된 끝 부분에서 맥락이 이어질 수 있도록 일부를 겹쳐서(overlap)분할하는 것

 

아래와 같은 개념이다. 네모 박스 친 문장을 chunk_size로 두었다면, 그다음 overlap은 네모 박스 마지막 문단을 다시한번 포함해서 그 뒤의 내용을 감싼다.

chunk, overlap

load()와 다르게 loader_and_splitter()는 문서를 chunking하기 때문에 문서의 갯수가 많아진다.

from langchain_text_splitters import RecursiveCharacterTextSplitter

# 문열 분할기 설정
text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=0)

# 예제 파일 경로
FILE_PATH = "./data/OOO.pdf"

# 로더 설정
loader = PyPDFLoader(FILE_PATH)

# 문서 분할
split_docs = loader.load_and_split(text_splitter=text_splitter)

# 로드된 문서의 수 확인
print(f"문서의 길이: {len(split_docs)}")

# 첫번째 문서 확인
split_docs[10]

lazy_load()

가 있는데, 이거는 문서의 양이 엄청 방대할 때 ex)100만 page라면  Memory에 올리면 부하가 심하지만, lazy_load() 함수를 사용해서 문서를 업로드하면 100만 페이지를 전부 읽고 즉시 버리면서 메모리 부하가 걸리지 않는다. 문서의 양이 많을 때, 사용하면 좋을 것 같다.

PDF 문서의 페이지는 총 23페이지로 metadata를 출력하면 아래와 같이 나오게 되는 것을 볼 수 있다.

loader.lazy_load()

# generator 방식으로 문서 로드
for doc in loader.lazy_load():
    print(doc.metadata)
    
# 출력

{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 0, 'page_label': '1'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 1, 'page_label': '2'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 2, 'page_label': '3'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 3, 'page_label': '4'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 4, 'page_label': '5'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 5, 'page_label': '6'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 6, 'page_label': '7'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 7, 'page_label': '8'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 8, 'page_label': '9'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 9, 'page_label': '10'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 10, 'page_label': '11'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 11, 'page_label': '12'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 12, 'page_label': '13'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 13, 'page_label': '14'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 14, 'page_label': '15'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 15, 'page_label': '16'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 16, 'page_label': '17'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 17, 'page_label': '18'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 18, 'page_label': '19'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 19, 'page_label': '20'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 20, 'page_label': '21'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 21, 'page_label': '22'}
{'producer': 'Hancom PDF 1.3.0.542', 'creator': 'Hwp 2018 10.0.0.13462', 'creationdate': '2023-12-08T13:28:38+09:00', 'author': 'dj', 'moddate': '2023-12-08T13:28:38+09:00', 'pdfversion': '1.4', 'source': './data/ooo.pdf', 'total_pages': 23, 'page': 22, 'page_label': '23'}

 

alod() 비동기 방식의 문서 로드

서비스 개발할 때 비동기 방식으로 문서를 로드해야할 때가 있으니 그때 사용하면 된다.

# 문서를 async 방식으로 로드
adocs = loader.aload()

# 문서 로드
await adocs

 

PyPDF(OCR)

일부 PDF에 스캔된 문서나 그림 내 텍스트 이미지가 포함되어 있다면, `rapidocr-onnxruntime`패키지를 사용해, 이미지에서 텍스트를 추출할 수도 있다.

 

간혹 이미지 내부에 있는 글자 텍스트를 추출하고 싶을 때가 있는데 그럴 때 사용해도 좋고 아니면 그냥 사진 내부에서의 글자를 OCR 처리해서 가져올 수 있으니 그럴 때 사용해보면 좋을 것 같다. Yolo랑 컬레보레이션을 한다면 그때 사용해도 괜찮을 클래스같다.

 

# 설치
# !pip install -qU rapidocr-onnxruntime

# PDF 로더 초기화, 이미지 추출 옵션 활성화
loader = PyPDFLoader("https://arxiv.org/pdf/2103.15348.pdf", extract_images=True)

# PDF 페이지 로드
docs = loader.load()

# 페이지 내용 접근
print(docs[4].page_content[:300])

 

아래 코드는 OCR 처리한 문서의 메타 데이터 출력 결과를 볼 수 있다.

# OCR처리한 문서의 메타 데이터 출력
show_metadata(docs)

# 출력
[metadata]
['producer', 'creator', 'creationdate', 'author', 'keywords', 'moddate', 'ptex.fullbanner', 'subject', 'title', 'trapped', 'source', 'total_pages', 'page', 'page_label']

[examples]
producer        : pdfTeX-1.40.21
creator         : LaTeX with hyperref
creationdate    : 2021-06-22T01:27:10+00:00
author          : 
keywords        : 
moddate         : 2021-06-22T01:27:10+00:00
ptex.fullbanner : This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) kpathsea version 6.3.2
subject         : 
title           : 
trapped         : /False
source          : https://arxiv.org/pdf/2103.15348.pdf
total_pages     : 16
page            : 0
page_label      : 1

 

PyPDFDirectoryLoader

이 클래스는 굉장히 좋은 게 나의 data 파일 내부에 있는 PDF 문서 전체를 한번에 로드를 해서 문서 내부의

metadata, 문서 개수를 전부 다 확인할 수 있어서 좋다. 사용할 때는 PDF 문서가 엄청 많아서 한번에 로드를하고 그 내부에 있는 중요 문서 내용들을 확인할 때 굉장히 유용하다.

from langchain_community.document_loaders import PyPDFDirectoryLoader

# 디렉토리 경로
loader = PyPDFDirectoryLoader("data/")

# 문서 로드
docs = loader.load()

# 문서의 개수 출력
print(len(docs))

# 출력
23

# 문서의 내용 출력
print(docs[20].page_content[:300])

 

PDFPlumberLoader

이거는 개인적으로 많이 사용하는 Loader이다. Teddynote님도 그렇고 AutoRAG라는 팀에서도 테스트 했을 때, PDFPlumberLoader가 좋다고 했다. 물론 순위도 높다.

아래는 Loader의 순위별 테스트 결과를 사진으로 가져온 것인데. AutoRAG팀에서 결과 낸 Loader이다.

 

출처 : https://velog.io/@autorag/PDF-%ED%95%9C%EA%B8%80-%ED%85%8D%EC%8A%A4%ED%8A%B8-%EC%B6%94%EC%B6%9C-%EC%8B%A4%ED%97%98#%EC%B4%9D%ED%8F%89

 

PDF 한글 텍스트 추출 실험

PDF 파서 중 어떤 것이 한글 PDF에 가장 좋을까?

velog.io

 

PDFPlumberLoader는 장점으로 bounding box를 만들어서 문서의 세부 내용을 전처리 할 수 있다.

하지만 PDFPlumberPython Package를 설치해서 bounding box를 만들어서 처리할 수 있다는 점이 있으니 인지해야한다.

from langchain_community.document_loaders import PDFPlumberLoader

# PDF 문서 로더 인스턴스 생성
loader = PDFPlumberLoader(FILE_PATH)

# 문서 로딩
docs = loader.load()

# 첫 번째 문서 데이터 접근
print(docs[10].page_content[:300])

 

CSVLoader

CSV는 다들 알다시피 Comma, Tab으로 구분되어 Column,  Row로 큰 데이터들을 보기위한 텍스트 파일인데

PDF 형식 말고도, CSV형식으로도 가능하다는 것을 볼 수 있는데, CSV 파일 형식으로 데이터를 받았을 때에 대한 문서 로드가 반드시 있고, 해당 분야에서 CSV 파일 업로드를 하는 경우가 있기 때문에 이렇게 사용하는 것도 굉장히 좋다고 생각한다.

 

from langchain_community.document_loaders.csv_loader import CSVLoader

# CSV 로더 생성
loader = CSVLoader(file_path="./data/titanic.csv")

# 데이터 로드
docs = loader.load()

print(len(docs))
print(docs[0].metadata)


# 출력
891
{'source': './data/titanic.csv', 'row': 0}

 

metadata를 출력하고 그 외 csv 문서에서 1페이지에 해당하는 column값들도 간편하게 볼 수 있어서 좋다.

print(docs[1].page_content)

# 출력
PassengerId: 2
Survived: 1
Pclass: 1
Name: Cumings, Mrs. John Bradley (Florence Briggs Thayer)
Sex: female
Age: 38
SibSp: 1
Parch: 0
Ticket: PC 17599
Fare: 71.2833
Cabin: C85
Embarked: C

 

# 컬럼정보:
# PassengerId,Survived,Pclass,Name,Sex,Age,SibSp,Parch,Ticket,Fare,Cabin,Embarked

# CSV 파일 경로
loader = CSVLoader(
    file_path="./data/titanic.csv",
    csv_args={
        "delimiter": ",",  # 구분자
        "quotechar": '"',  # 인용 부호 문자
        "fieldnames": [
            "Passenger ID",
            "Survival (1: Survived, 0: Died)",
            "Passenger Class",
            "Name",
            "Sex",
            "Age",
            # "Number of Siblings/Spouses Aboard",
            "Number of Parents/Children Aboard",
            "Ticket Number",
            "Fare",
            "Cabin",
            "Port of Embarkation",
        ],  # 필드 이름
    },
)

# 데이터 로드
docs = loader.load()

# 데이터 출력
print(docs[1].page_content)



# 출력
Passenger ID: 1
Survival (1: Survived, 0: Died): 0
Passenger Class: 3
Name: Braund, Mr. Owen Harris
Sex: male
Age: 22
Number of Parents/Children Aboard: 1
Ticket Number: 0
Fare: A/5 21171
Cabin: 7.25
Port of Embarkation: 
None: S

 

XML 문서 형식으로 처리할 때도 사용하는 방식이 있는데 XML은 태그 형식으로 데이터를 감싸서 보기 때문에 좀 더 보기 수월하다고 생각하고. LLM한테 문서를 로드해줘야 하는데 XML 형식은 태그로 데이터를 감싸기 때문에 좀 더 명확하게 데이터를 저장시킬 수 있는 중요한 전처리 방식이기 때문에 이 방법도 고수하면 오히려 더 좋다고 생각한다.

명확하게 데이터를 지정해야 그만큼 유사 검색할 때도 찾기 쉽기 때문이다.

row = docs[1].page_content.split("\n")
row_str = "<row>"
for element in row:
    splitted_element = element.split(":")
    value = splitted_element[-1]
    col = ":".join(splitted_element[:-1])
    row_str += f"<{col}>{value.strip()}</{col}>"
row_str += "</row>"
print(row_str)

# 출력

<row><Passenger ID>1</Passenger ID><Survival (1: Survived, 0: Died)>0</Survival (1: Survived, 0: Died)><Passenger Class>3</Passenger Class><Name>Braund, Mr. Owen Harris</Name>
<Sex>male</Sex><Age>22</Age><Number of Parents/Children Aboard>1</Number of Parents/Children Aboard><Ticket Number>0</Ticket Number><Fare>A/5 21171</Fare><Cabin>7.25</Cabin>
<Port of Embarkation></Port of Embarkation><None>S</None></row>

 

WebBaseLoader

웹 크롤링으로 하는 Loader인데, 웹 크롤링으로 유명한 모듈 Beautiful Soup으로 웹 페이지를 파싱하는 방법도 있다.

아마 나는 잘 사용하지는 않은 것 같은데 그래도 알고 있으면 나중에 유용하게 쓸 수 있다고 생각한다.

 

url 주소는 튜플 형식으로 되어있기 때문에 여러 개의 웹 기사를 넣어도 잘 파싱한다!!

그리고 "User_Agent":에 대한 값도 명확하게 넣어야 warnign이 안 뜨고 코드가 잘 동작한다.(GPT, Grok3를 사용할 것)

import bs4
from langchain_community.document_loaders import WebBaseLoader

# 뉴스기사 내용을 로드합니다.
loader = WebBaseLoader(
    web_paths=("https://n.news.naver.com/article/437/0000378416",),
    bs_kwargs=dict(
        parse_only=bs4.SoupStrainer(
            "div",
            attrs={"class": ["newsct_article _article_body", "media_end_head_title"]},
        )
    ),
    header_template={
        "User_Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36",
    },
)

docs = loader.load()
print(f"문서의 수: {len(docs)}")
docs

# 출력
USER_AGENT environment variable not set, consider setting it to identify your requests.
문서의 수: 1

[Document(metadata={'source': 'https://n.news.naver.com/article/437/0000378416'},
page_content="\n출산 직원에게 '1억원' 쏜다…회사의 파격적 저출생 정책\n\n\n[앵커]올해 아이 낳을 계획이 있는 가족이라면 솔깃할 소식입니다. 정부가 
저출생 대책으로 매달 주는 부모 급여, 0세 아이는 100만원으로 올렸습니다. 여기에 첫만남이용권, 아동수당까지 더하면 아이 돌까지 1년 동안 1520만원을 받습니다. 지자체도 경쟁하듯 지원에 나섰습니다. 인천시는 새로 태어난 아기,
18살될 때까지 1억원을 주겠다. 광주시도 17살될 때까지 7400만원 주겠다고 했습니다. 선거 때면 나타나서 아이 낳으면 현금 주겠다고 밝힌 사람이 있었죠. 과거에는 표만 노린 '황당 공약'이라는 비판이 따라다녔습니다. 그런데 지금은 출산율이 이보다 더 나쁠 수 없다보니, 
이런 현금성 지원을 진지하게 정책화 하는 상황까지 온 겁니다. 게다가 기업들도 뛰어들고 있습니다. 이번에는 출산한 직원에게 단번에 1억원을 주겠다는 회사까지 나타났습니다.이상화 기자가 취재했습니다.[기자]한 그룹사가 오늘 파격적인 저출생 정책을 내놨습니다.
2021년 이후 태어난 직원 자녀에 1억원씩, 총 70억원을 지원하고 앞으로도 이 정책을 이어가기로 했습니다.해당 기간에 연년생과 쌍둥이 자녀가 있으면 총 2억원을 받게 됩니다.[오현석/부영그룹 직원 : 아이 키우는 데 금전적으로 많이 힘든 세상이잖아요. 
교육이나 생활하는 데 큰 도움이 될 거라 생각합니다.]만약 셋째까지 낳는 경우엔 국민주택을 제공하겠다는 뜻도 밝혔습니다.[이중근/부영그룹 회장 : 3년 이내에 세 아이를 갖는 분이 나올 것이고 따라서 주택을 제공할 수 있는 계기가 될 것으로 생각하고.]
[조용현/부영그룹 직원 : 와이프가 셋째도 갖고 싶어 했는데 경제적 부담 때문에 부정적이었거든요. (이제) 긍정적으로 생각할 수 있을 것 같습니다.]오늘 행사에서는, 회사가 제공하는 출산장려금은 받는 직원들의 세금 부담을 고려해 정부가 면세해달라는 제안도 나왔습니다.
이같은 출산장려책은 점점 확산하는 분위기입니다.법정기간보다 육아휴직을 길게 주거나, 남성 직원의 육아휴직을 의무화한 곳도 있습니다.사내 어린이집을 밤 10시까지 운영하고 셋째를 낳으면 무조건 승진시켜 주기도 합니다.한 회사는 지난해 네쌍둥이를 낳은 직원에 의료비를 지원해 관심을 모았습니다.
정부 대신 회사가 나서는 출산장려책이 사회적 분위기를 바꿀 거라는 기대가 커지는 가운데, 여력이 부족한 중소지원이 필요하다는 목소리도 나옵니다.[영상디자인 곽세미]\n\t\t\n")]

 

위 코드에서 자세히 보아야 할 것은, bs_kwargs에서 attrs 속성의 class인데 내가 원하는 기사 문에서 Ctrl+Shift+C 키를 눌러서 기사문의 제목을 눌렀을 때 감싸고 있는 <div class="media end_head_title"> media end_head_title을 

위 코드 내 "class":["media end_head_title"]를 써야 문서 제목을 출력한다.

 

 

원하는 내용의 문서는 기사 내용을 선택한 후, <div id>태그에 있는 class="" 내부에 빨간줄로 그어있는 이름을 복사해서 class코드에 넣어야 기사 문서를 파싱해서 출력하는 것을 확인 가능하다.

 

※ 참고 : 네이버의 기사의 div 태그 이름들은 크게 변하지 않아서 네이버의 기사를 크롤링 할 때는 편하게 사용할 수 있다.

 

스크래핑 속도를 높여서 사용하는 방법이 있긴 한데, Default값 초당 2회이며, 서버 부하에 대해 걱정하지 않는 스크래핑 서버 제어라면 Requests_per_second 매개변수를 변경해서 최대 동시 요청 수를 늘릴 수 있다.

 

하지만 이 방법은 스크래핑 속도를 높일 수 있지만 서버로부터 차단될 수 있다......... ㅋㅋㅋ

일전에 AI 교육 받을 때 동기생 중에 웹크롤링으로 스크래핑을 과도하게 요청해서 서버로부터 ban을 당한 사람이 있었다.

# jupyter notebook 에서만 실행(asyncio)
import nest_asyncio

nest_asyncio.apply()


# 초당 요청 수 설정
loader.requests_per_second = 1

# 비동기 로드
docs = loader.aload()

 

간혹 IP 차단을 우회할 때 프록시를 사용하기도 하는데 프록시를 사용하면 로더에 프록시 딕셔너리를 전달할 수 있다.

※ 아래 코드 참고

loader = WebBaseLoader(
    "https://www.google.com/search?q=parrots",
    proxies={
        "http": "http://{username}:{password}:@proxy.service.com:6666/",
        "https": "https://{username}:{password}:@proxy.service.com:6666/",
    },
    # 웹 기반 로더 초기화
    # 프록시 설정
)

# 문서 로드
docs = loader.load()

 

이 외에 Langchain Loader 관련 Docs 문서를 찾아보면 Loader의 종류는 엄청나게 많다.

상황에 따라 주어진 부분에서 reference보고 사용하면 유용할 것 같다!!!!!!

 

 

 + 추가적으로 Llamaparser를 사용해서 OCR기능을 활용하면 할루시네이션을 겪지 않고 이미지나, 문서의 차트를 잘 읽어들여서 글자로 출력하는 것을 볼 수 있는데 굉장히 유용하니 한번쯤 사용해보면 좋을 것 같다.

 

https://cloud.llamaindex.ai

 

LlamaCloud

LlamaCloud is a new generation of managed parsing, ingestion, and retrieval services, designed to bring production-grade context-augmentation to your LLM and RAG applications.

cloud.llamaindex.ai

Llama Parser

아래의 코드에 별도의 문서를 로드한 후, Documents를 LlamaParser로 지정하고 변수값들을 넣어 출력했다.

Llama Parser가 MultiModal을 지원하는데 prompt를 작성하면 input에 맞는 내용을 잘 가져올 수 있다.

출력하는 부분은 문서 내에서 표를 읽어들이는지 보려고 확인했는데, 굉장히 잘 나오는 것을 볼 수 있었다!!!!

# 설치
# !pip install llama-index-core llama-parse llama-index-readers-file python-dotenv
from llama_parse import LlamaParse
from llama_index.core import SimpleDirectoryReader



# SimpleDirectoryReader를 사용하여 파일 파싱
file_extractor = {".pdf": parser}

# 파서 설정
documents = LlamaParse(
    use_vendor_multimodal_model=True,
    vendor_multimodal_model_name="openai-gpt4o",
    vendor_multimodal_api_key=os.environ["OPENAI_API_KEY"],
    result_type="markdown",
    language="ko",
    # skip_diagonal_text=True,
    # page_separator="\n=================\n"
)

# parsing 된 결과
parsed_docs = documents.load_data(file_path="data/OOO.pdf")

# langchain 도큐먼트로 변환
docs = [doc.to_langchain_format() for doc in parsed_docs]

print(docs[18].page_content)

# 출력

### (구글 딥마인드의 범용 AI 분류 프레임워크)

| 성능  | 특수 AI 예시 | 범용 AI 예시 |
|-------|--------------|--------------|
| 0단계: AI 아님 | 계산기, 스프레드시트, 엑셀 | 아마존 메카니컬 터크 |
| 1단계: 신진(초보단계 인간) | GOFAI(Good Old Fashioned Artificial Intelligence) | 챗GPT, 바드, 라마2 |
| 2단계: 유능(숙련된 인간의 50% 이상) | 스마트 스피커(애플 시리, 아마존 알렉사, 구글 어시스턴트), IBM 왓슨 | 미달성 |
| 3단계: 전문가(숙련된 인간의 90% 이상) | 로봇 조작(자율주행), 생성 이미지(딥페이크) | 미달성 |
| 4단계: 최선(숙련된 인간의 99% 이상) | 딥블루, 알파고 | 미달성 |
| 5단계: 초인간(인간 100% 능가) | 알파폴드, 알파제로, 스톡피시 | 미달성 |

출처: Arxiv.org, Levels of AGI: Operationalizing Progress on the Path to AGI, 2023.11.04.

 

문서 내 표

Llama Parser Multimodal Prompt 코드

Parsing_instruction에 "You are parsing a brief of AI Report. Please extract tables in markdown format"이라고 지정하고

Parser 값 지정하면 된다!!!

# parsing instruction 을 지정합니다.
parsing_instruction = (
    "You are parsing a brief of AI Report. Please extract tables in markdown format."
)

# LlamaParse 설정
parser = LlamaParse(
    use_vendor_multimodal_model=True,
    vendor_multimodal_model_name="openai-gpt4o",
    vendor_multimodal_api_key=os.environ["OPENAI_API_KEY"],
    result_type="markdown",
    language="ko",
    parsing_instruction=parsing_instruction,
)

# parsing 된 결과
parsed_docs = parser.load_data(file_path="data/OOO.pdf")

# langchain 도큐먼트로 변환
docs = [doc.to_langchain_format() for doc in parsed_docs]

# markdown 형식으로 추출된 테이블 확인
print(docs[-3].page_content)

 

 

 

 

 

'AI' 카테고리의 다른 글

10. Langchain-Embeddings  (1) 2025.03.24
9. Langchain-RecursiveCharacterTextSplitter  (0) 2025.03.22
7. Langchain 캐싱(API 호출 비용 감소)  (0) 2025.03.17
6. Langchain Parser??  (0) 2025.03.11
5. Lagnchain-prompt-template 생성  (0) 2025.03.07