For several years I had an idea to build something to automate social media images from my blogs, also called Open Graph images, because this is something that I never enjoyed doing. The most common approach to solve this problem was creating a HTML page with the content and styles and using a headless browser to take a screenshot, but this always seen too cumbersome and I never started doing anything. But while I watched Language models on the command-line w/ Simon Willison to learn more about how to use LLM, I found out that Simon created a tool called shot-scraper to automate screenshots and that it could also run JavaScript code. This got me excited again about my project idea because I could see for the first time a way that I could implement it.

Planning the minimum viable version

To don’t get stuck in minor details, I planned a quick version of my idea that I could see something working and help me get excited to continue working on it. This was my initial plan:

  1. Install the tool and test it;
  2. Create simple HTML and take screenshot of it;
  3. Change the content of the page using the tool JavaScript support and take a screenshot of the result;
  4. Read a CSV file with content and take screenshot for each one of them.

To help me focus, I used the Pomodoro Technique and defined that at the end of the first Pomodoro I would have this done. Are you ready?

1. Installation

I followed the official shot-scraper documentation and since I had already Python and some tools installed in my computer (Ubuntu 24.04), I just typed in my terminal:

pipx install shot-scraper

shot-scraper install
Bash

Now let’s create a folder for the project and test the tool.

shot-scraper https://www.danielkossmann.com/
Bash

Let’s see how to set a custom width and height based on the image size that I use for my blog images and test it.

shot-scraper https://www.danielkossmann.com/ --width 1200 --height 630
Bash

Success! Now let’s move on to the next phase.

2. Test it with custom HTML

I created a simple HTML called index.html using Emmet in VS Code with html:5 and added a title and description.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>Original H1 Content</h1>
    <p>Original paragraph content.</p>
</body>
</html>
HTML

And them looked up how to take a screenshot for a local file and give the image a name.

shot-scraper index.html --width 1200 --height 630 -o thumbnail.png
Bash

Success! Now let’s move on to the next phase.

3. Changing the content before taking the screenshoot

To add JavaScript to shot-scraper is very simple, and I used ChatGPT to help me write a code to change the title, description and background. In this process I learned that the easiest way would be to add IDs in my code so I can easily change them. So I updated the HTML to:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        body {
            background-color: white;
        }
    </style>
</head>
<body>
    <h1 id="header">Original H1 Content</h1>
    <p id="paragraph">Original paragraph content.</p>
</body>
</html>
HTML

And this was my JavaScript code that I quickly tested in the browser console:

document.getElementById("header").innerHTML = "Daniel Kossmann";

document.getElementById("paragraph").innerHTML = "I'm a Technical Product Manager";

document.body.style.backgroundColor = "lightblue";
JavaScript

But to add it into the command line, I needed to change the type of quotes, and the final result was. Adding the code to shot-scraper looked like this.

shot-scraper index.html --width 1200 --height 630 -o thumbnail.png --javascript "
document.getElementById('header').innerHTML = 'Daniel Kossmann';
document.getElementById('paragraph').innerHTML = 'I\'m a Technical Product Manager';
document.body.style.backgroundColor = 'lightblue';
"
Bash

It worked!

Create screenshots for a list of content

Since I know how to program in Python, I asked ChatGPT to

write me a python script that reads a CSV with 4 values (ID, NAME, DESCRIPTION, COLOR) and for each one run the following script, replacing each of the variables (inside {}):

shot-scraper index.html --width 1200 --height 630 -o {ID}.png --javascript "
document.getElementById('header').innerHTML = '{NAME}';
document.getElementById('paragraph').innerHTML = '{DESCRIPTION}';
document.body.style.backgroundColor = '{COLOR}';
"
Markdown

And changed the result to save the files under the folder screenshots/ to keep things more organized. Here is the final code for screenshots.py:

import csv
import os

# Function to run the shot-scraper command
def run_shot_scraper(row):
    command = f"""
    shot-scraper index.html -o screenshots/{row['ID']}.png --width 800 --height 600 --javascript \"
    document.getElementById('header').innerHTML = \\"{row['NAME']}\\";
    document.getElementById('paragraph').innerHTML = \\"{row['DESCRIPTION'].replace('"', '\\"')}\\";
    document.body.style.backgroundColor = '{row['COLOR']}';
    \"
    """
    os.system(command)

# Read CSV and process each row
def process_csv(file_path):
    with open(file_path, mode='r', newline='', encoding='utf-8') as file:
        csv_reader = csv.DictReader(file)
        for row in csv_reader:
            run_shot_scraper(row)

# Path to your CSV file
csv_file_path = 'data.csv'

# Run the script
process_csv(csv_file_path)
Python

I also created data.csv with some dummy content:

ID,NAME,DESCRIPTION,COLOR
thumb1,Daniel Kossmann,I'm a Technical Product Manager,lightblue
thumb2,OpenAI GPT,An advanced AI language model,lightgreen
thumb3,Python Script,Automating tasks with ease,lightcoral
Markdown

The result was the following three images.

The Pomodoro timer is over now!

Next steps

As the next step I will probably focus on improving the design or see how to get the data from my blog to generated the images based on it.


My plan is to continue documenting in public what I’ve been experimenting with. If you have any tips or found this useful, please leave a comment!

This post was written in almost 3 Pomodoros, listening to Duality from Neon Nox and Powernerd in loop.



Comments

Leave a Reply

Your email address will not be published. Required fields are marked *