🚀 Executive Summary
TL;DR: Disqus comments can significantly impact website performance and SEO due to external hosting and iframe loading. This guide provides a Python script-based solution to export Disqus comments, convert them into WordPress eXtended RSS (WXR) format, and import them into native WordPress comments, thereby improving Core Web Vitals and consolidating user-generated content ownership.
🎯 Key Takeaways
- Disqus comments can be exported as a gzipped XML file from the Disqus Admin panel under ‘Site’ > ‘Export’.
- A custom Python script, utilizing the `lxml` library, is necessary to transform the Disqus XML structure into the WordPress WXR format, ensuring proper mapping of posts to comments and preserving nested comment threads.
- The generated `wordpress-import.xml` (WXR file) is imported via the WordPress Admin Dashboard under ‘Tools’ > ‘Import’, requiring the WordPress Importer plugin to be installed.
Exporting Disqus Comments to WordPress Native Comments
Hey there, Darian Vance here. I was recently looking at a client’s site performance, and the network waterfall chart lit up like a Christmas tree every time the Disqus iframe loaded. It was a significant drag on our Core Web Vitals, and worse, all that valuable, user-generated content wasn’t even on our own domain. That’s when I decided it was time to repatriate those comments. Bringing them back into the native WordPress system not only sped up the site but also gave us full ownership and better SEO. It’s a weekend project that pays dividends for years. Let me walk you through how I do it.
Prerequisites
Before we dive in, make sure you have the following ready to go:
- Your Disqus comments XML export file. You can generate this from your Disqus Admin panel under “Site” > “Export”.
- Administrator access to your WordPress dashboard.
- Python 3 installed on your local machine.
- A good code editor.
The Guide: From Disqus XML to WordPress WXR
Step 1: Get Your Disqus Data
First things first, log into your Disqus account. Navigate to the admin section, select your site, and find the “Export” option. Kick off the export process. Disqus will email you a link to a gzipped XML file when it’s ready. Download and unzip it. You should have a file named something like yourforum-2023-10-27T12_00_00-all.xml. Let’s call this disqus-export.xml for simplicity.
Step 2: The Conversion Logic
Our goal is to convert the Disqus XML format into a WordPress eXtended RSS (WXR) file. This is the standard format WordPress uses for its import/export tool. The structure is different, so we can’t just import the Disqus file directly. We’ll need a script to map the fields correctly—specifically, mapping Disqus posts to WordPress comments and, crucially, preserving the nested comment threads.
Step 3: The Python Script
This is where the magic happens. We’ll use Python with the lxml library, which is fantastic for parsing XML. I’ll skip the standard virtualenv setup since you likely have your own workflow for that. Just make sure you install the required library. You can do this by running a command like python3 -m pip install lxml in your terminal.
Save the following code as disqus_converter.py and place it in the same directory as your disqus-export.xml file.
from lxml import etree
import datetime
# --- Configuration ---
DISQUS_XML_FILE = 'disqus-export.xml'
WORDPRESS_WXR_OUTPUT_FILE = 'wordpress-import.xml'
SITE_URL = 'https://your-website.com' # Your WordPress site URL
def convert_disqus_to_wxr():
print(f"Starting conversion of {DISQUS_XML_FILE}...")
# Define XML namespaces
NS_CONTENT = "http://purl.org/rss/1.0/modules/content/"
NS_WP = "http://wordpress.org/export/1.2/"
NS_DSQ = "http://disqus.com/"
NS_DC = "http://purl.org/dc/elements/1.1/"
NAMESPACES = {
'content': NS_CONTENT,
'wp': NS_WP,
'dsq': NS_DSQ,
'dc': NS_DC
}
# Create the root of the WXR file
wxr_root = etree.Element("rss", version="2.0", nsmap=NAMESPACES)
wxr_channel = etree.SubElement(wxr_root, "channel")
# Add some basic channel info
etree.SubElement(wxr_channel, "title").text = "Disqus Comments Import"
etree.SubElement(wxr_channel, "link").text = SITE_URL
etree.SubElement(wxr_channel, "description").text = "Comments exported from Disqus"
etree.SubElement(wxr_channel, "pubDate").text = datetime.datetime.now().strftime("%a, %d %b %Y %H:%M:%S +0000")
etree.SubElement(wxr_channel, "language").text = "en-US"
etree.SubElement(wxr_channel, etree.QName(NS_WP, "wxr_version")).text = "1.2"
# Parse the Disqus XML file
try:
disqus_tree = etree.parse(DISQUS_XML_FILE)
disqus_root = disqus_tree.getroot()
except IOError:
print(f"Error: Could not read file {DISQUS_XML_FILE}")
return
except etree.XMLSyntaxError as e:
print(f"Error: XML syntax error in {DISQUS_XML_FILE}: {e}")
return
# Create a mapping of thread IDs to post URLs
thread_map = {}
for thread in disqus_root.findall('dsq:thread', NAMESPACES):
thread_id = thread.get(etree.QName(NS_DSQ, 'id'))
link = thread.find('link').text
if thread_id and link:
thread_map[thread_id] = link
# Process each post (comment)
for post in disqus_root.findall('dsq:post', NAMESPACES):
thread_id = post.find('dsq:thread', NAMESPACES).get(etree.QName(NS_DSQ, 'id'))
post_url = thread_map.get(thread_id)
if not post_url:
continue # Skip comments not attached to a known thread
# Create a new item for each comment
item = etree.SubElement(wxr_channel, "item")
etree.SubElement(item, "title").text = "Comment"
etree.SubElement(item, "link").text = post_url
etree.SubElement(item, etree.QName(NS_WP, "post_type")).text = "post" # This is a placeholder
etree.SubElement(item, etree.QName(NS_WP, "status")).text = "publish"
# Create the comment element
comment = etree.SubElement(item, etree.QName(NS_WP, "comment"))
etree.SubElement(comment, etree.QName(NS_WP, "comment_id")).text = post.get(etree.QName(NS_DSQ, 'id'))
author_name = post.find('dsq:author/dsq:name', NAMESPACES)
if author_name is not None:
etree.SubElement(comment, etree.QName(NS_WP, "comment_author")).text = author_name.text
author_email = post.find('dsq:author/dsq:email', NAMESPACES)
if author_email is not None:
etree.SubElement(comment, etree.QName(NS_WP, "comment_author_email")).text = author_email.text
# Format date to GMT
created_at = post.find('dsq:createdAt', NAMESPACES).text.replace('T', ' ').split('.')[0]
etree.SubElement(comment, etree.QName(NS_WP, "comment_date_gmt")).text = created_at
message = post.find('dsq:message', NAMESPACES)
if message is not None:
etree.SubElement(comment, etree.QName(NS_WP, "comment_content")).text = etree.CDATA(message.text)
etree.SubElement(comment, etree.QName(NS_WP, "comment_approved")).text = "1"
parent = post.find('dsq:parent', NAMESPACES)
if parent is not None:
etree.SubElement(comment, etree.QName(NS_WP, "comment_parent")).text = parent.get(etree.QName(NS_DSQ, 'id'))
else:
etree.SubElement(comment, etree.QName(NS_WP, "comment_parent")).text = "0"
# Write the WXR file
with open(WORDPRESS_WXR_OUTPUT_FILE, 'wb') as f:
f.write(etree.tostring(wxr_root, pretty_print=True, xml_declaration=True, encoding='UTF-8'))
print(f"Success! Conversion complete. Output file: {WORDPRESS_WXR_OUTPUT_FILE}")
if __name__ == "__main__":
convert_disqus_to_wxr()
Pro Tip: Before you run the script, open it and change the `SITE_URL` variable to your actual website’s URL. This helps WordPress map things correctly. Also, for truly massive Disqus exports (I’m talking gigabytes), you might want to look into an iterative parser like `etree.iterparse` to avoid loading the entire file into memory. The script above works great for 99% of cases.
Run the script from your terminal with `python3 disqus_converter.py`. If all goes well, you’ll see a success message and a new file named `wordpress-import.xml` will appear.
Step 4: Importing to WordPress
This is the easy part. Go to your WordPress Admin Dashboard.
- Navigate to “Tools” > “Import”.
- Under “WordPress”, click “Install Now” if you haven’t already installed the importer plugin. Once it’s installed, the link will change to “Run Importer”. Click that.
- On the next screen, click “Choose File” and select your newly created `wordpress-import.xml`.
- Click “Upload file and import”.
- WordPress will process the file. It might take a few minutes for a large number of comments. You’ll see a success message when it’s done.
That’s it! Go to a few of your old posts. You should see all the Disqus comments right there in your native WordPress comments section, with threading preserved.
Common Pitfalls (Where I Usually Mess Up)
- URL Mismatches: The most common issue. If you’ve changed your domain or URL structure (e.g., from `http` to `https`), the links in the Disqus export might not match your current posts. The script won’t be able to map the comments. You might need to add a find-and-replace step in the Python script to update the `post_url` variable before it’s used.
- Character Encoding: XML files can be finicky. The script is set to output UTF-8, which is standard. If you see weird characters after import, your original export might have had an unusual encoding.
- Server Timeouts: If you have tens of thousands of comments, the WordPress importer might time out, especially on shared hosting. The solution is often to break your `wordpress-import.xml` into smaller chunks or temporarily increase the `max_execution_time` in your server’s PHP configuration.
Conclusion
Taking control of your comments is a powerful move. It improves site performance, consolidates your data, and boosts your SEO with user-generated content. This script-based approach gives you full control over the migration process. Once you’re done, you can safely deactivate the Disqus plugin and enjoy a faster, more integrated website. Happy coding.
🤖 Frequently Asked Questions
âť“ Why is it beneficial to migrate Disqus comments to WordPress native comments?
Migrating improves site performance by eliminating the Disqus iframe, enhances SEO by bringing user-generated content onto your domain, and grants full ownership and control over your comment data.
âť“ How does this script-based conversion compare to other potential migration methods?
This script-based approach offers granular control over the conversion process, allowing for precise field mapping and custom adjustments (e.g., for URL mismatches), which might not be achievable with generic plugins or less flexible import tools.
âť“ What is a common pitfall during the Disqus to WordPress migration and how can it be addressed?
URL mismatches are a common issue if your site’s domain or URL structure has changed. This can be resolved by modifying the Python script to include a find-and-replace step for the `post_url` variable, ensuring it matches your current WordPress site URLs.
Leave a Reply