How to Use Python to Post on Popular Blogging Websites
Tags: python, tutorial, automation, computerscience
Published: 2022-09-25 23:15:36


## Create once, publish multiple times. Automate the manual work with Python 💯

In this article, we will use Python to automate the following tasks

- Creating gists for your code snippets
  
- Embed gists into the article with different requirements for Wordpress, Medium, Dev.to, Hashnode

- Post the Article on WordPress
  
- Post the Article on Medium with WordPress Canonical Link
  
- Post the Article on Dev.to with WordPress Canonical Link
  
- Post the Article on Hashnode with WordPress Canonical Link

Essentially we will be using Python to automate cross-posting to multiple websites. We will be using various APIs for doing the above tasks.

You can find all the code [in my GitHub repo](https://github.com/rahulbanerjee26/Automate_Cross_Poster)

## Setting Up the Project 💻

First, create a new folder for the project

```bash
mkdir crossPoster
cd crossPoster
```

Next, create a virtual environment and activate it

```bash
python3 -m venv venv

source venv/bin/activate
```

Install the required libraries

```bash
pip3 install requests, Markdown, python-dotenv
```

Create a file named '.env' in your root directory. This will be used to store all your credentials. You will also need a test markdown file to be able to cross-post. In this tutorial, I am going to assume that all the images have public URLs and are not loaded from local files.

## 1️⃣ How to use GitHub API to create Gists for Code Snippets

Before proceeding, we will need a GitHub token. Go to [your developer settings token page](https://github.com/settings/tokens) to get your GitHub token. When creating the token, make sure the option "Create Gist" is checked. This allows you to create gists using your GitHub token. Copy the token and save it in your .env file

```bash
GITHUB_TOKEN = "YOUR_GITHUB_TOKEN"
```

First, we will need to write a function to get all the code snippets from a markdown file. We will use a Regex pattern to get the code snippet blocks. For now, ignore the function "getSnippetName", just assume it returns a tuple consisting of the file name and actual code. If the code snippet has a language "bash", we ignore it. You can extend the list of languages to ignore.

{% embed https://gist.github.com/cff9b71e7f099d54782320231e6a325c %}

We are going to assume that the user will begin their code snippet with a comment and the comment will have the name of the gist (basically like the above code snippet). If no such comment is provided, the gist will have a default name, Eg: index.js for a javascript file, init.py for a python file. Now let's look at the "getSnippetName" function

{% embed https://gist.github.com/4ace813b3677627566df0d14eeae4680 %}

It searches for a comment in the first line of the code snippet, if a comment is present it extracts the file name from the first line. If no comment is present it uses a dictionary to return the default file name. You can extend the code to support comments in other languages.

Now we have an array of tuples, the first element in the tuple is the name of the gist and the second element is the content of the gist. We can use this to create our gists.

{% embed https://gist.github.com/6bf4249c5eb01b79702378ecf9448a32 %}

We will make a post request to the following endpoint

```bash
https://api.github.com/gists
```

If you want the created gists to be private, you can set the parameter "public" to False. The function will return all the URLs of the created gists.

Now we need to replace the code snippets in the markdown file with the relevant gist URLs. Medium, Wordpress, Dev.to, and Hashnode have different formats to embed gists so let's create a dictionary for that

{% embed https://gist.github.com/33f4c2afee63fd8dc4fbd643a3d8a68b %}

Each of the lambda function will accept the gist URL as an argument and return the corresponding embed element. The logic to replace to code snippet is similar to the logic to get all the code snippets

{% embed https://gist.github.com/56a10ae348601e232a8a5aecff07f0e6 %}

We keep a counter to keep track of which code snippet corresponds to which gist URL. Since we ignored "bash" when creating the gists, we increment the counter by 1 without actually replacing anything. In all other cases we replace the code snippet with the gist URL at "counter" index and increment counter by 1. This function is going to create new files for Wordpress, Medium, Dev.to, Hashnode.

Check the below code snippet that uses the above-created functions. It will create public gists for the code snippets present in "index".md" and create a markdown for wordpress, a markdown for medium, etc

{% embed https://gist.github.com/675a40e657f98a75b09418b8c06bc201 %}

## 2️⃣ How to use the Wordpress API to Create a Post

You can find the [documentation here](https://developer.wordpress.org/rest-api/using-the-rest-api/global-parameters/)

You will have to install a plugin to generate an application password for your account. Search for the plugin [Application Password](https://wordpress.org/plugins/application-passwords/) and install it. After you install it, go to your `Users>Profile`. There should be an option called `Application Passwords` and a button to `Add new application password`. Give it a name and create a new application password. Copy the new password.

Update your .env file

```bash
WP_USERNAME = "YOUR ADMIN USERNAME"
WP_PASSWORD = "YOUR NEW APPLICATION PASSWORD"
```

Your wordpress username is the one that is shown in `Users>Profile`.

We will create a class to post to wordpress. The wordpress api endpoint is of the following format

```bash
{Your Website Domain}/wp-json/wp/v2/{route}
```

Check out the below code snippet that creates the class.

{% embed https://gist.github.com/fb83962aa07df96f08e9a40faca3196f %}

Now will have to add some methods to this class that will be used to post to WordPress.

The first method will be used to upload our cover image or header image to Wordpress.

{% embed https://gist.github.com/6aca4be55d0620c6fbcdd373edf5f241 %}

It has 3 inputs - the path to the image, alternative text, and the caption of the image. A post request has to be made to the following endpoint

```bash
{Your Website Domain}/wp-json/wp/v2/media
```

The response is going to have a field called `id`. This will be used when we are posting our article. It will have another field called `jetpack_featured_media_url` that contains the URL to the image.

Next, we need a method to create tag IDs for our article

{% embed https://gist.github.com/402ec15f9deb690cef17ac9614abca21 %}

It has one input - the list of tags. For each tag, it will try to create a new tag. The response is either going to contain the id of the newly created tag. If the tag already exists, the response will contain the existing tag id. We will combine the ids and return them.

A similar function can be created for categories as well.

{% embed https://gist.github.com/7dd4c6d75614eb20fb2fcf2cb4631050 %}

Finally, we will add a method to post to wordpress.

{% embed https://gist.github.com/7cc881d668c51a268e836088b65d0e97 %}

The function logic itself is pretty simple, let's look at the json data that we will be adding to our post request

- title: A string that is the title of your article
- content: Your article's content (in HTML, I'll show you how to convert your markdown file to HTML)
- status: A string "publish" or "draft" depending on if you want to post your article as a draft or not
- categories: The Category IDs returned by the `get_category_ids` method
- tags: The Tag IDs returned by the `get_category_ids` method
- featured_media: The ID of the cover/header image returned by the `upload_media` method

To convert your markdown file to HTML, we will use a library called `markdown`

```bash
pip3 install Markdown
```

We will need to write the following helper function to do the conversion

{% embed https://gist.github.com/ae80f1effa7949f2e828982ca1103e8c %}

Below is a sample usage of the `wordpress class`

{% embed https://gist.github.com/b339fb24ff8c121c4a31510c41ad20dc %}

If you are posting a draft, you can create a new variable to store the post slug. The post slug is the URL to your article after it has been posted. The slug should be present in the response object.

## 3️⃣ How to use the Medium API to Create a Post

We will be using Medium's REST API for this part. You can find the documentation [here](https://github.com/Medium/medium-api-docs).

To get your access token, go to your [profile settings](https://medium.com/me/settings) and go to `Integration Tokens`. Enter a description and click on the 'Get Integration Token' button.

Save the token in your .env file

```bash
MEDIUM_TOKEN = "Your Meidum Token"
```

Similar to the class we created for Wordpress, we will create a class for Medium as well.

{% embed https://gist.github.com/f5e75aa7f8d4eb74d68ccd6b6dee618e %}

Now let's define the `getUserID` method.

{% embed https://gist.github.com/efd9d0b10331c5bcd681da910bfd9ef0 %}

This simply makes a GET request to the following endpoint and returns your user ID. Note: this is different from your username.

```bash
https://api.medium.com/v1/me
```

Now, we can add the method to create a medium post

{% embed https://gist.github.com/47e8fa7fa99d45f1aff6cab8c6a71676 %}

Let's take a look at the json data added to the post request.

- title: The title of your article
- content: The content of your article in HTML
- publishStatus: A string 'draft' , 'published' or 'unlisted'
- contentFormat: In our case this will be 'HTML'
- tags: A list of strings that are the tags of the article.
- notifyFollowers: A boolean value
- canonicalUrl: If applicable, the canonical URL. In my case, I am going to set it to the WordPress Post's URL.
  
Now let's take a look at a sample usage of the class

{% embed https://gist.github.com/3537b42b047a776b53376465bc216b0e %}

## 4️⃣ How to use the Dev.to API to Create a Post

The Dev.to implementation is pretty similar and I am not going to go much in detail. You can find your token [here](https://dev.to/settings/extensions). Scroll to the bottom to generate a token and update your .env file

```bash
DEVTO_TOKEN = "GCP1itdmDAmVzfTjEQRRnnFc"
```

You can find the documentation [here](https://developers.forem.com/api/v0)

Below is the dev.to class

{% embed https://gist.github.com/3722a2ec4ad6da6eafe2dcdf79be653d %}

For dev.to, we do not need to convert our markdown file to HTML. We can directly use the markdown content.

## 5️⃣ How to use the Hashnode API to Create a Post

Hashnode has a GraphQL API and it took me quite some time to figure out the correct query and how to make the correct request. You should be able to directly use the code snippet shown in this section. You can generate your token [here](https://hashnode.com/settings/developer).

You can play around with their API over [here](https://api.hashnode.com/)

Below is the implementation of the Hashnode class

{% embed https://gist.github.com/380f8eb8ba01504d135349ce30d8a55b %}

If you are not familiar with GraphQL, try playing with the API in their API Playground. Let's take a look at some of the variables that are passed to the query

- title: The title of your article.
- content: Your article's content in markdown. Similar to dev.to, you do not need to convert to HTML.
- publicationID: This is a required parameter. If you do not add it to your query, your article won't be listed under your profile but it'll still be posted. It'll be under `hashnode.com` and not your personal hasnode blog or account. To find your publication ID, go to your dashboard and copy it from the URL

```bash
https://hashnode.com/{YOUR PUBLICATION ID}/dashboard
```

- tags: A list of tags
- canonicalUrl: The Canonical URL of the article
- hideFromHashnodeFeed: The API doesn't support posting drafts (or at least I couldn't find a way to post draft). Setting hideFromHashnodeFeed to True posts the article but it won't show up on anyone else's feed. You can toggle this in the hashnode editor as well.

## Future Improvements

You can find a sample usage of all the above classes to create an "automated pipeline" in this [file in the repo](https://github.com/rahulbanerjee26/Automate_Cross_Poster/blob/main/sample.py)

You can think of this as a POC to automate your process. It obviously doesn't cover all use cases. Feel free to clone the repo and edit it as you like. Here are some improvements that can be made

- Support for local images - The logic should be similar to how code snippets are converted to gists. Either one of the existing APIs can be used or a new API like imgurr can be used
- Extend this to automatically share your article to various subreddits or Twitter as well
- Add support for other blog editors

Please let me know in the comments if you found this article useful or if you have any suggestions on features that can be automated, improvements, etc.

```bash
This article was originally posted on 
https://www.realpythonproject.com/how-to-use-python-to-post-on-popular-blogging-websites/

Connect with me on LinkedIn: 
https://www.linkedin.com/in/rahulbanerjee2699/
```