🚀 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

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.

  1. Navigate to “Tools” > “Import”.
  2. 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.
  3. On the next screen, click “Choose File” and select your newly created `wordpress-import.xml`.
  4. Click “Upload file and import”.
  5. 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.

Darian Vance - Lead Cloud Architect

Darian Vance

Lead Cloud Architect & DevOps Strategist

With over 12 years in system architecture and automation, Darian specializes in simplifying complex cloud infrastructures. An advocate for open-source solutions, he founded TechResolve to provide engineers with actionable, battle-tested troubleshooting guides and robust software alternatives.


🤖 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

Discover more from TechResolve - SaaS Troubleshooting & Software Alternatives

Subscribe now to keep reading and get access to the full archive.

Continue reading