HTML::...xtract(3pm)
HTML::...xtract(3pm) π
User Contributed Perl Documentation β¨ HTML::...xtract(3pm)
okay bestie, buckle up, because this is the page for HTML::TableExtract β the Perl module that reaches into an HTML document and pulls out exactly the tables you want. π
πΈ NAME πΈ
HTML::TableExtract β a Perl module for extracting the content contained in tables within an HTML document, either as text or encoded element trees. no cap, that's the whole job. π
β¨ SYNOPSIS β¨
so the vibe is: matched tables come back to you as table objects, and you can match them by column headers, by depth, by count within a depth, by table tag attributes, or by literally any combination of those four. π¦
# Matched tables are returned as table objects; tables can be matched
# using column headers, depth, count within a depth, table tag
# attributes, or some combination of the four.
# Example: Using column header information.
# Assume an HTML document with tables that have "Date", "Price", and
# "Cost" somewhere in a row. The columns beneath those headings are
# what you want to extract. They will be returned in the same order as
# you specified the headers since 'automap' is enabled by default.
use HTML::TableExtract;
my $te = HTML::TableExtract->new( headers => [qw(Date Price Cost)] );
$te->parse($html_string);
# Examine all matching tables
foreach my $ts ($te->tables) {
print "Table (", join(',', $ts->coords), "):\n";
foreach my $row ($ts->rows) {
print join(',', @$row), "\n";
}
}
# Shorthand...top level rows() method assumes the first table found in
# the document if no arguments are supplied.
foreach my $row ($te->rows) {
print join(',', @$row), "\n";
}
# Example: Using depth and count information.
# Every table in the document has a unique depth and count tuple, so
# when both are specified it is a unique table. Depth and count both
# begin with 0, so in this case we are looking for a table (depth 2)
# within a table (depth 1) within a table (depth 0, which is the top
# level HTML document). In addition, it must be the third (count 2)
# such instance of a table at that depth.
my $te = HTML::TableExtract->new( depth => 2, count => 2 );
$te->parse_file($html_file);
foreach my $ts ($te->tables) {
print "Table found at ", join(',', $ts->coords), ":\n";
foreach my $row ($ts->rows) {
print " ", join(',', @$row), "\n";
}
}
# Example: Using table tag attributes.
# If multiple attributes are specified, all must be present and equal
# for match to occur.
my $te = HTML::TableExtract->new( attribs => { border => 1 } );
$te->parse($html_string);
foreach my $ts ($te->tables) {
print "Table with border=1 found at ", join(',', $ts->coords), ":\n";
foreach my $row ($ts->rows) {
print " ", join(',', @$row), "\n";
}
}
# Example: Extracting as an HTML::Element tree structure
# Rather than extracting raw text, the html can be converted into a
# tree of element objects. The HTML document is composed of
# HTML::Element objects and the tables are HTML::ElementTable
# structures. Using this, the contents of tables within a document can
# be edited in-place.
use HTML::TableExtract qw(tree);
my $te = HTML::TableExtract->new( headers => qw(Fee Fie Foe Fum) );
$te->parse_file($html_file);
my $table = $te->first_table_found;
my $table_tree = $table->tree;
$table_tree->cell(4,4)->replace_content('Golden Goose');
my $table_html = $table_tree->as_HTML;
my $table_text = $table_tree->as_text;
my $document_tree = $te->tree;
my $document_html = $document_tree->as_HTML;
π DESCRIPTION π
HTML::TableExtract is a subclass of HTML::Parser, and its whole main character moment is extracting the info from the tables you actually care about inside an HTML document. π«Ά Every extracted table's info gets stored in table objects. You can pull those tables out as text, as HTML, or as HTML::ElementTable structures (which is the move if you want to edit or manipulate them in-place). iconic.
Right now there are four constraints you can use to say which tables you want pulled out of a document: Headers, Depth, Count, and Attributes. π
- Headers β the most flexible and adaptive technique, fr fr. You hand it an array of text you expect to show up above the data in the tables you want. Once every one of those headers has been located in a row of that table, all the further cells underneath the columns that matched your headers get extracted. Every other column gets ignored β think of it as vertical slices through a table. π° On top of that, TableExtract automatically rearranges each row into the same order as the headers you gave it. Wanna turn that off? Set
automapto0when you create the object, and instead lean on thecolumn_map()method to find out the order the headers were actually found in. Also, TableExtract automatically compensates for cell span issues, so the columns are really the same columns you'd see with your eyeballs in a browser. π You can kill that behavior by setting thegridmapparameter to0. And heads up: HTML is stripped from the entire textual content of a cell before any header matching is attempted β unless thekeep_htmlparameter was enabled. π§ - Depth and Count β the more specific way to point at tables in relation to each other. Depth is how deeply a table lives inside other tables. A top-level table in the document has a depth of
0. A table inside a top-level table has a depth of1, and so on. Each depth is basically a layer; tables sharing the same depth are on the same layer. π Within each of those layers, Count is the order in which a table was seen at that depth, starting with0. Give both a depth and a count and you have uniquely specified one single table in the document. π₯ - Attributes β these match based on the attributes of the html
<table>tag. Think border widths, or background color. π¨
Each of the Headers, Depth, Count, and Attributes specifications is cumulative in how it affects the overall extraction. So, for instance:
- If you specify only a Depth, you get all tables at that depth (and note that these could very well live in separate higher-level tables all over the document, since depth extends across tables).
- If you specify only a Count, you get the tables at that Count from all depths (i.e., the nth occurrence of a table at each depth).
- If you only specify Headers, you get all tables in the document that contain those column headers.
- If you've specified multiple constraints out of Headers, Depth, Count, and Attributes, then each constraint has veto power over whether a particular table gets extracted. π
If no Headers, Depth, Count, or Attributes are specified at all, then all tables match. slay, everything is invited to the party. π
When you're extracting only text from tables, the text is decoded with HTML::Entities by default β you can disable that by setting the decode parameter to 0. β¨
π¦ Extraction Modes π¦
The default mode of extraction for HTML::TableExtract is raw text or HTML. In this mode, embedded tables are completely decoupled from one another. In this case, HTML::TableExtract is a subclass of HTML::Parser:
use HTML::TableExtract;
Alternatively, tables can be extracted as HTML::ElementTable structures, which are in turn embedded in an HTML::Element tree representing the entire HTML document. Embedded tables are not decoupled from one another here, because that tree structure has to be maintained. In this case, HTML::TableExtract is a subclass of HTML::TreeBuilder (itself a subclass of HTML:::Parser):
use HTML::TableExtract qw(tree);
Either way, the basic interface for HTML::TableExtract and the resulting table objects stays exactly the same β the only thing that changes is what you can do with the resulting data. π«
HTML::TableExtract is a subclass of HTML::Parser, and as such it inherits all of its basic methods such as parse() and parse_file(). During scans, start(), end(), and text() are utilized. Feel free to override them! but if you don't eventually invoke them in the SUPER class with some content, results are not guaranteed. π consider yourself warned bestie.
π Advice π
The main point of this module was to provide a flexible way of extracting tabular information from HTML documents without relying too heavily on the document layout. For that reason, I suggest using Headers whenever possible β that way you're anchoring your extraction on what the document is trying to communicate, rather than on some feature of the HTML that makes up the document (other than the fact that the data is contained in a table). π«ΆπΈ
β¨ METHODS β¨
These are the top-level methods you call on the HTML::TableExtract object itself, bestie. Any table that actually matched your query comes back as its own separate object of type HTML::TableExtract::Table, and those table objects have their own whole set of methods, which we get to further down. π«Ά
π CONSTRUCTOR π
new()
Returns a brand-new HTML::TableExtract object. The valid attributes are giving main character energy, and here's every single one:
headersβ Passed in as an array reference,headersare the strings you actually care about, the ones sitting at the top of columns inside the tables you're targeting. They can be plain strings or regular expressions (qr//). If they're strings, they eventually get run through a non-anchored, case-insensitive regex, so regexp special characters are allowed in them.
The table row that holds the headers does not get returned, unless keep_headers was specified or you're extracting into an element tree. Either way, you can grab the header row from inside the table object using the hrow() method.
Columns that aren't beneath one of the headers you gave get ignored β unless slice_columns was set to 0. And columns will, by default, get rearranged into the same order as the headers you provided (peep the automap parameter for the details) unless slice_columns is 0.
Also, by default, columns mean what you'd visually see beneath that header when the table is rendered in a browser. See the gridmap parameter for more on that.
HTML inside a header gets stripped before the match is attempted β unless the keep_html parameter was specified and strip_html_on_match is false. π§
depthβ Tells the module how deeply embedded your tables of interest should be. Top-level tables in the HTML document have a depth of0, tables inside top-level tables have a depth of1, and so on down the line.countβ Which table within each depth you're interested in, starting from0.attribsβ Passed as a hash reference,attribsspecifies attributes of interest inside the HTML<table>tag itself.automapβ Automatically applies the ordering reported bycolumn_map()to the rows returned byrows(). This only makes a difference if you specified Headers and they turn out to be in a different order in the table than what you specified. Automap will rearrange the columns into the same order as the headers appear. To get the original ordering back, you'll need to take another slice of each row usingcolumn_map().automapis enabled by default. πslice_columnsβ Enabled by default. This option controls whether vertical slices get returned from under headers that match. When you disable it, all columns of the matching table are retained, regardless of whether they had a matching header above them. Disabling this also disablesautomap.keep_headersβ Disabled by default, and only applicable when header constraints have been specified. When enabled,keep_headersretains the matching header row as the first row of table data. This option has no effect if you're extracting into an element tree structure. In any case, the header row is always reachable from the table methodhrow().gridmapβ Controls whether the table contents come back as a grid or as a tree. ROWSPAN and COLSPAN issues get compensated for, and columns really are columns. Empty phantom cells are created wherever they would've been obscured by ROWSPAN or COLSPAN settings. This genuinely becomes a whole thing when you're extracting columns beneath headers. Enabled by default. β¨subtablesβ Extract all tables embedded within matched tables.decodeβ Automatically decodes retrieved text withHTML::Entities::decode_entities(). Enabled by default. Has no effect ifkeep_htmlwas specified or if you're extracting into an element tree structure.br_translateβ Translates<br>tags into newlines. Sometimes the leftover text is genuinely hard to parse if the<br>tag just gets dropped. Enabled by default. Has no effect ifkeep_htmlis enabled or if you're extracting into an element tree structure.keep_htmlβ Returns the raw HTML contained in the cell, instead of just the visible text. Embedded tables are not retained in the HTML extracted from a cell. If this option is on, your patterns for header matches have to take HTML in the string into account. This option has no effect if you're extracting into an element tree structure.strip_html_on_matchβ Whenkeep_htmlis enabled, HTML gets stripped by default while the module attempts to match header strings (so ifstrip_html_on_matchis not enabled andkeep_htmlis, you'd have to include potential HTML tags in the regexp for header matches). Stripped header tags are replaced with an empty string β like,'hot d<em>og</em>'would become'hot dog'before the match is attempted.error_handleβ The filehandle where error messages get printed.STDERRby default.debugβ Prints some debugging information toSTDERR, more of it for higher values. Iferror_handlewas provided, the messages get printed there instead ofSTDERR.
πΈ REGULAR METHODS πΈ
These ones you invoke directly on an HTML::TableExtract object.
depths()β Returns all depths that contained matched tables in the document.counts($depth)β For one particular depth, returns all counts that contained matched tables.table($depth, $count)β For one particular depth and count, returns the table object for the table found, if there is one.tables()β Returns table objects for all tables that matched. Returns an empty list if nothing matched.first_table_found()β Returns the table state object for the first table matched in the document. Returnsundefif no tables were matched.current_table()β Returns the current table object while parsing the HTML. Only useful if you're messing around with overridingHTML::Parsermethods.tree()β If the module was invoked in tree extraction mode, returns a reference to the top node of theHTML::Elementtree structure for the entire document (which ultimately includes all tables within the document).tables_report([$show_content, $col_sep])β Returns a string summarizing the extracted tables, along with their depth and count. Optionally takes a$show_contentflag, which will also dump the extracted contents of each table, with columns separated by$col_sep. Default$col_sepis':'.tables_dump([$show_content, $col_sep])β Same astables_report()except it dumps the information toSTDOUT.start,end,textβ These are the hooks intoHTML::Parser. If you want to subclass this module and have things actually work, you have to call these with content at some point.
π DEPRECATED METHODS π
Tables used to be called 'table states'. So accordingly, these following methods still work, but yeah, they've been deprecated:
table_state()β Is nowtable()table_states()β Is nowtables()first_table_state_found()β Is nowfirst_table_found()
π TABLE METHODS π
These ones you invoke from an HTML::TableExtract::Table object, like the ones you get back from the tables() method.
rows()β Returns all rows within a matched table. Each row that comes back is a reference to an array containing the text, the HTML, or a reference to theHTML::Elementobject of each cell, depending on the mode of extraction. Tables withrowspanorcolspanattributes will have some cells containingundef. Returns a list or a reference to an array, depending on context.columns()β Returns all columns within a matched table. Each column that comes back is a reference to an array containing the text, the HTML, or a reference to theHTML::Elementobject of each cell, depending on the mode of extraction. Tables withrowspanorcolspanattributes will have some cells containingundef.row($row)β Returns one particular row from within a matched table, either as a list or an array reference, depending on context.column($col)β Returns one particular column from within a matched table as a list or an array reference, depending on context.cell($row,$col)β Returns one particular item from within a matched table β whether that's the text, the HTML, or a reference to theHTML::Elementobject of that cell, depending on the mode of extraction. If the cell was covered due to rowspan or colspan effects, it returnsundef.space($row,$col)β The same ascell(), except in cases where the given coordinates were covered due to rowspan or colspan issues, in which case the content of the covering cell is returned instead ofundef.depth()β Returns the depth at which this table was found.count()β Returns the count for this table within the depth it was found.coords()β Returns depth and count in a list.tree()β If the module was invoked in tree extraction mode, this accessor gives you a reference to theHTML::ElementTablestructure encompassing the table.hrow()β Returns the header row as a list when headers were specified as a constraint. Ifkeep_headerswas specified initially, this is equivalent to the first row returned by therows()method.column_map()β Returns the order (via indices) in which the provided headers were found. These indices can be used as slices on rows to either order the rows in the same order as the headers, or restore the rows to their natural order β depending on whether the rows have been pre-adjusted using theautomapparameter.lineage()β Returns the path of matched tables that led to matching this table. The path is a list of array refs containing depth, count, row, and column values for each ancestor table involved. Heads up though: corresponding table objects will not exist for ancestral tables that didn't match specified constraints. π
π³ NOTES ON TREE EXTRACTION MODE π³
okay so like, as we said up there, HTML::TableExtract can be invoked in 'tree' mode' and when that happens, the resulting HTML and the extracted tables get encoded in HTML::Element tree structures:
use HTML::TableExtract 'tree';
bestie there's a whole bunch of stuff you gotta keep in mind while you're in this mode, so let's get into it π
The entire HTML document gets encoded into an HTML::Element tree. Every table is part of that structure, yeah, but it's also tracked separately via an HTML::ElementTable structure, which is like a specialized form of HTML::Element tree. so it's giving main character in two stories at once β¨
The HTML::ElementTable objects are accessible by invoking the tree() method from within each table object that HTML::TableExtract hands back to you. And those HTML::ElementTable objects have their own row(), col(), and cell() methods (among others π). Do NOT confuse these with the row() and column() methods that the HTML::TableExtract::Table objects give you. they are different girlies, okay? π
So for example: the row() method from HTML::ElementTable will give you a reference to a 'glob' of all the elements in that row. Actions (like setting attributes) that you perform on that row reference will affect all the elements within that row. iconic. but the row() method from the HTML::TableExtract::Table object? that one returns an array (either by reference or as a list, depending on context) of the contents of each cell within the row. and in tree mode, that content is represented by individual references to each cell β which are references to the same HTML::Element objects chilling in the HTML::Element tree. no cap π«Ά
So the cell() methods in both cases will therefore return references to the same object. slay, consistency. BUT β the exception to this is when a 'cell' in the table grid was originally 'covered' due to rowspan or colspan issues β in that case the cell content will be undef. π likewise, the row() or column() methods from HTML::TableExtract::Table objects will return arrays that potentially contain a mixture of object references and undefs. it's giving chaos a little bit.
ngl if you're gonna be doing LOTS of manipulation of the table elements, it might be more efficient to access them via the methods provided by theHTML::ElementTableobject instead. SeeHTML::ElementTablefor more info on how to manipulate those objects π
An alternative to the cell() method in HTML::TableExtract::Table is the space() method. It's largely similar to cell(), EXCEPT when you give it coordinates of a cell that was covered due to rowspan or colspan effects β it will return the contents of the cell that was covering that space rather than undef. so like, if for example cell (0,0) had a rowspan of 2 and colspan of 2, then cell(1,1) would return undef and space(1,1) would return the same content as cell(0,0) or space(0,0). the way that actually makes sense?? ate π
π¦ REQUIRES π¦
β¨ OPTIONALLY REQUIRES β¨
π AUTHOR π
Matthew P. Sisk, <sisk@mojotoad.com>
π COPYRIGHT π
Copyright (c) 2000-2017 Matthew P. Sisk. All rights reserved. All wrongs revenged. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
(not the "all wrongs revenged" line taking me OUT π iconic behavior fr fr)
π SEE ALSO π
and that's the whole page bestie!! π¦ this bad girl shipped as perl v5.34.0 and the page is dated 2022-06-14 β so yeah, vintage but still serving π go forth and extract those tables, you absolute legend πΈπβ¨
π¦ where this page comes from
- debian bookworm: libhtml-tableextract-perl 2.15-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- debian trixie: libhtml-tableextract-perl 2.15-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libhtml-tableextract-perl 2.15-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libhtml-tableextract-perl 2.15-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libhtml-tableextract-perl 2.15-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms