How to Turn an HTML Table to CSV (and Open It Cleanly in Excel)
Copy a table off a web page and get clean CSV for Excel. How tr maps to rows, td to cells, why commas need quoting, plus a worked example.
How to Turn an HTML Table to CSV (and Open It Cleanly in Excel)
You found a table on a web page. Maybe it's a price list, a population breakdown, a leaderboard. You want it in a spreadsheet so you can sort it, filter it, run a formula across a column. The data is right there on the screen, but there's no "Download CSV" button anywhere in sight. Copy-pasting the rendered table into Excel either dumps everything into one cell or scatters it across columns that don't line up.
The fix is to take the raw HTML and convert it to CSV. CSV (comma-separated values) is the format every spreadsheet reads, so once you have it, Excel, Google Sheets, Numbers, and pandas all open it without a fight. This guide walks through how the conversion actually works — what maps to what, where it goes wrong, and how to get a file that opens cleanly.
Where the HTML comes from
Before any conversion, you need the table's HTML, not its rendered pixels. Three reliable ways to grab it:
- Inspect element. Right-click the table in your browser, choose "Inspect", find the
<table>node in the elements panel, right-click it, and copy the outer HTML. This gives you exactly the table and nothing else. - View source. For simple pages, View Source (or
Ctrl+U) shows the full document. Search for<tableand copy from there to the matching</table>. - Save the whole page. Save the page as HTML and paste the entire source. A good converter ignores the scripts, styles, and layout wrappers and pulls out only the
<table>elements.
Paste that into the HTML Table to CSV converter and the parsing happens in your browser tab. Nothing uploads, which matters when the page source carries email addresses, session-bound URLs, or internal hostnames you'd rather not hand to a stranger's server.
How tr becomes a row and td becomes a column
An HTML table is a grid described row by row. Each <tr> (table row) is one horizontal line of data. Inside it, each <td> (table data cell) or <th> (table header cell) is one column position. So the mental model is direct:
- One
<tr>becomes one line in the CSV file. - Each
<td>or<th>inside that<tr>becomes one comma-separated field on that line.
A row like <tr><td>Tokyo</td><td>Japan</td><td>37400068</td></tr> becomes the CSV line Tokyo,Japan,37400068. Three cells, three fields, two commas between them. Repeat for every <tr> and you have a CSV file. That's the whole idea — the rest is handling the cases where the grid isn't a tidy rectangle.
The header row is the one special case. If the source has a <thead> section, those cells become the column names. If it doesn't, a good converter looks at the first row: if every cell is plain text rather than a number, it treats that row as the header; if the first row is 1, 2, 3 or 12.5%, 8.0%, it leaves it as data. You can always override with "first row" or "no header" if the guess is wrong.
Escaping commas, quotes, and newlines
Here's the trap that breaks most quick-and-dirty conversions. CSV uses the comma as the field separator, so what happens when a cell contains a comma? Take an address cell: Tokyo, Japan. If you write it raw, the CSV reader sees two fields where you meant one, and every column after it shifts left by one.
The standard rule (RFC 4180) is: any cell containing a comma, a double quote, or a newline gets wrapped in double quotes. So Tokyo, Japan is written as "Tokyo, Japan". If the cell itself contains a double quote, that quote is doubled: She said "hi" becomes "She said ""hi""". A cell with a line break inside it (common when a <td> has a <br>) gets quoted too, so the newline stays inside the field instead of starting a new row.
A converter that knows this does the quoting for you. One that doesn't will silently corrupt any table with commas in it — and those are exactly the tables (addresses, names, descriptions) you most want to convert. If you'd rather sidestep quoting entirely, switch the output to TSV: tabs separate the fields, and since cells rarely contain tabs, nothing needs escaping. Paste TSV straight into Excel and each tab becomes a cell.
A worked example
Say a page has this table:
<table>
<thead>
<tr><th>City</th><th>Country</th><th>Notes</th></tr>
</thead>
<tbody>
<tr><td>Tokyo</td><td>Japan</td><td>Largest metro, by far</td></tr>
<tr><td>Delhi</td><td>India</td><td>Fast growing</td></tr>
</tbody>
</table>
Run it through the converter and the CSV output is:
City,Country,Notes
Tokyo,Japan,"Largest metro, by far"
Delhi,India,Fast growing
Notice the third cell on the Tokyo row. Largest metro, by far contains a comma, so it's wrapped in quotes. Fast growing has no comma, so it stays bare. The <thead> row became the header line, and each <tr> in the body became one data line. Open that file in Excel and you get three clean columns with no shifting.
Opening it cleanly in Excel
One more thing trips people up after the CSV is correct: encoding. If your data has non-ASCII characters — Chinese, Japanese, Korean, accented Latin, emoji — Excel may show garbage like ä instead of ä, or boxes instead of Chinese characters. This isn't a bug in your CSV. Excel's importer defaults to the local code page (CP-1252 on Western Windows, GB-18030 on Chinese Windows) unless the file starts with a UTF-8 byte-order mark (BOM).
The fix is to prepend three bytes (0xEF 0xBB 0xBF) to the file. The converter has a "BOM (Excel UTF-8)" checkbox for exactly this — turn it on, and double-clicking the CSV opens it in the right encoding. Your numbers and column data don't change; you're only telling Excel which character set to read.
I hit this myself converting a roster of Chinese vendor names. The CSV was correct in every text editor I opened it in, but Excel turned every name into mojibake, and I spent ten minutes convinced the conversion was broken before I remembered the BOM. Now I tick the box by reflex whenever the data isn't pure ASCII. It's the difference between a file that opens perfectly and one that looks corrupted to anyone who double-clicks it.
When to reach for JSON or Markdown instead
CSV is the right default, but it's not always the right destination. If you're feeding the data into code, switch the output to JSON: with a header row you get an array of {column: value} objects ready for JSON.parse or Python's json.loads. If the result needs cleanup or you want to inspect the structure, a quick pass through a JSON formatter makes it readable.
If the table is headed for a wiki or a README, choose Markdown output — you get a GitHub-flavored table with pipe characters inside cells safely escaped. And if you ever need to go the other way, building a table from scratch rather than extracting one, the Markdown table generator handles that side.
The pattern is the same across all of them: a <tr> is a row, a <td> is a cell, and a cell with a separator character inside it has to be escaped so the structure survives. Get those three things right and any web table becomes data you can actually work with.
Made by Toolora · Updated 2026-06-13