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 \ wget \
gnupg2 \ gnupg2 \
procps \ 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 \ && 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 \ && 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 \ && apt-get update \
+147 -9
View File
@@ -4,6 +4,9 @@ import os
import asyncio import asyncio
import json import json
import re import re
import sqlite3
import time
from datetime import datetime, timedelta
from typing import Optional, Dict, List, Any from typing import Optional, Dict, List, Any
from urllib.parse import unquote from urllib.parse import unquote
@@ -17,6 +20,59 @@ if not API_KEY:
# Define custom user agent # 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' 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): async def wait_for_network_idle(page):
"""Wait until no network requests are in flight""" """Wait until no network requests are in flight"""
await page.waitForNetworkIdle(idleTime=500, timeout=30000) 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: if not x_api_key or x_api_key != API_KEY:
raise HTTPException(status_code=401, detail="Invalid 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 browser = None
try: try:
# Decode URL if it's encoded
decoded_url = unquote(url)
print(decoded_url) print(decoded_url)
# Launch browser using installed Chrome # Launch browser using installed Chrome
@@ -59,7 +121,12 @@ async def visit_url(url: str, x_api_key: Optional[str] = Header(None)):
# Close page # Close page
await page.close() 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: except Exception as e:
raise HTTPException(status_code=500, detail=str(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: if not x_api_key or x_api_key != API_KEY:
raise HTTPException(status_code=401, detail="Invalid 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 browser = None
try: try:
# Decode URL if it's encoded
decoded_url = unquote(url)
print(f"Extracting SEO from: {decoded_url}") print(f"Extracting SEO from: {decoded_url}")
# Launch browser using installed Chrome # 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 "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: except Exception as e:
raise HTTPException(status_code=500, detail=str(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: if not x_api_key or x_api_key != API_KEY:
raise HTTPException(status_code=401, detail="Invalid 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 browser = None
try: try:
# Decode URL if it's encoded
decoded_url = unquote(url)
print(f"Extracting meta tags from: {decoded_url}") print(f"Extracting meta tags from: {decoded_url}")
# Launch browser using installed Chrome # 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 # Close page
await page.close() await page.close()
return { result = {
"status": "success", "status": "success",
"url": decoded_url, "url": decoded_url,
"title": title, "title": title,
"meta_tags": meta_tags "meta_tags": meta_tags
} }
# Save to cache
save_to_cache(decoded_url, "meta", result)
return result
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=str(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: if browser:
await browser.close() 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__": if __name__ == "__main__":
import uvicorn import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000) uvicorn.run(app, host="0.0.0.0", port=8000)