How to Clean AI-Generated HTML With Regex Without Damaging Real Content

Date:

You paste AI-drafted text into a page, run a few find-and-replace rules to strip the leftovers, and later notice a missing image or a sentence glued to a file extension. The rules removed the junk, and some of your content with it.

The short answer: to clean AI-generated HTML with regex safely, only delete patterns that can’t occur in real writing ([Image of …], empty tags), convert formatting instead of stripping it, and test every rule against lines you want to keep. In one test of a typical 15-rule cleanup set, 5 rules did their job and 6 changed legitimate content: one deleted a real image, one deleted a source citation, one turned the .htaccess file into the.htaccess file.
Two-panel bar chart of fifteen regex rules to clean AI-generated HTML, each tested on 18 labelled lines. Panel one sorts the rules by outcome: 5 removed only artifacts, 3 matched nothing in the sample (one of them cannot run in Google Sheets), 1 left a stray fragment behind, and 6 damaged legitimate content, marked as a problem. Panel two counts the legitimate lines each damaging rule changed: the italic rule 3, the caption-line rule 2, the space-before-punctuation rule 2, and the image-with-alt, double line break and bold rules 1 each.

Why a cleanup rule that looks right still breaks pages

A typical cleanup set for AI-drafted product or course descriptions, cleaned in a spreadsheet before import, covers seven jobs: remove image placeholders, remove stray captions, strip leftover markdown, delete empty tags, protect shortcodes, tidy whitespace, then check. Each rule is usually written while looking at the junk it should remove, so it passes that test by construction.

The missing test is the other direction: does the rule leave good content alone? To find out, the 15 patterns of such a set were run one at a time against 18 synthetic lines. Eight were artifacts an AI draft really leaves behind ([Image of a laptop], **bold** markers, an empty <p>). The other ten were legitimate lines a product page really contains (an image with alt text, a cited source, a file extension, an offer with a footnote star). Every line a rule changed was recorded.

A cleanup rule isn’t tested until it has run on text you want to keep.

The five that did their job

These matched only artifacts and left every legitimate line alone:

  • \[Image of[^\]]*\] and \[Image:[^\]]*\]: the bracketed image descriptions chat assistants put where a picture would go.
  • <p>\s*</p> and <h[1-6]>\s*</h[1-6]>: empty paragraphs and headings.
  • the inline-code rule, which removes a pair of backticks and keeps the words between them.

What they have in common is that the thing they match can’t be legitimate. Nobody writes [Image of …] on purpose.

The six that damaged real content

“Empty or broken” image tags. The pattern was <img[^>]*alt="[^"]*"[^>]*\/?>. It matches any image that has an alt attribute, which is every accessible image on the page. It deleted the sample course cover outright. An image is broken when its file is missing, and no pattern can see that. Alt text is a separate question; even an empty alt="" is correct for a decorative image.

Caption lines. ^(Image credit:|Source:|Figure\s+\d+:|Diagram showing).*$ was meant to catch captions orphaned by a removed image. It also deleted Source: Bureau of Labor Statistics, Occupational Outlook Handbook, 2025. and Figure 1: Enrollment by month (our own data). A line starting with “Source:” is usually how you credit data.

Space before punctuation. \s+([.,;:]) → $1 fixes word , but treats a leading dot as punctuation: Upload the .htaccess file and convert images to .webp format. became Upload the.htaccess file and convert images to.webp format., and .NET and .csv went the same way.

Italic markers. \*(.*?)\* → $1 removed the footnote stars from Free trial* for 7 days, *card required., the multiplication signs from width * height * depth, and the double stars from f(**kwargs). Three legitimate lines out of ten, the most of any rule.

Bold markers. \*\*(.*?)\*\* → $1 also turned f(**kwargs) and g(**options) into f(kwargs) and g(options). Code in prose is exactly where AI drafts put double stars on purpose.

Double line breaks. (<br\s*\/?>\s*){2,} replaced with nothing was meant to collapse extra breaks. Replacing with nothing deletes them, so Line one<br><br>Line two became Line oneLine two.

Six of the fifteen rules changed lines they should have left alone.

One left debris, one won’t run in Sheets

The markdown-image pattern !\[[^\]]*\]\([^\)]*\) stops at the first ). A Wikipedia image URL such as …/Logo_(2019).png has its own brackets, so the rule removed most of the line and left .png) behind.

The rule for __bold__ used (?<=\s) and (?=\s), a lookbehind and a lookahead. Google Sheets uses the RE2 regular-expression engine, which doesn’t support lookaround, so in a spreadsheet, where this rule set was meant to run, that pattern can’t be used at all. It matched nothing in the sample in Python either.

A safer rule set to clean AI-generated HTML

The safer set is built on two ideas: match only what can’t be legitimate, and convert formatting to its HTML equivalent rather than throwing it away. The bold and italic rules now need a space, a tag or punctuation on both sides, so f(**kwargs) and width * height no longer qualify, and double line breaks are left alone, because they’re often meant. Run in this order, twice, the nine rules below turned all 8 artifacts into the expected output and left all 10 legitimate lines byte-for-byte unchanged. They also passed 6 edge cases added afterwards: an italic word, stray spaces before punctuation, two bold words in one line, two bold runs separated by a single space, 2**10 inside a code block, and footnote stars after numbers.

1. Image placeholders   \[(?:Image|Figure|Diagram)(?: of|:)[^\]]*\]                        ->  (nothing)
2. Markdown images      !\[[^\]\n]*\]\((?:[^()\s]|\([^()\s]*\))*\)                       ->  (nothing)
3. Empty paragraphs     <p>\s*</p>                                                       ->  (nothing)
4. Empty headings       <h[1-6]>\s*</h[1-6]>                                             ->  (nothing)
5. Bold                 (^|[\s>])\*\*([^*\s][^*\n]*?[^*\s]|[^*\s])\*\*([\s<.,;:!?)]|$)    ->  $1<strong>$2</strong>$3
6. Italic               (^|[\s>(])\*([^*\s][^*\n]*[^*\s]|[^*\s])\*([\s<.,;:!?)]|$)        ->  $1<em>$2</em>$3
7. Inline code          `([^`\n]+)`                                                       ->  <code>$1</code>
8. Space before , ; :   [ \t]+([,;:])                                                     ->  $1
9. Space before a sentence-ending full stop   [ \t]+\.([ \t]|$)                           ->  .$1Code language: HTML, XML (xml)

None of them uses lookaround or a backreference inside the pattern, so all nine run in RE2. The price is the second pass: without lookaround, a bold or italic run that follows another one with a single space between them shares that space and is skipped the first time (**a** **b** needs two passes). Running the set again changes nothing that’s already converted. In Google Sheets, put each rule in REGEXREPLACE, which uses $1 for a captured group. In Python, write \1 instead.

Two of the old rules have no safe regex version, so they’re gone: broken images need someone to check the file exists, and an orphaned caption needs someone to see that its image is missing. Those lines go to a review list, not to a delete.

Detect instead of replace when you can

For text that matters, a safer alternative to auto-correcting is to flag residue and let a person fix it. A check that reads each published page and fails it on things that can’t be legitimate (chat citation markers, utm_source=chatgpt.com on links, an As an AI disclaimer, [Insert …] placeholders) never deletes a paragraph by mistake. It’s slower than a replace-all. The reasoning matches render bugs that pass every check: automated checks catch structure, and someone still has to read the result. The corrections no linter catches are the other half of that review.

Where you do clean in bulk, remember that cleaning later is the expensive end of the job. If the drafts come from a prompt, asking it not to emit image placeholders and markdown at all is cheaper than finding them afterwards.

The short version

A typical set of 15 regex rules for cleaning AI-generated HTML was tested against 10 legitimate lines. Five rules removed only artifacts. Six damaged real content: they deleted a real image and a source citation, merged lines, stripped footnote and multiplication stars, and glued words to file extensions. A rewritten set of nine rules passed all 18 lines and 6 edge cases. Delete only what can’t be legitimate, convert formatting instead of stripping it, keep a file of lines that must never change, and run every rule against it before you trust it.

Frequently Asked Questions

How do I remove image placeholders from AI-generated text?

Find \[(?:Image|Figure|Diagram)(?: of|:)[^\]]*\] and replace it with nothing. It matches the bracketed descriptions chat assistants insert where an image would go, such as [Image of a laptop] or [Image: chart], and leaves WordPress shortcodes like [caption] and [gallery] alone. It works in Google Sheets and in most regex engines.

Why does my regex work in Python but not in Google Sheets?

Google Sheets uses the RE2 engine, which leaves out some features other engines have. The usual culprits are lookbehind and lookahead ((?<=…), (?=…)) and backreferences inside the pattern. Rewrite the rule to capture the surrounding character in a group and put it back in the replacement with $1.

Share post:

Subscribe

Popular

More like this
Related

Replace the WordPress Logo With Your Site Icon, Everywhere It Shows

Changing the login logo is the well-known fix. The WordPress mark also sits in the toolbar, the login popup, every tab title and the admin footer, and on a multisite network each site should show its own icon, not one shared logo.

How to Show a WordPress Shortcode Without Running It

You write a tutorial that mentions a shortcode in a code example, and the published page shows something else: a gallery, a block of other posts, or an empty gap where the example should be. WordPress ran the shortcode you were only trying to mention.

How to Customize the tagDiv Newspaper Theme When the Accent Color Won’t Change

You change the Newspaper theme's accent color in the Theme Panel, save, and the header, menu and footer are still the old red. Then you try to adjust the phone layout outside the Composer and find every setting stored as base64-encoded JSON. Both have a reliable fix, step by step.

How to Update Rank Math SEO Fields Through the REST API Without Exposing Your Focus Keywords

You send a meta description to a post's REST API endpoint, WordPress replies with HTTP 200, and the field in the Rank Math panel is still empty. One function call per field fixes the write. Done the obvious way, it also publishes every focus keyword you chose.