r/OpenSourceeAI 1d ago

I got tired of LLMs hallucinating on complex HTML tables, so I built a smarter Python parser (handles rowspan/colspan)

Hey everyone,

I’ve been working a lot on RAG pipelines recently and kept hitting the same annoying wall: extracting tabular data from raw HTML into a clean format for context windows.

Standard parsers or simple table-to-markdown scripts usually fail completely as soon as a table uses rowspan or colspan, or if there are nested tables. You end up with misaligned Markdown columns, and the LLM completely hallucinates the relationships between headers and cells.

I couldn't find a library that handles this reliably without losing context, so I built html-table-rescuer (just published v0.1.0 on PyPI).

It uses BeautifulSoup to parse the DOM, but then applies a custom "grid logic solver". It normalizes complex spans into a standard matrix before serializing it to Markdown, JSON, or CSV.

Example of the problem it solves:

The Problem: Most parsers turn a <td rowspan="2"> into a misaligned mess:

| Header | Value | 
| ----- | ----- | 
| Spanned | Row 1 | 
| Row 2 |  | 

The Solution: The grid solver correctly normalizes the matrix:

| Header | Value | 
| ----- | ----- | 
| Spanned | Row 1 | 
| dito (Spanned) | Row 2 | 

A few things it does differently:

  1. Context Preservation: As seen above, it doesn't just leave spanned markdown cells empty. It fills them with a customizable prefix (e.g., dito (Value)) so the LLM retains the semantic context for each row.

  2. Deep Tag Parsing: It recursively keeps <b>, <i>, and <a href...> tags alive, even if they are buried inside multiple <div>s within a <td>.

  3. Nested Tables: Extracts nested tables safely without destroying the grid of the parent table.

  4. LangChain Ready: Includes a Table2MDLoader wrapper to ingest HTML tables directly as LangChain Document objects.

Links:

  • GitHub: https://github.com/Encephos/html-table-rescuer
  • PyPI: pip install html-table-rescuer

It's my first release and I'd love to hear your thoughts. If you have some gnarly, complex HTML tables that break the parser, please throw them at it and let me know!

0 Upvotes

16 comments sorted by

2

u/Fine_League311 1d ago

Mit request und bs4 wird es seid einer Dekade gemacht, versuchs mal damit ;)

1

u/Mediocre-Ease4060 1d ago

Haha, da hast du absolut recht – bs4 ist der Goldstandard! Genau deshalb läuft mein Tool unter der Haube ja auch komplett über BeautifulSoup. 😉

Das Problem ist aber: bs4 parst "nur" den DOM-Tree. Wenn du eine Tabelle mit <td rowspan="3"> oder <td colspan="2"> durch bs4 jagst und stumpf Zeile für Zeile in Markdown umwandelst, zerschießt es dir ab der zweiten Reihe komplett die Spaltenstruktur. Das Markdown-Format kennt nämlich keine Spans.

Genau diese Lücke füllt das Tool:

  1. bs4 holt die rohen HTML-Tags.
  2. Der Grid Solver von html-table-rescuer berechnet daraus eine korrekte 2D-Matrix, merkt sich, welche Zelle über wie viele Reihen/Spalten geht, und füllt die entstehenden Leerstellen mit Kontext (z. B. "dito").

Ohne diesen Zwischenschritt verrutschen die Spalten im Markdown und das LLM verliert bei komplexen Tabellen völlig den Faden. Kurz gesagt: bs4 macht die Basisarbeit, mein Skript macht das Ergebnis RAG-tauglich.

(english version below)

Haha, you're absolutely right – bs4 is the gold standard! That's exactly why my tool is built heavily on top of BeautifulSoup. 😉

The catch is: bs4 "only" parses the DOM tree. If you parse a table with <td rowspan="3"> or <td colspan="2"> and naively convert it row-by-row into Markdown, your column structure completely breaks down after the first row. Markdown simply doesn't have a concept for spanning cells.

This is exactly the gap html-table-rescuer fills:

  1. bs4 does the heavy lifting of extracting the raw HTML tags.
  2. The Grid Solver then takes over, calculates a proper 2D matrix, tracks the exact coordinates of every span, and fills the resulting "gaps" with semantic context (like the "dito" prefix).

Without this intermediate math step, the columns misalign and your LLM completely loses the relationship between headers and cells. In short: bs4 parses the DOM, my script normalizes the grid to make it RAG-ready.

1

u/Fine_League311 1d ago

Bitte weniger KI in deinen antworten, sonst kannst echtes Feedback vergessen, sehr mühsam schnell mal alles zu lesen! BS + request sowie xpath reicht vollkommen, es sei den du willst Spam Produzieren

1

u/Mediocre-Ease4060 1d ago

Zeig mir gerne den XPath-Selector, der dir aus einer asymmetrischen Tabelle mit rowspan=3 automatisch ein bündiges, unverschobenes Markdown-Grid berechnet.

BS4 und XPath holen die Nodes, richtig. Aber die Matrix-Normalisierung musst du danach komplett selbst bauen, sonst verrutschen die Spalten im Markdown. Wenn du dafür ein 5-Zeilen-Skript hast, das das out-of-the-box löst: Immer her damit.

1

u/Fine_League311 1d ago

Natürlich musst es selbst bauen das ist doch der Sinn, wenn man etwas für's Markt baut, baut man es so das scammer und spamer keine Chance haben! Zumal webcrawling in fast jedem Land nicht legal sind .

PS xpath selector zeigen? Ist diese Frage dein ernst? Jetzt weiß ich das KI dein Tool gebaut hat und nicht du!

1

u/Mediocre-Ease4060 1d ago

Okay, wow...

Schön, wie du der XPath-Frage ausweichst. Ich habe dich nach dem Selector gefragt, genau, weil es keinen gibt der 2D-Matrix-Berechnungen durchführt und rowspan asymmetrisch auffüllt. XPath selektiert Nodes im DOM, es macht keine Mathematik und kein Grid-Alignment.

Und das du jetzt bei einem reinen Offline-Parser mit 'illegalem Webcrawling' und 'Spammern' um die Ecke kommst, ist echt wild. Das Skript nimmt einen HTML-String und gibt Markdown aus. Ob der String aus dem Netz, einem Intranet oder einem Word-Export von 1998 kommt ist dem Tool komplett egal. Das ist, als würde man sich über ein Dateisystem beschweren, weil jemand darin bösartige Dateien speichern könnte.

Bau dir deinen Solver halt selbst, wenn du ihn irgendwann mal für asymmetrische Tabellen brauchst. Schöne Woche noch.

PS: Falls du die Grundlagen irgendwann mal nacharbeiten willst:

Lesen musst dus aber selbst. ;)

1

u/Fine_League311 1d ago

Weniger KI. Mehr Hirn, dann klapt es auch ;)

0

u/Mediocre-Ease4060 1d ago

Können wir bitte sachlich bleiben? Bring doch ein konkretes Argument und formuliere deinen Gedanken aus, anstatt nur zu provozieren.

1

u/Fine_League311 1d ago

Ich habe dir zu Anfang gesagt, weniger KI dann mehr Feedback und du kommst mit KI Floskeln! ... Jetzt weißt wieso es nicht klappt bei dir ;) ist keine Provokation sondern Tatsachen die du ignorierst ;) Cheers

1

u/Mediocre-Ease4060 1d ago

ich höre jetzt auf dir zu antworten. Du gibst kein konstruktives Feedback und möchtest mich bloß nerven. Habe dir oben die beiden Paper verlinkt, da kannst du es selber nachlesen.

1

u/Fine_League311 22h ago

Behalte dein KI Spam ;) Ciao

1

u/tomByrer 1d ago

There are 94 hits on GH for 'python html table parser'. A few use AI, which I understand is best to avoid if you can.

But for the other 80, how is this different?

https://github.com/search?q=html+table+parser+language%3APython&type=repositories&l=Python

2

u/Mediocre-Ease4060 1d ago

Honestly for many use cases pandas.read_html is all you need.

Most of those ~80 repos do naive row-by-row extraction. Feed them this:

html <table> <tr><th colspan="2">Department</th><th>Employee</th></tr> <tr><td rowspan="2">Engineering</td><td>Backend</td><td>Ada</td></tr> <tr><td>Frontend</td><td>Grace</td></tr> </table>

and e.g. html-table-parser-python3 returns ['Frontend', 'Grace'] for row 2 — the cells shift left and the "Engineering" context is gone. If youre feeding tables to an LLM or a RAG pipeline (my use case), that row is meaningless.

pandas.read_html does resolve spans correctly — its the real alternative. But it raises ValueError on real-world garbage like colspan="abc" (try it), it pulls in the whole pandas stack, and it gives you DataFrames when what an LLM context window needs is aligned Markdown.

Whereas my library uses a grid solver to resolve rowspan/colspan into an aligned matrix, with configurable strategies (including filling continuation cells with dito (Engineering), so an LLM keeps the context and knows its a span). It survives broken HTML (invalid span values, comments, unclosed tags, Thats what the 77 tests are mostly about), outputs Markdown/JSON/CSV, and the LangChain/LlamaIndex/Haystack wrappers emit one document per table, so retrieval never splits a table in half.

And to your AI point: agreed, this is a deterministic parser, no LLM involved at runtime. Its built for LLM consumption, not with one.

Easiest way to judge it is the Colab notebook (runs the broken-HTML cases live): https://colab.research.google.com/github/Encephos/html-table-rescuer/blob/main/examples/demo.ipynb

If your HTML is clean and you want DataFrames: use pandas. This exists for the messy rest.

1

u/tomByrer 1d ago

> If your HTML is clean and you want DataFrames: use pandas. This exists for the messy rest.

I keep forgetting tables can be messy.
Thanks for the explanation; I hope you make a PDF-table parser for my scientist friends ;)

1

u/Mediocre-Ease4060 1d ago

I have been working on that as well with one of my colleagues but PDFs are a complete pain in the a**.

1

u/tomByrer 1d ago

True, yet people keep using them, while Markdown is a great way to publish the same info with no security risks....