Convert DOCX to Text by Unzipping XML in Python

Extract plain text from a .docx file by unzipping the container and parsing word/document.xml with regex, using only Python's standard library.

Medium Python 3.9+ Aug 9, 2026 Automation & scripting 11 views 0 copies

Python code

56 lines
Python 3.9+
import zipfile
import re
from pathlib import Path

def docx_to_text_unzip_xml(docx_path: str) -> str:
    """Extract plain text from a .docx file by unzipping and parsing document.xml."""
    docx_path = Path(docx_path)
    if not docx_path.exists():
        raise FileNotFoundError(f"File not found: {docx_path}")

    with zipfile.ZipFile(docx_path, 'r') as z:
        # Read the main document XML from the DOCX container
        with z.open('word/document.xml') as xml_file:
            xml_content = xml_file.read().decode('utf-8')

    # Remove XML tags, extracting text content between paragraphs
    # Split into paragraphs first, then strip tags from each
    paragraphs = re.findall(r'<w:p[^>]*>(.*?)</w:p>', xml_content, re.DOTALL)
    text_parts = []
    for para in paragraphs:
        # Remove all XML tags within the paragraph
        text = re.sub(r'<[^>]+>', '', para)
        # Unescape common XML entities
        text = text.replace('&amp;', '&').replace('&lt;', '<').replace('&gt;', '>')
        text = text.replace('&quot;', '"').replace('&apos;', "'")
        text_parts.append(text.strip())

    return '\n'.join(text_parts)

if __name__ == "__main__":
    # Mock example: create a minimal in-memory docx and test extraction
    import io
    from xml.sax.saxutils import escape

    # Build a simple docx-like zip structure in memory
    buf = io.BytesIO()
    with zipfile.ZipFile(buf, 'w') as z:
        doc_xml = (
            '<?xml version="1.0" encoding="UTF-8"?>'
            '<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">'
            '<w:body>'
            '<w:p><w:r><w:t>Hello &amp; welcome!</w:t></w:r></w:p>'
            '<w:p><w:r><w:t>Second line with &lt;tag&gt;.</w:t></w:r></w:p>'
            '</w:body>'
            '</w:document>'
        )
        z.writestr('word/document.xml', doc_xml)

    buf.seek(0)
    # Write to temp file and extract
    temp_path = Path('mock_doc.docx')
    temp_path.write_bytes(buf.getvalue())

    result = docx_to_text_unzip_xml(str(temp_path))
    print(result)
    temp_path.unlink()  # cleanup

Output

stdout
Hello & welcome!
Second line with <tag>.

How it works

A .docx file is a ZIP archive containing XML files, with the main content in word/document.xml. The code opens the archive with zipfile, reads that XML, and splits it into paragraphs using the tags. Each paragraph is stripped of remaining XML tags with a regex substitution, then XML entities like & and < are unescaped back to their literal characters. Joining the cleaned paragraphs with newlines produces readable text without needing third-party libraries.

Common mistakes

  • Forgetting to handle namespaces — some DOCX files use namespace prefixes like w: that the regex must account for
  • Assuming all text is in direct <w:t> tags — content can be nested or split across multiple runs within a paragraph
  • Not unescaping XML entities, which leaves &amp; and &lt; in the output

Variations

  1. Use lxml or xml.etree.ElementTree to parse the XML structurally instead of regex
  2. Install python-docx and use the higher-level Document API for a more robust solution

Real-world use cases

  • Building a search index by extracting content from a batch of uploaded .docx documents without heavyweight dependencies.
  • Automating legal or HR document workflows where text must be pulled into a database or reporting tool.
  • Creating plain-text backups or previews of Word documents in a document management system.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Automation & scripting

Related tutorials and quizzes for this topic.