Every retrieval pipeline starts with real, raw content sitting somewhere โ a folder of text files, a PDF, a web page, a spreadsheet. Document loaders are LangChainโs answer to โhow do I get that content into a form the rest of the pipeline can actually use.โ
The Document object, properly explained
Every loader, regardless of source, produces the same output shape:
from langchain_core.documents import Document
doc = Document(
page_content="Our return policy allows returns within 30 days of purchase.",
metadata={"source": "policies.txt", "category": "returns"},
)
print(doc.page_content)
print(doc.metadata)
page_content is the actual text. metadata is a dictionary of anything else worth knowing about where this content came from โ its source file, a page number, an author, a date. This matters more than it might seem: metadata is what lets you later filter retrieval results (โonly search documents from this categoryโ), or show a user exactly which source an answer came from โ a real, practical need in genuinely trustworthy RAG applications.
Example 1: loading a plain text file
from langchain_community.document_loaders import TextLoader
loader = TextLoader("policies.txt")
documents = loader.load()
print(len(documents))
print(documents[0].page_content[:100])
print(documents[0].metadata)
documents[0].metadata already contains {"source": "policies.txt"}, filled in automatically โ the loader knows where the content came from without you specifying it yourself.
Example 2: loading a PDF
from langchain_community.document_loaders import PyPDFLoader
loader = PyPDFLoader("employee_handbook.pdf")
documents = loader.load()
print(f"Loaded {len(documents)} pages.")
print(documents[0].metadata)
Notice this typically produces one Document per page, not one giant document for the whole PDF โ each pageโs metadata includes its page number, genuinely useful later when you want to tell a user exactly which page an answerโs source came from.
Example 3: loading a web page
from langchain_community.document_loaders import WebBaseLoader
loader = WebBaseLoader("https://example.com/faq")
documents = loader.load()
print(documents[0].page_content[:200])
print(documents[0].metadata)
This fetches the live page and extracts its readable text content โ genuinely useful for building a RAG system over your own websiteโs public documentation or FAQ.
Example 4: loading a CSV
from langchain_community.document_loaders import CSVLoader
loader = CSVLoader("products.csv")
documents = loader.load()
print(f"Loaded {len(documents)} rows.")
print(documents[0].page_content)
Each row of the CSV becomes its own Document โ useful for making structured, tabular data, like a product catalog, individually searchable.
Example 5: loading an entire directory at once
from langchain_community.document_loaders import DirectoryLoader
loader = DirectoryLoader("./company_docs", glob="**/*.txt")
documents = loader.load()
print(f"Loaded {len(documents)} documents from the directory.")
for doc in documents:
print("-", doc.metadata["source"])
DirectoryLoader walks an entire folder structure, applying a matching loader to every file that fits the glob pattern โ genuinely practical for building a RAG system over an entire existing folder of company documents, rather than loading each file individually by hand.
Common mistakes worth avoiding
Assuming every loader produces exactly one Document per file. Recall Example 2 โ PyPDFLoader genuinely produces one Document per page, not per file. Code that assumes len(documents) == 1 for a single PDF will break in a confusing way the first time itโs given a multi-page file.
Ignoring metadata because it isnโt immediately needed. Itโs tempting to treat metadata as optional boilerplate, but recall Module 26โs real payoff โ genuine source citations depend entirely on metadata being captured correctly here, at the very start of the pipeline. Skipping it now means retrofitting it later, across every document youโve already loaded.
Loading an entire directory without checking what actually got matched. Recall Example 5โs glob="**/*.txt" โ a mistyped pattern can silently load zero files, or far more files than intended, with no obvious error. Always check len(documents) after a DirectoryLoader call before assuming it worked as expected.
What you should take away from this module
- Every loader, regardless of source, produces the same shape: a list of
Documentobjects, each withpage_contentandmetadata. metadatais filled in automatically with real, useful context โ source file, page number โ and matters for filtering and source attribution later.- LangChain has a real, dedicated loader for essentially every common content type: text, PDF, web pages, CSV, and entire directories at once.
Where this goes next
The next module covers Text Splitting โ because a loaded document, especially a long PDF or web page, is almost never the right size to hand directly to an embedding model or a retriever. Youโll learn how, and how much, to break it apart.
- Author
- TechByteByByte Editorial Team
- Reviewed by
- TechByteByByte Admin
- Published
- Last reviewed