add-api-extract-pdf-content
This commit is contained in:
1 parent
4e3358fb1c
commit
ef8f0afa64
7 files changed
+176
-1
No files matched your search
+22
@@ -0,0 +1,22 @@
|
|||||||
|
# Virtual environment
|
||||||
|
myenv/
|
||||||
|
venv/
|
||||||
|
.env/
|
||||||
|
|
||||||
|
# Python cache
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
|
||||||
|
# VSCode
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
|
||||||
|
# MacOS
|
||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
from pdf2image import convert_from_path
|
||||||
|
import pytesseract
|
||||||
|
|
||||||
|
pages = convert_from_path("images/sample.pdf", dpi=300)
|
||||||
|
|
||||||
|
for i, page in enumerate(pages):
|
||||||
|
text = pytesseract.image_to_string(page, config="--psm 6")
|
||||||
|
print(f"--- Page {i+1} ---")
|
||||||
|
print(text)
|
||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
import os
|
||||||
|
import uuid
|
||||||
|
import tempfile
|
||||||
|
import requests
|
||||||
|
from minio import Minio
|
||||||
|
from pdf2image import convert_from_path
|
||||||
|
import pytesseract
|
||||||
|
import concurrent.futures
|
||||||
|
import PyPDF2
|
||||||
|
|
||||||
|
# ================= CONFIG =================
|
||||||
|
MINIO_ENDPOINT = "192.168.10.76:9000"
|
||||||
|
MINIO_ACCESS = "minioadmin"
|
||||||
|
MINIO_SECRET = "Admin@!@2025"
|
||||||
|
BUCKET_NAME = "pdf-storage"
|
||||||
|
|
||||||
|
POPPLER_PATH = r"C:\Program Files\poppler\Library\bin" # Ubuntu = None
|
||||||
|
MAX_FILE_SIZE_MB = 20
|
||||||
|
|
||||||
|
# ================= INIT MINIO =================
|
||||||
|
minio_client = Minio(
|
||||||
|
MINIO_ENDPOINT,
|
||||||
|
access_key=MINIO_ACCESS,
|
||||||
|
secret_key=MINIO_SECRET,
|
||||||
|
secure=False
|
||||||
|
)
|
||||||
|
|
||||||
|
if not minio_client.bucket_exists(BUCKET_NAME):
|
||||||
|
minio_client.make_bucket(BUCKET_NAME)
|
||||||
|
|
||||||
|
|
||||||
|
# ================= HELPERS =================
|
||||||
|
|
||||||
|
def pdf_has_text(file_path):
|
||||||
|
reader = PyPDF2.PdfReader(file_path)
|
||||||
|
for page in reader.pages:
|
||||||
|
if page.extract_text():
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def ocr_page(page):
|
||||||
|
return pytesseract.image_to_string(page, config="--psm 6")
|
||||||
|
|
||||||
|
|
||||||
|
def process_pdf(url):
|
||||||
|
response = requests.get(url, timeout=30)
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
raise Exception("Download failed")
|
||||||
|
|
||||||
|
if "application/pdf" not in response.headers.get("Content-Type", ""):
|
||||||
|
raise Exception("Not a valid PDF")
|
||||||
|
|
||||||
|
if len(response.content) > MAX_FILE_SIZE_MB * 1024 * 1024:
|
||||||
|
raise Exception("File too large")
|
||||||
|
|
||||||
|
return _process_bytes(response.content)
|
||||||
|
|
||||||
|
|
||||||
|
def process_uploaded_pdf(file_bytes):
|
||||||
|
return _process_bytes(file_bytes)
|
||||||
|
|
||||||
|
|
||||||
|
def _process_bytes(file_bytes):
|
||||||
|
file_id = str(uuid.uuid4())
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
|
||||||
|
tmp.write(file_bytes)
|
||||||
|
local_pdf = tmp.name
|
||||||
|
|
||||||
|
# Upload PDF
|
||||||
|
minio_client.fput_object(
|
||||||
|
BUCKET_NAME,
|
||||||
|
f"{file_id}.pdf",
|
||||||
|
local_pdf,
|
||||||
|
content_type="application/pdf"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Detect digital PDF
|
||||||
|
if pdf_has_text(local_pdf):
|
||||||
|
reader = PyPDF2.PdfReader(local_pdf)
|
||||||
|
full_text = ""
|
||||||
|
for page in reader.pages:
|
||||||
|
full_text += page.extract_text() or ""
|
||||||
|
source_type = "digital_pdf"
|
||||||
|
else:
|
||||||
|
pages = convert_from_path(
|
||||||
|
local_pdf,
|
||||||
|
dpi=300,
|
||||||
|
poppler_path=POPPLER_PATH
|
||||||
|
)
|
||||||
|
|
||||||
|
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||||
|
results = executor.map(ocr_page, pages)
|
||||||
|
|
||||||
|
full_text = "\n".join(results)
|
||||||
|
source_type = "scanned_pdf"
|
||||||
|
|
||||||
|
os.remove(local_pdf)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"file_id": file_id,
|
||||||
|
"type": source_type,
|
||||||
|
"text_length": len(full_text),
|
||||||
|
"text": full_text
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ from flask import Flask, request, jsonify
|
|||||||
import requests
|
import requests
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from DuckDuckGo import search_duckduckgo
|
from DuckDuckGo import search_duckduckgo
|
||||||
|
from PdfExtract import process_pdf, process_uploaded_pdf
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -14,6 +16,41 @@ def search():
|
|||||||
results = search_duckduckgo(query)
|
results = search_duckduckgo(query)
|
||||||
return jsonify(results)
|
return jsonify(results)
|
||||||
|
|
||||||
|
# ================= API 1: Extract from URL =================
|
||||||
|
@app.route("/extract", methods=["POST"])
|
||||||
|
def extract():
|
||||||
|
try:
|
||||||
|
data = request.get_json()
|
||||||
|
if not data or "url" not in data:
|
||||||
|
return jsonify({"error": "Missing URL"}), 400
|
||||||
|
|
||||||
|
result = process_pdf(data["url"])
|
||||||
|
return jsonify(result)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 400
|
||||||
|
|
||||||
|
|
||||||
|
# ================= API 2: Upload file =================
|
||||||
|
@app.route("/upload", methods=["POST"])
|
||||||
|
def upload():
|
||||||
|
try:
|
||||||
|
if "file" not in request.files:
|
||||||
|
return jsonify({"error": "No file field"}), 400
|
||||||
|
|
||||||
|
file = request.files["file"]
|
||||||
|
|
||||||
|
if file.filename == "":
|
||||||
|
return jsonify({"error": "No selected file"}), 400
|
||||||
|
|
||||||
|
file_bytes = file.read()
|
||||||
|
|
||||||
|
result = process_uploaded_pdf(file_bytes)
|
||||||
|
return jsonify(result)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 400
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run(debug=True)
|
app.run(host="0.0.0.0", port=8000)
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
Reference in new issue
Block a user