Automatiser l'export de son blog Ghost

Automatiser l'export de son blog Ghost

Il est important de sauvegarder ses données, encore plus lorsqu'il s'agit des articles de son blog Ghost ! Nous allons voir comment exporter toutes ses données de son blog Ghost avec un script Python, en utilisant une partie de l'API cachée de Ghost.

Introduction

Je naviguais dans l'interface d'administration de Ghost, et je suis tombé sur la partie export/import de contenu, dans le menu "Labs".

En analysant les requêtes avec la console de mon navigateur, j'ai pu voir le endpoint de l'API qui permettait l'export, celle-ci n'est même pas documentée officiellement !

N'hésitez pas à vous rendre sur le repository Gitlab que j'ai crée, qui contient le code source.

Matthieu Calasin / export-ghost · GitLab
GitLab.com

Prérequis

Pour effectuer l'export des données de son blog Ghost, il faut s'authentifier en entrant son nom d'utilisateur ainsi que son mot de passe. Afin de ne pas stocker ses identifiants Ghost en clair, il faudra exporter les variables d'environnement suivantes :

export GHOST_USER='YOUR GHOST USER MAIL'
export GHOST_PASSWORD='YOUR PASSWORD GHOST USER'
export GHOST_EXPORT_DIRECTORY='PATH OF EXPORT FILE'
export GHOST_URL='FULL GHOST URL (example : https://dynops.fr)'

Le script

J'ai développé le script en Python, et j'ai pu obtenir l'endpoint de l'API de Ghost qui permet de faire l'export en analysant les requêtes réseau via la console de mon navigateur.
Voici le bout de code permettant l'authentification auprès de Ghost :

import requests
import json
import re
import os
from datetime import datetime

# API Authentification
GHOST_URL = os.getenv('GHOST_URL')
endpoint_auth = f"{GHOST_URL}/ghost/api/admin/session"

payload_auth = json.dumps({
    "username": os.getenv('GHOST_USER'),
    "password": os.getenv('GHOST_PASSWORD')
})
headers_auth = {
    'Content-Type': 'application/json'
}

response_auth = requests.post(endpoint_auth, headers=headers_auth, data=payload_auth)

if response_auth.status_code == 201:
    # Store the cookie
    cookie_created = response_auth.headers.get("Set-Cookie")
    # RegEx to isolate the cookie
    isolate_cookie = re.findall(r"\b\S+", cookie_created)
    cookie_with_semicolon = isolate_cookie[0]
    cookie = cookie_with_semicolon.rstrip(cookie_with_semicolon[-1])

    headers_export = {
        'Cookie': cookie
    }
else:
    print("L'authentification a échoué, le code retour HTTP est : " + str(response_auth.status_code)) 
    exit(1)

Dans cette partie du code, j'effectue une requête POST en injectant un payload contenant le nom d'utilisateur et le mot de passe.

Je vérifie si le code retour HTTP est égal à 201, si c'est le cas, je stocke le cookie renvoyé. Sinon, le script s'arrête sur un code retour 1.

Analysons maintenant la partie export :

# API Export
endpoint_export = f"{GHOST_URL}/ghost/api/admin/db/"

export_path = os.getenv('GHOST_EXPORT_DIRECTORY')

get_export = requests.get(url=endpoint_export, headers=headers_export)
get_export = get_export.json()
get_export = json.dumps(get_export, indent=2)

# Récupération de la date du jour
today = datetime.now().strftime("%d-%m-%Y")

export_file_name = f"export-{today}.json"

with open(os.path.join(export_path, export_file_name), 'w') as export:
    export.write(get_export)

# Récupération de la liste des fichiers dans le répertoire courant
files = os.listdir()

# Parcours de la liste des fichiers
for file in files:
    # Vérification si le fichier correspond au format du nom du fichier avec la date
    if file.startswith("export-") and file.endswith(".json"):
        # Récupération de la date du fichier
        date_str = file[7:-5]
        date = datetime.strptime(date_str, "%d-%m-%Y")

        # Calcul de la différence entre la date du jour et la date du fichier
        diff = datetime.now() - date

        # Si la différence est supérieure à 14 jours, suppression du fichier
        if diff.days > 14:
            os.remove(file)

Dans cette partie, j'effectue une requête GET sur l'endpoint de l'API. Je renvoie la réponse HTTP dans un fichier JSON que je nomme avec la date du jour.

Pour garder une rétention des exports des derniers 14 jours uniquement, je liste les fichiers dans le répertoire contenu dans la variable d'environnement GHOST_EXPORT_DIRECTORY
et je supprime tous ceux dont la date dépasse les 14 derniers jours.

Au final, le résultat de l'export ressemble à ceci :

Le code au complet correspond à ceci :

import requests
import json
import re
import os
from datetime import datetime

# API Authentification
GHOST_URL = os.getenv('GHOST_URL')
endpoint_auth = f"{GHOST_URL}/ghost/api/admin/session"

payload_auth = json.dumps({
    "username": os.getenv('GHOST_USER'),
    "password": os.getenv('GHOST_PASSWORD')
})
headers_auth = {
    'Content-Type': 'application/json'
}

response_auth = requests.post(endpoint_auth, headers=headers_auth, data=payload_auth)

if response_auth.status_code == 201:
    # Store the cookie
    cookie_created = response_auth.headers.get("Set-Cookie")
    # RegEx to isolate the cookie
    isolate_cookie = re.findall(r"\b\S+", cookie_created)
    cookie_with_semicolon = isolate_cookie[0]
    cookie = cookie_with_semicolon.rstrip(cookie_with_semicolon[-1])

    headers_export = {
        'Cookie': cookie
    }
else:
    print("L'authentification a échoué, le code retour HTTP est : " + str(response_auth.status_code)) 
    exit(1)

# API Export
endpoint_export = f"{GHOST_URL}/ghost/api/admin/db/"

export_path = os.getenv('GHOST_EXPORT_DIRECTORY')

get_export = requests.get(url=endpoint_export, headers=headers_export)
get_export = get_export.json()
get_export = json.dumps(get_export, indent=2)

# Récupération de la date du jour
today = datetime.now().strftime("%d-%m-%Y")

export_file_name = f"export-{today}.json"

with open(os.path.join(export_path, export_file_name), 'w') as export:
    export.write(get_export)

# Récupération de la liste des fichiers dans le répertoire courant
files = os.listdir()

# Parcours de la liste des fichiers
for file in files:
    # Vérification si le fichier correspond au format du nom du fichier avec la date
    if file.startswith("export-") and file.endswith(".json"):
        # Récupération de la date du fichier
        date_str = file[7:-5]
        date = datetime.strptime(date_str, "%d-%m-%Y")

        # Calcul de la différence entre la date du jour et la date du fichier
        diff = datetime.now() - date

        # Si la différence est supérieure à 14 jours, suppression du fichier
        if diff.days > 14:
            os.remove(file)

Il ne vous reste plus qu'à orchestrer ce script et vous aurez les données de votre blog Ghost sauvegardées !