add caching to puppeteer
Build and Push Docker Images / build-and-push (push) Successful in 3m55s

This commit is contained in:
2025-04-30 19:09:28 +02:00
parent a1d6db5a06
commit 5eeaa12bee
2 changed files with 148 additions and 9 deletions
+1
View File
@@ -5,6 +5,7 @@ RUN apt-get update && apt-get install -y \
wget \
gnupg2 \
procps \
sqlite3 \
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /usr/share/keyrings/google-chrome-keyring.gpg \
&& echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome-keyring.gpg] http://dl.google.com/linux/chrome/deb/ stable main" | tee /etc/apt/sources.list.d/google-chrome.list \
&& apt-get update \
+147 -9
View File
@@ -4,6 +4,9 @@ import os
import asyncio
import json
import re
import sqlite3
import time
from datetime import datetime, timedelta
from typing import Optional, Dict, List, Any
from urllib.parse import unquote
@@ -17,6 +20,59 @@ if not API_KEY:
# Define custom user agent
CUSTOM_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
# Initialize SQLite database
def init_db():
conn = sqlite3.connect('cache.db')
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS cache (
url TEXT,
route TEXT,
data TEXT,
timestamp INTEGER,
PRIMARY KEY (url, route)
)
''')
conn.commit()
conn.close()
# Get cached data if it exists and is not older than 36 hours
def get_cached_data(url, route):
conn = sqlite3.connect('cache.db')
cursor = conn.cursor()
cache_expiry = int(time.time()) - (36 * 60 * 60) # 36 hours in seconds
cursor.execute(
"SELECT data FROM cache WHERE url = ? AND route = ? AND timestamp > ?",
(url, route, cache_expiry)
)
result = cursor.fetchone()
conn.close()
if result:
print(f"Cache hit for {url} on route {route}")
return json.loads(result[0])
return None
# Save data to cache
def save_to_cache(url, route, data):
conn = sqlite3.connect('cache.db')
cursor = conn.cursor()
timestamp = int(time.time())
# Convert data to JSON string
data_json = json.dumps(data)
cursor.execute(
"INSERT OR REPLACE INTO cache (url, route, data, timestamp) VALUES (?, ?, ?, ?)",
(url, route, data_json, timestamp)
)
conn.commit()
conn.close()
print(f"Saved to cache: {url} on route {route}")
# Initialize database on startup
init_db()
async def wait_for_network_idle(page):
"""Wait until no network requests are in flight"""
await page.waitForNetworkIdle(idleTime=500, timeout=30000)
@@ -31,10 +87,16 @@ async def visit_url(url: str, x_api_key: Optional[str] = Header(None)):
if not x_api_key or x_api_key != API_KEY:
raise HTTPException(status_code=401, detail="Invalid API key")
# Decode URL if it's encoded
decoded_url = unquote(url)
# Check cache first
cached_result = get_cached_data(decoded_url, "visit")
if cached_result:
return cached_result
browser = None
try:
# Decode URL if it's encoded
decoded_url = unquote(url)
print(decoded_url)
# Launch browser using installed Chrome
@@ -59,7 +121,12 @@ async def visit_url(url: str, x_api_key: Optional[str] = Header(None)):
# Close page
await page.close()
return {"status": "success", "content": content}
result = {"status": "success", "content": content}
# Save to cache
save_to_cache(decoded_url, "visit", result)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@@ -76,10 +143,16 @@ async def extract_seo(url: str, x_api_key: Optional[str] = Header(None)):
if not x_api_key or x_api_key != API_KEY:
raise HTTPException(status_code=401, detail="Invalid API key")
# Decode URL if it's encoded
decoded_url = unquote(url)
# Check cache first
cached_result = get_cached_data(decoded_url, "seo")
if cached_result:
return cached_result
browser = None
try:
# Decode URL if it's encoded
decoded_url = unquote(url)
print(f"Extracting SEO from: {decoded_url}")
# Launch browser using installed Chrome
@@ -169,7 +242,12 @@ async def extract_seo(url: str, x_api_key: Optional[str] = Header(None)):
"twitter_card": twitter_data
}
return {"status": "success", "seo_data": seo_data}
result = {"status": "success", "seo_data": seo_data}
# Save to cache
save_to_cache(decoded_url, "seo", result)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@@ -186,10 +264,16 @@ async def extract_meta_tags(url: str, x_api_key: Optional[str] = Header(None)):
if not x_api_key or x_api_key != API_KEY:
raise HTTPException(status_code=401, detail="Invalid API key")
# Decode URL if it's encoded
decoded_url = unquote(url)
# Check cache first
cached_result = get_cached_data(decoded_url, "meta")
if cached_result:
return cached_result
browser = None
try:
# Decode URL if it's encoded
decoded_url = unquote(url)
print(f"Extracting meta tags from: {decoded_url}")
# Launch browser using installed Chrome
@@ -227,13 +311,18 @@ async def extract_meta_tags(url: str, x_api_key: Optional[str] = Header(None)):
# Close page
await page.close()
return {
result = {
"status": "success",
"url": decoded_url,
"title": title,
"meta_tags": meta_tags
}
# Save to cache
save_to_cache(decoded_url, "meta", result)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@@ -242,6 +331,55 @@ async def extract_meta_tags(url: str, x_api_key: Optional[str] = Header(None)):
if browser:
await browser.close()
@app.get("/cache/clear")
async def clear_cache(x_api_key: Optional[str] = Header(None)):
"""Clear the entire cache database"""
# Validate API key
if not x_api_key or x_api_key != API_KEY:
raise HTTPException(status_code=401, detail="Invalid API key")
conn = sqlite3.connect('cache.db')
cursor = conn.cursor()
cursor.execute("DELETE FROM cache")
conn.commit()
conn.close()
return {"status": "success", "message": "Cache cleared successfully"}
@app.get("/cache/stats")
async def cache_stats(x_api_key: Optional[str] = Header(None)):
"""Get cache statistics"""
# Validate API key
if not x_api_key or x_api_key != API_KEY:
raise HTTPException(status_code=401, detail="Invalid API key")
conn = sqlite3.connect('cache.db')
cursor = conn.cursor()
# Get total entries
cursor.execute("SELECT COUNT(*) FROM cache")
total_entries = cursor.fetchone()[0]
# Get entries by route
cursor.execute("SELECT route, COUNT(*) FROM cache GROUP BY route")
routes = {route: count for route, count in cursor.fetchall()}
# Get recent entries (last 24 hours)
recent_timestamp = int(time.time()) - (24 * 60 * 60)
cursor.execute("SELECT COUNT(*) FROM cache WHERE timestamp > ?", (recent_timestamp,))
recent_entries = cursor.fetchone()[0]
conn.close()
return {
"status": "success",
"stats": {
"total_entries": total_entries,
"entries_by_route": routes,
"recent_entries": recent_entries
}
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)