Skip to content

fix(converter): stop deleting the article's own title, byline and tags - #89

Open
Agnik47 wants to merge 1 commit into
Anakin-Inc:masterfrom
Agnik47:fix/converter-strips-article-header
Open

fix(converter): stop deleting the article's own title, byline and tags#89
Agnik47 wants to merge 1 commit into
Anakin-Inc:masterfrom
Agnik47:fix/converter-strips-article-header

Conversation

@Agnik47

@Agnik47 Agnik47 commented Aug 14, 2026

Copy link
Copy Markdown

The bug

HTMLToMarkdown strips boilerplate before extracting the main content:

doc.Find("script, style, noscript, iframe, svg, nav, footer, header").Remove()

doc.Find matches at any depth, so this does not just remove site chrome — it also removes the <header> and <footer> that semantic HTML nests inside the content:

<article>
  <header><h1>Post Title</h1><p>By Jane Doe</p></header>
  <p>…the actual post…</p>
  <footer><p>Filed under Testing</p></footer>
</article>

That is the standard layout for blog posts, docs pages and news articles (WordPress, Ghost, Hugo, MDN, most CMS themes). The result: every scrape of such a page silently loses the post's <h1> title, its byline and its tags. The markdown comes back as body text with no heading at all — and the title is usually the single most valuable field for anyone consuming markdown.

Reproduction

Against master, the test added in this PR produces:

expected markdown to contain "# Post Title", got: "The body of the post. The body of the post. …"
expected markdown to contain "By Jane Doe", got: "The body of the post. …"
expected markdown to contain "Filed under Testing", got: "The body of the post. …"

The title, byline and tags are gone; only the paragraph survives.

The fix

Split the removal into two rules:

  • Always removed, at any depthscript, style, noscript, iframe, svg, nav. These never carry readable content, and a <nav> is navigation wherever it appears, so its behaviour is unchanged.
  • Removed only at page levelheader, footer. These are dropped unless they sit inside a main-content container.

"Main-content container" reuses the exact selector list extractMainContent already uses (main, article, [role='main'], #content, #main-content, .content, .main-content), lifted into a package-level mainContentSelectors so the two cannot drift apart. The check uses ParentsFiltered, which walks ancestors only — so an element like <header id="content"> still counts as chrome rather than matching itself.

Behaviour only changes for <header>/<footer> nested inside a main-content container. Pages with no such container (where the cleaner falls back to the whole <body>) strip exactly what they stripped before.

Tests

Three subtests added to TestHTMLToMarkdown:

  1. article header and footer survive chrome stripping — the article's <header>/<footer> are kept while the page-level ones are dropped. Fails on master.
  2. page chrome is removed even when the body fallback is used<main> is deliberately under the 100-character threshold so cleaning falls back to the whole body, proving page chrome is already gone by then and the nested header is not. Fails on master.
  3. nav is removed even inside the main content — pins the deliberately unchanged <nav> behaviour. Passes both before and after.

Verification

Run against Go 1.26 locally:

  • go build ./... — clean
  • go vet ./... — clean
  • go test ./... — all packages pass
  • gofmt -l — clean on both changed files

The existing boilerplate tags are removed and nav content is removed subtests still pass unchanged.

No open issue or PR covers internal/converter.

HTMLToMarkdown removed every <header>, <footer> and <nav> on the page
before extracting the main content. Semantic HTML nests two of those
inside the content itself: <article><header> carries the post's <h1> and
byline, and <article><footer> carries its tags and author bio. Stripping
them globally deleted the title of the very page being scraped, which is
the layout used by most blog, docs and news pages.

Scope the header/footer removal to elements that are not inside a
main-content container, reusing the same selector list extractMainContent
already uses so the two stay consistent. <nav> is navigation at any
depth, so it keeps being removed everywhere.

Tests cover an article whose header/footer survive while the page-level
ones are dropped, the body-fallback path where the main content is under
the 100-character threshold, and a nav nested in an article to pin the
unchanged behaviour.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant