How to connect Python to WordPress

how to connect Python to WordPress

You might already be using Python to scrape, enrich and/or curate data. Python offers great packages such as Selenium, BeautifulSoup and Pandas enabling you to create advanced automated workflows. I'll cover those packages in more details in forthcoming articles. 

What if you want to connect Python to WordPress to generate and/or update some posts on your WP website? You don't need any plugin for that! You can easily integrate your Python scripts with your WordPress site using the WordPress REST API

Using Python + WordPress is an easy way to implement a programmatic SEO strategy around structured data. 

How to call the WordPress API from your Python IDE? 

I am using PyCharm but the process is the same if you're using Visual Studio Code or another IDE. 

You can also connect to your WordPress site from a local Jupyter notebook, from Replit or from Google Collab (I absolutely love Replit to privately share Python scripts with my collaborators). 

1° Create an application password

First of all, head to yourdomain.com/wp-admin/profile.php and scroll down to the "Applications Passwords" section. 

Give a name to your application (e.g. Python Connect) and press Add New Application Password

wordpress api application password

You will get a 24-character password. Keep it in a safe place.

24-character application password WordPress API

Make sure that you have access to the REST API of your website (you might have disabled access via your security plugin, WP Cerber or Wordfence). 

2° Create a new .py file in your IDE

Name it for instance wordpress_playground.py

👉The best practice is to structure your recurring WordPress functions in another file under dedicated Python functions, in order to be able to import and call this module's functions from other scripts.

This will allow you not to repeat the full code each time you need to access your WordPress site. 

In our playground file, we'll just start by defining two variables. 

We'll need them for the authentication function: wp_user and wp_pw

wp_user is the main admin email attached to your site, wp_pw is the 24-character password generated in the previous section.

wp_user = "admin@mydomain.com"
wp_pw = "e5wp oFZo txIA VZ5V zSp2 QGzE"

3° Create the file containing your WordPress API scripts

Name it for instance wordpress_scripts.py

Let's import two packages into this Python file: requests (which we'll need to call the API) and base64 (which we'll need to generate the authentication token). 

import requests
import base64

You can easily install those modules via the Packages section in Pycharm or via a pip install packagename terminal command in other applications (e.g. pip install requests)

I suggest to create at least 3 dedicated functions to integrate Python with your WordPress site

  • a function to generate the header authentication 
  • a function to upload images (and retrieve their unique ID)
  • a function to create a brand new post

4° In wordpress_scripts. py, create the authentication function

def wordpress_header (wp_user, wp_pw):

 wordpress_credentials = wp_user + ":" + wp_pw
 wordpress_token = base64.b64encode(wordpress_credentials.encode())
 wordpress_header = {'Authorization': 'Basic ' + wordpress_token.decode('utf-8')}
 wordpress_auth = wordpress_token.decode('utf-8')

 return wordpress_header, wordpress_auth

This function will return in a tuple (between ( ) ) two results:

wordpress_header = a ready-to-use header for the WP post creation function (the output is a Python dictionary consisting of a single key-value pair which will look like this 
{'Authorization': 'Basic token'}
)

wordpress_auth is just the raw token, which we'll use inside the extended payload for the media upload API call. 

As you see in the code above, we simply pass wp_user and wp_pw to generate a unique token.

5° In wordpress_scripts. py, create the media upload function

def postmedia (url, media, filename,wordpress_auth_media):

    Headers =  {
        'Authorization': f'Basic {wordpress_auth_media}',
        "Content-Disposition": f"attachment; filename={filename}.jpg",
        'Content-Type': 'multipart/form-data',
        'Cache-Control' : 'no-cache'
                }

    api_url = f"https://{url}" + "/wp-json/wp/v2/media/"

    mediaImageBytes = open(media, 'rb').read()

    response = requests.post(api_url, headers=Headers, data=mediaImageBytes)

    results = response.json()

    return results["id"]

For this function, we'll pass the url of the WordPress site (in the root format, e.g. mydomain.com) which will be used to generate the api_url endpoint. We pass a reference to the media file location (between quotes, e.g. "mymedia.jpg" ). 

👉If the media isn't located in the same folder as the WP Python script, make sure you indicate the correct path, e.g. "images/mymedia.jpg". 

We also pass a filename (the name we'll give to the uploaded file). Of course those elements can include variables if you're uploading multiple images in a row. For instance for the image location: f"/images/{dynamic_name}_image.jpg" where dynamic_name will correspond to the title of each individual image.

In the Headers section we're using the wordpress_auth result returned by our wordpress_header authentication function, passed as wordpress_auth_media

As previously said, since our wordpress_header function returns what we call a tuple (multiple results inside parentheses), in our wordpress_playground.py file we'll need to extract the second part of the tuple before using it in this postmedia function. That's why I used a slightly different name.

This is the way you do it (in the playground Python file where you're calling the functions). 

wordpress_auth_media = wordpress_header(wp_user, wp_pw)[1]

[1] for the second part of the tuple (0 = first result, 1 = second result)

Before moving back to our wordpress_scripts.py file, we will also extract the first part of the tuple, which we'll use for the WordPress post creation script.

wordpress_head_post = wordpress_header(wp_user, wp_pw)[0]

[0] for the first part of the tuple, it's that easy. 

We're almost ready, let's just add one last function in wordpress_scripts.py

6° In wordpress_scripts. py, write the post creation function

def create_wordpress_post(url, title, body,image_id, categories, tags, wordpress_header_post):

 api_url = f'https://{url}/wp-json/wp/v2/posts'

#data structure of the post, in JSON
 data = {
"title": title,
"content": body,
"featured_media": image_id,
"status": "publish",
"categories": categories,
"tags": tags
 }

 response = requests.post(api_url, headers=wordpress_header_post, json=data)

 result = response.json()

 #print(result) You can uncomment this line if you want to print a confirmation of each post creation in the console

 return result

As you can see in the code above, we pass a few variables in the ( ) of the function: 

  • the url (root domain) of our site (e.g. "mydomain.com")
  • the title of the post (e.g. "My great first post generated with Python")
  • the body of the post (e.g. "This is the body of my first awesome post. Sorry it's a bit short")
  • the image_id (not the image binary itself, but the ID integer which we must retrieve from the postmedia function, I'll show you in a minute how to do it, e.g. "5")
  • the categories and tags for this post, which must be integer references for categories and tags PREVIOUSLY created via the WordPress admin (you can't pass category or tag names as strings in the API call), e.g. "11" if the category reference ID is ["11"]). Note that those IDs must be passed as a list, between square brackets (even if you're just passing a single category), e.g.  ["11"] for category 11 or  ["11", "23"] for categories 11 and 23, etc.
  • wordpress_header_post = the header retrieved from the tuple returned by the wordpress_header function. 

All the data will be posted to the endpoint in JSON format (hence json=data in the POST call ( )).

That's it. Now you're ready to use those simple functions in any other python file. For instance in the wordpress_playground.py file you've just created for this tutorial. 

Let's see how we can create our first WordPress post in a snap. 

7° Create your first WordPress post using Python

This is how your wordpress_playground.py file currently looks like:

wp_user = "admin@mydomain.com"
wp_pw = "e5wp oFZo txIA VZ5V zSp2 QGzE"
wordpress_head_post = wordpress_header(wp_user, wp_pw)[0]
wordpress_auth_media = wordpress_header(wp_user, wp_pw)[1]

First we need to import our WordPress functions from wordpress_scripts.py by inserting a few lines at the top of our wordpress_playground.py file

from wordpress_scripts import wordpress_header
from wordpress_scripts import postmedia
from wordpress_scripts import create_wordpress_post

That way we won't need to copy-paste the WP functions inside of our playground file. And you'll be able to re-use those functions in other Python files. Just make sure to reference the path if your wordpress_scripts.py file isn't located in the same folder as the file where you want to use your WP functions. For instance if you store this wordpress_scripts.py file in a functions folder, you'll declare it this way from functions.wordpress_scripts import wordpress_header etc.

So at this stage, this is what wordpress_playground.py should look like with our 3 imports:

from wordpress_scripts import wordpress_header
from wordpress_scripts import postmedia
from wordpress_scripts import create_wordpress_post

wp_user = "admin@mydomain.com"
wp_pw = "e5wp oFZo txIA VZ5V zSp2 QGzE"
wordpress_head_post = wordpress_header(wp_user, wp_pw)[0]
wordpress_auth_media = wordpress_header(wp_user, wp_pw)[1] 

For the purpose of this tutorial we'll declare a few extra variables which will be used to upload one single image (which will become the featured image of our first post) and create one single post. You can of course use dynamic data stored in a Google Sheet, a Notion database or an Airtable DB to upload multiple images and create multiple posts in a row. 

In another article, I'll show you how to easily integrate Python with Google Sheets but for this tutorial, let's play with single data items declared in our playground Python file. 

url = "mydomain.com" #you set the root domain of your WordPress site
media = "images/myimage.jpg" #you set the location of the image you want to upload
filename = "my image name" #you set the name of your image
title = "My great first post generated with Python" #you set the title of your first WordPress post
body = "This is the body of my first awesome post. Sorry it's a bit short"
categories = [11] #11 being for instance the "Coding" category on your site
tags = [2] #2 being for instance the "Python" tag on your site

👉 Remember that we've already defined wordpress_head_post and wordpress_auth_media 

In thoses 2 lines we already called the wordpress_header function from our scripts.py file to return the data we need to authenticate: 

wordpress_head_post = wordpress_header(wp_user, wp_pw)[0]
wordpress_auth_media = wordpress_header(wp_user, wp_pw)[1] 

So now we're ready to upload an image, retrieve its ID and create our first post. Let's do it! 

Our first call will upload the image and retrieve the ID.

image_id = postmedia(url, media, filename, wordpress_auth_media)
#print(image_id) uncomment to print the image ID returned by the postmedia function

At this stage, our image file should be in the media section of our WordPress site (accessible via mydomain.com/wp-admin/upload.php). And image_id should equal the unique ID of that image. You can always print(image_id) to check it out. It should display an integer (e.g. "5'")

Our second call will use the image_id and call the API to generate our first post. How exciting! 

create_wordpress_post(url, title, body, image_id, categories, tags, wordpress_head_post)

Now you should see a brand new post in mydomain.com/wp-admin/edit.php

Voilà, you've created your first WordPress post using a Python script. 

Now you can integrate this beautiful workflow with your own data sources and dynamically populate the ( ) of the postmedia and create_wordpress_post functions to upload images and create multiple posts in a row. 

It's important to FIRST upload the image(s) and THEN create the post(s) since you need the image_id for the featured media in the data JSON payload of your create_wordpress_post function.

Your Python to WordPress files, ready to be used

Let's wrap up with the full python files we've created in this tutorial.

wordpress_scripts.py 

import requests
import base64

#token creation function, for authentication purposes
def wordpress_header (wp_user, wp_pw):

 wordpress_credentials = wp_user + ":" + wp_pw
 wordpress_token = base64.b64encode(wordpress_credentials.encode())
 wordpress_header = {'Authorization': 'Basic ' + wordpress_token.decode('utf-8')}
 wordpress_auth = wordpress_token.decode('utf-8')

 return wordpress_header, wordpress_auth


#post media function
def postmedia (url, media, filename,wordpress_auth_media):

    Headers =  {
        'Authorization': f'Basic {wordpress_auth_media}',
        "Content-Disposition": f"attachment; filename={filename}.jpg",
        'Content-Type': 'multipart/form-data',
        'Cache-Control' : 'no-cache'
                }

    api_url = f"https://{url}" + "/wp-json/wp/v2/media/"

    mediaImageBytes = open(media, 'rb').read()

    response = requests.post(api_url, headers=Headers, data=mediaImageBytes)

    results = response.json()

    return results["id"]

#post creation function
def create_wordpress_post(url, title, body,image_id, categories, tags, wordpress_header_post):

 api_url = f'https://{url}/wp-json/wp/v2/posts'

#data structure of the post, in JSON
 data = {
"title": title,
"content": body,
"featured_media": image_id,
"status": "publish",
"categories": categories,
"tags": tags
 }

 response = requests.post(api_url, headers=wordpress_header_post, json=data)

 result = response.json()

 #print(result) You can uncomment this line if you want to print a confirmation of each post creation in the console

 return result

wordpress_playground.py

from wordpress_scripts import wordpress_header
from wordpress_scripts import postmedia
from wordpress_scripts import create_wordpress_post

wp_user = "admin@mydomain.com"
wp_pw = "e5wp oFZo txIA VZ5V zSp2 QGzE"

url = "mydomain.com" #you set the root domain of your WordPress site
media = "images/myimage.jpg" #you set the location of the image you want to upload
filename = "my image name" #you set the name of your image
title = "My great first post generated with Python" #you set the title of your first WordPress post
body = "This is the body of my first awesome post. Sorry it's a bit short"
categories = [11] #11 being for instance the "Coding" category on your site
tags = [2] #2 being for instance the "Python" tag on your site

#authentication
wordpress_head_post = wordpress_header(wp_user, wp_pw)[0]
wordpress_auth_media = wordpress_header(wp_user, wp_pw)[1] 

#image upload
image_id = postmedia(url, media, filename, wordpress_auth_media)

#post creation
create_wordpress_post(url, title, body, image_id, categories, tags, wordpress_head_post)

Don't hesitate to ask any additional questions you might have via the comments section below.

Have fun with Python and WordPress! 

PS: you can isolate your credentials (wp_user, wp_pw) as environment variables in a .env file at the root of your project if you don't want to include them in your .py file. The process is well explained in this Medium piece.





Comments