HTML to CSV: get a web table into a spreadsheet
Tables on web pages hold a lot of useful data: rate cards, sports standings, release schedules, reference lists. Copying them with the mouse often scrambles columns, drags along links and icons, or breaks when cells span several rows. This html to csv converter reads the table markup itself and writes one CSV line per table row, so the grid arrives in your spreadsheet exactly as the page defines it.
You can paste an entire saved page; everything outside <table> elements is ignored. Scripts in the pasted markup are not run, and only the text of each cell is kept.
Example: merged cells expanded
Here is a small price table with a heading that spans two columns and a plan name that spans two rows. The first price cell also contains a link and an image:
<table id="rates">
<tr><th>Plan</th><th colspan="2">Price</th></tr>
<tr><td rowspan="2">Basic</td><td>Monthly</td><td>$9</td></tr>
<tr><td>Yearly</td><td>90</td></tr>
<tr><td><a href="pro.html">Pro</a> <img src="p.png"></td><td>Monthly</td><td>0019</td></tr>
</table>
The converter produces a rectangular grid:
Plan,Price,Price_2
Basic,Monthly,$9
Basic,Yearly,90
Pro,Monthly,0019
The spanned heading appears in both columns and is made unique as Price and Price_2. The rowspan value Basic is repeated on the second row, which is what you want for sorting and filtering in a spreadsheet. The link became plain text and the image disappeared.
How the table is read
- All rows count. Rows from
<thead>,<tbody>and<tfoot>are included, and a footer such as a totals row ends up as the last line.<th>cells used as row labels are treated like any other cell. A<caption>is not part of the output. - Whitespace is collapsed. Line breaks and runs of spaces inside a cell become a single space and the text is trimmed. A
<br>tag is read as a line break, so the words on either side of it are separated by a single space. - Ragged rows are padded to the widest row. Extra columns beyond the header get names like
column3, and missing cells become empty fields. - Nested tables are not extracted separately; their text is merged into the text of the cell that contains them.
Options explained
- Include header row – writes the column names as the first CSV line. They come from the first table row, with blank or repeated names fixed as shown above. When unticked, that line is left out and only the rows below it are written; the first table row is always read as column names, never as data.
- Detect numbers & booleans – converts cells that look like plain numbers or
true/falseinto typed values. In CSV this is mostly invisible, with one exception: numbers are normalized, so89.00is written as89and39.50as39.5. Values with leading zeros like0019, currency symbols, thousands separators or percent signs are left as text. Untick the option to copy every cell verbatim. - Delimiter – comma, semicolon, pipe or tab.
- Quote all fields – wraps every value in double quotes rather than only those that contain the delimiter, a quote or a line break.
- Flatten nested objects – relevant only when several tables are converted together, see below.
Pages with several tables
When the input contains more than one top-level table, all of them are read, but a CSV file can only represent one grid. The result is a single wide row with column names such as rates.0.Plan. For clean output, paste only the table you need. In most browsers you can right-click the table, choose Inspect, select the <table> element in the developer tools and copy its outer HTML.
If you need several tables at once, HTML to JSON keeps them apart, keyed by each table's id. For a quick paste into Google Sheets, HTML to TSV is often more convenient, and CSV viewer lets you check the result as a table before importing it.
Loading a page by URL
Load URL fetches a public page through our server and puts its HTML in the editor. It sees the HTML the server delivers, so tables that a site builds with JavaScript after loading will not be in it. In that case, save the rendered page from your browser, or copy the table's outer HTML from the developer tools, and paste that instead. Respect the terms of use of the sites you collect data from.
Privacy
Parsing and conversion run in your browser. The exceptions are Load URL, which fetches the address through our server, and Save & Share, which stores your input so a link can be created.
Convert an HTML table to CSV in Python
Without third-party packages, subclass HTMLParser from html.parser: collect the text of every td and th into one list per tr, then hand the rows to csv.writer. The parser decodes entities, so R&D arrives as R&D. Do not join the cells with commas by hand; csv.writer adds the quotes that a value like Bob, Jr. needs. When you write to a file, open it with newline="", because the writer supplies its own line endings.
import csv, sys
from html.parser import HTMLParser
class TableRows(HTMLParser):
def __init__(self):
super().__init__()
self.rows, self.cell = [], None # cell is None outside td/th
def handle_starttag(self, tag, attrs):
if tag == "tr":
self.rows.append([])
self.cell = "" if tag in ("td", "th") else self.cell
def handle_data(self, data):
self.cell = None if self.cell is None else self.cell + data
def handle_endtag(self, tag):
if tag in ("td", "th") and self.cell is not None:
self.rows[-1].append(self.cell.strip())
self.cell = None
parser = TableRows()
parser.feed("<table><tr><th>name</th><th>team</th></tr><tr><td>Bob, Jr.</td><td>R&D</td></tr></table>")
csv.writer(sys.stdout, lineterminator="\n").writerows(parser.rows)
For a single table, pasting the HTML into the converter above is quicker and gives you the same rows.