Εικόνες OG στην Python
Αυτός ο οδηγός σάς δείχνει πώς να δημιουργείτε εικόνες OG στην Python με το FastOG — Django, FastAPI ή Flask — υπογράφοντας μια διεύθυνση URL HMAC και αποδίδοντας δυναμικές εικόνες Open Graph εν κινήσει. Με συνδρομή και δωρεάν λογαριασμό (επιλέξτε ένα πρόγραμμα Dynamic API· δημιουργήστε το κλειδί API σας στον πίνακα ελέγχου)· δοκιμάστε το Δωρεάν Εργαλείο Δοκιμής Εικόνων OG για να δείτε ένα πρότυπο πριν από την ενσωμάτωση.
Πλήρης οδηγός για την υλοποίηση διευθύνσεων URL εικόνων OG υπογεγραμμένων με HMAC στην Python.
Γρήγορη Εκκίνηση
1. Δημιουργήστε την ενότητα υπογραφής (signer module)
# utils/fastog.py
import hmac
import hashlib
import os
from urllib.parse import quote, urlencode
def _enc(s):
return quote(str(s), safe='-._~') # RFC-3986: only unreserved chars
def generate_signed_og_url(params: dict, options: dict = None) -> str:
options = options or {}
base_url = options.get('base_url', os.environ.get('FASTOG_BASE_URL', 'https://fastog.com/api/v1/og'))
api_key = options.get('api_key', os.environ.get('FASTOG_API_KEY'))
hmac_secret = options.get('hmac_secret', os.environ.get('FASTOG_HMAC_SECRET'))
if not api_key or not hmac_secret:
raise ValueError("Missing FASTOG_API_KEY or FASTOG_HMAC_SECRET")
all_params = {**params, 'key': api_key}
pairs = []
for k, v in all_params.items():
for vv in sorted(v if isinstance(v, list) else [v]):
pairs.append(f"{_enc(k)}={_enc(vv)}")
canonical = '&'.join(sorted(pairs))
signature = hmac.new(
hmac_secret.encode(),
f"GET/api/v1/og\n{canonical}".encode(),
hashlib.sha256
).hexdigest()
return f"{base_url}?{urlencode({**all_params, 's': signature})}"
2. Μεταβλητές περιβάλλοντος
# .env
FASTOG_API_KEY=sk_your_full_api_key_here
FASTOG_HMAC_SECRET=your_64_char_hex_secret_here
pip install python-dotenv
# config.py
from dotenv import load_dotenv
load_dotenv()
Django
Υπηρεσία Υπογραφής (Signer Service)
# services/fastog.py
from utils.fastog import generate_signed_og_url
class QuickogService:
@staticmethod
def get_og_url(template: str, **params) -> str:
return generate_signed_og_url({
'template': template,
**params,
})
Προβολή (View)
# views.py
from django.shortcuts import render
from services.fastog import QuickogService
def blog_post(request, slug):
post = get_post(slug)
og_image_url = QuickogService.get_og_url(
template='blog',
title=post.title,
subtitle=post.excerpt,
author=post.author.name,
)
return render(request, 'blog/post.html', {
'post': post,
'og_image_url': og_image_url,
})
Πρότυπο (Template)
{# templates/blog/post.html #}
<!DOCTYPE html>
<html>
<head>
<meta property="og:image" content="{{ og_image_url }}" />
<meta property="og:title" content="{{ post.title }}" />
<meta property="og:description" content="{{ post.excerpt }}" />
</head>
<body>
<h1>{{ post.title }}</h1>
<p>{{ post.excerpt }}</p>
</body>
</html>
Προσαρμοσμένη Ετικέτα Προτύπου (Custom Template Tag)
# templatetags/fastog_tags.py
from django import template
from services.fastog import QuickogService
register = template.Library()
@register.simple_tag
def og_image_url(template_name, **params):
return QuickogService.get_og_url(template_name, **params)
{# In template #}
{% load fastog_tags %}
<meta property="og:image" content="{% og_image_url 'blog' title=post.title subtitle=post.excerpt %}" />
FastAPI
# main.py
from fastapi import FastAPI, Query
from pydantic import BaseModel
from utils.fastog import generate_signed_og_url
app = FastAPI()
class OgRequest(BaseModel):
title: str
template: str = "default"
subtitle: str | None = None
@app.get("/api/og")
def generate_og(title: str = Query(...), template: str = Query("default")):
og_url = generate_signed_og_url({
'template': template,
'title': title,
})
return {"ogImageUrl": og_url}
@app.get("/blog/{slug}")
async def blog_post(slug: str):
post = await get_post(slug)
og_url = generate_signed_og_url({
'template': 'blog',
'title': post.title,
'subtitle': post.excerpt,
})
return {"post": post, "ogImageUrl": og_url}
Flask
# app.py
from flask import Flask, render_template, request, jsonify
from utils.fastog import generate_signed_og_url
app = Flask(__name__)
@app.route("/blog/<slug>")
def blog_post(slug):
post = get_post(slug)
og_image_url = generate_signed_og_url({
'template': 'blog',
'title': post['title'],
'subtitle': post['excerpt'],
})
return render_template('blog/post.html', post=post, og_image_url=og_image_url)
@app.route("/api/og")
def api_og():
title = request.args.get('title')
template = request.args.get('template', 'default')
if not title:
return jsonify({'error': 'Missing title'}), 400
og_url = generate_signed_og_url({
'template': template,
'title': title,
})
return jsonify({'ogImageUrl': og_url})
Παράδειγμα Ηλεκτρονικού Εμπορίου
# views.py
def product_detail(request, product_id):
product = get_product(product_id)
og_image_url = generate_signed_og_url({
'template': 'ecommerce',
'title': product.name,
'price': product.formatted_price,
'image': product.image_url,
})
return render(request, 'products/detail.html', {
'product': product,
'og_image_url': og_image_url,
})
Παράδειγμα Άρθρου Ιστολογίου
def user_profile(request, username):
user = get_user(username)
og_image_url = generate_signed_og_url({
'template': 'blog',
'title': user.name,
'subtitle': user.bio,
'author': user.name,
})
return render(request, 'profile/show.html', {
'user': user,
'og_image_url': og_image_url,
})
Συχνά Σφάλματα
Υπογραφή Λάθος Μηνύματος
❌ Λάθος: f"GET/api/v1/og{canonical}" — λείπει ο διαχωριστής νέας γραμμής.
✅ Σωστό: f"GET/api/v1/og\n{canonical}" — μέθοδος + διαδρομή, μια νέα γραμμή και στη συνέχεια το κανονικό ερώτημα.
Παράλειψη του key από την Υπογραφή
❌ Λάθος: υπογραφή μόνο των παραμέτρων περιεχομένου σας και προσάρτηση του key στη διεύθυνση URL εκ των υστέρων.
✅ Σωστό: το key συγχωνεύεται στις παραμέτρους πριν κατασκευαστεί το κανονικό ερώτημα και εμφανίζεται στην τελική διεύθυνση URL.
Παράλειψη Κωδικοποίησης URL για Κλειδιά και Τιμές
❌ Λάθος: κατασκευή του κανονικού ερωτήματος με μη επεξεργασμένες, μη κωδικοποιημένες τιμές.
✅ Σωστό: κωδικοποιήστε με ποσοστιαία κωδικοποίηση τόσο τα κλειδιά όσο και τις τιμές με RFC-3986 (safe='-._~') πριν από την υπογραφή — χρησιμοποιήστε το quote, όχι το quote_plus (το οποίο εκπέμπει + για τα κενά και δεν θα ταιριάξει με τον διακομιστή).
401 missing_signature / invalid_signature
Το missing_signature (401) σημαίνει ότι το key ή το s λείπει από τη διεύθυνση URL. Το invalid_signature (401) σημαίνει ότι το κανονικό ερώτημα δεν ταιριάζει — επιβεβαιώστε τη νέα γραμμή στο υπογεγραμμένο μήνυμα, το ίδιο hmac_secret και στις δύο πλευρές και το quote(safe='-._~') (όχι το quote_plus) για κάθε κλειδί και τιμή.