Pdf extract
Smart PDF content extraction with context, page references, and pattern matching. Triggers when finding specific content in PDFs (contract prices, terms), searching for patterns, extracting images, or needing snippets with surrounding context.From its SKILL.md
npx -y skills add stonegiantstudio/skills --skill pdf-extractAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
9.9 KB, as published. Nobody here has run it
PDF Smart Extraction
Extract content from PDFs with context, page numbers, and pattern matching.
Quick Reference
| Task | Method |
|---|---|
| Find specific value (price, date) | extract_with_context() |
| Find all pattern instances | find_all_matches() |
| Extract images | extract_images() |
| Get structured data | extract_tables() with pandas |
Installation
pip install pdfplumber pypdfium2 Pillow pandas
Extract with Context
Find content and return surrounding text with page number.
import pdfplumber
import re
def extract_with_context(pdf_path: str, pattern: str, context_chars: int = 200) -> list[dict]:
"""
Find pattern matches with surrounding context and page numbers.
Returns: [{
'page': 1,
'match': 'Total: $45,000',
'before': '...preceding text...',
'after': '...following text...',
'full_snippet': '...before...match...after...'
}]
"""
results = []
with pdfplumber.open(pdf_path) as pdf:
for page_num, page in enumerate(pdf.pages, start=1):
text = page.extract_text() or ""
for match in re.finditer(pattern, text, re.IGNORECASE):
start = max(0, match.start() - context_chars)
end = min(len(text), match.end() + context_chars)
results.append({
'page': page_num,
'match': match.group(),
'before': text[start:match.start()].strip(),
'after': text[match.end():end].strip(),
'full_snippet': text[start:end].strip(),
'position': {'start': match.start(), 'end': match.end()}
})
return results
# Example: Find contract total
matches = extract_with_context(
"contract.pdf",
r"(?:total|amount)[:\s]*\$[\d,]+(?:\.\d{2})?",
context_chars=150
)
for m in matches:
print(f"Page {m['page']}: {m['match']}")
print(f" Context: ...{m['before'][-50:]} [{m['match']}] {m['after'][:50]}...")
Find All Pattern Instances
Search across entire document for all occurrences.
def find_all_matches(pdf_path: str, patterns: dict[str, str]) -> dict[str, list]:
"""
Find all instances of multiple patterns.
Args:
patterns: {'label': 'regex_pattern', ...}
Returns: {
'label': [{'page': 1, 'match': '...', 'count': 3}, ...]
}
"""
results = {label: [] for label in patterns}
with pdfplumber.open(pdf_path) as pdf:
for page_num, page in enumerate(pdf.pages, start=1):
text = page.extract_text() or ""
for label, pattern in patterns.items():
matches = re.findall(pattern, text, re.IGNORECASE)
if matches:
results[label].append({
'page': page_num,
'matches': matches,
'count': len(matches)
})
# Add totals
for label in results:
total = sum(r['count'] for r in results[label])
results[label] = {'items': results[label], 'total': total}
return results
# Example: Find multiple patterns
results = find_all_matches("document.pdf", {
'emails': r'[\w\.-]+@[\w\.-]+\.\w+',
'phone_numbers': r'\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}',
'dollar_amounts': r'\$[\d,]+(?:\.\d{2})?'
})
for label, data in results.items():
print(f"{label}: {data['total']} total")
for item in data['items']:
print(f" Page {item['page']}: {item['count']} matches")
Extract Images
Get all images from PDF with page references.
import pypdfium2 as pdfium
from PIL import Image
from pathlib import Path
def extract_images(pdf_path: str, output_dir: str) -> list[dict]:
"""
Extract all images from PDF.
Returns: [{
'page': 1,
'index': 0,
'path': 'output/page_1_img_0.png',
'size': (800, 600)
}]
"""
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
results = []
pdf = pdfium.PdfDocument(pdf_path)
for page_num, page in enumerate(pdf, start=1):
for img_index, img_obj in enumerate(page.get_objects()):
if img_obj.type == pdfium.FPDF_PAGEOBJ_IMAGE:
bitmap = img_obj.get_bitmap()
pil_img = bitmap.to_pil()
filename = f"page_{page_num}_img_{img_index}.png"
filepath = output_path / filename
pil_img.save(filepath)
results.append({
'page': page_num,
'index': img_index,
'path': str(filepath),
'size': pil_img.size
})
return results
# Extract all images
images = extract_images("brochure.pdf", "./extracted_images")
print(f"Found {len(images)} images")
for img in images:
print(f" Page {img['page']}: {img['path']} ({img['size'][0]}x{img['size'][1]})")
Extract Tables to DataFrame
Get structured table data with page tracking.
import pdfplumber
import pandas as pd
def extract_tables(pdf_path: str) -> list[dict]:
"""
Extract all tables as DataFrames with metadata.
Returns: [{
'page': 1,
'table_index': 0,
'df': DataFrame,
'rows': 10,
'cols': 5
}]
"""
results = []
with pdfplumber.open(pdf_path) as pdf:
for page_num, page in enumerate(pdf.pages, start=1):
tables = page.extract_tables()
for table_index, table in enumerate(tables):
if table and len(table) > 1:
# First row as headers
df = pd.DataFrame(table[1:], columns=table[0])
results.append({
'page': page_num,
'table_index': table_index,
'df': df,
'rows': len(df),
'cols': len(df.columns)
})
return results
# Extract and combine tables
tables = extract_tables("financial_report.pdf")
for t in tables:
print(f"Page {t['page']}, Table {t['table_index']}: {t['rows']}x{t['cols']}")
print(t['df'].head())
Search with Fuzzy Matching
Find content even with OCR errors or variations.
from difflib import SequenceMatcher
import pdfplumber
def fuzzy_search(pdf_path: str, target: str, threshold: float = 0.8) -> list[dict]:
"""
Find text similar to target (handles typos, OCR errors).
Args:
threshold: 0.0-1.0, higher = stricter matching
"""
results = []
words = target.lower().split()
window_size = len(words)
with pdfplumber.open(pdf_path) as pdf:
for page_num, page in enumerate(pdf.pages, start=1):
text = page.extract_text() or ""
text_words = text.lower().split()
for i in range(len(text_words) - window_size + 1):
window = ' '.join(text_words[i:i + window_size])
ratio = SequenceMatcher(None, target.lower(), window).ratio()
if ratio >= threshold:
# Get original text with proper casing
original = ' '.join(text.split()[i:i + window_size])
results.append({
'page': page_num,
'match': original,
'similarity': round(ratio, 2),
'position': i
})
return sorted(results, key=lambda x: -x['similarity'])
# Find "John Smith" even if OCR read "Jonn Srnith"
matches = fuzzy_search("scanned_doc.pdf", "John Smith", threshold=0.7)
Batch Processing
Process multiple PDFs efficiently.
from pathlib import Path
from concurrent.futures import ProcessPoolExecutor
import json
def process_pdf(pdf_path: str, patterns: dict) -> dict:
"""Process single PDF and return results."""
return {
'file': pdf_path,
'matches': find_all_matches(pdf_path, patterns)
}
def batch_search(pdf_dir: str, patterns: dict, workers: int = 4) -> list[dict]:
"""Search patterns across multiple PDFs in parallel."""
pdf_files = list(Path(pdf_dir).glob("*.pdf"))
with ProcessPoolExecutor(max_workers=workers) as executor:
results = list(executor.map(
lambda p: process_pdf(str(p), patterns),
pdf_files
))
return results
# Search across all PDFs in directory
results = batch_search("./contracts", {
'total_amount': r'total[:\s]*\$[\d,]+',
'effective_date': r'effective\s+date[:\s]*[\w\s,]+\d{4}'
})
# Save results
with open("search_results.json", "w") as f:
json.dump(results, f, indent=2, default=str)
Common Patterns
PATTERNS = {
# Financial
'currency': r'\$[\d,]+(?:\.\d{2})?',
'percentage': r'\d+(?:\.\d+)?%',
# Dates
'date_us': r'\d{1,2}/\d{1,2}/\d{2,4}',
'date_iso': r'\d{4}-\d{2}-\d{2}',
'date_written': r'(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\s+\d{1,2},?\s+\d{4}',
# Contact
'email': r'[\w\.-]+@[\w\.-]+\.\w+',
'phone_us': r'\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}',
# Legal/Contract
'section_ref': r'[Ss]ection\s+\d+(?:\.\d+)*',
'article_ref': r'[Aa]rticle\s+[IVXLCDM]+|\d+',
# Technical
'url': r'https?://[^\s<>"{}|\\^`\[\]]+',
'ip_address': r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}',
}
Tips
- OCR'd PDFs - Use
fuzzy_search()for scanned documents with potential errors - Large PDFs - Process pages in chunks to manage memory
- Structured data - Extract tables first, then search within DataFrames
- Image analysis - Extract images, then use Claude's vision for analysis
- Page ranges - Most functions accept
start_page/end_pageparams (add as needed)