In my case, there is a single table which happens to be a device list from a router.  If you wish to read the table using TR/TH/TD (row, header, data) instead of a matrix as mentioned above, you can do something like the following:
    List<TableRow> deviceTable = (from table in document.DocumentNode.SelectNodes(XPathQueries.SELECT_TABLE)
                                       from row in table?.SelectNodes(HtmlBody.TR)
                                       let rows = row.SelectSingleNode(HtmlBody.TR)
                                       where row.FirstChild.OriginalName != null && row.FirstChild.OriginalName.Equals(HtmlBody.T_HEADER)
                                       select new TableRow
                                       {
                                           Header = row.SelectSingleNode(HtmlBody.T_HEADER)?.InnerText,
                                           Data = row.SelectSingleNode(HtmlBody.T_DATA)?.InnerText}).ToList();
                                       }  
TableRow is just a simple object with Header and Data as properties.
The approach takes care of null-ness and this case:
<tr>
    <td width="28%"> </td>
</tr>
 
 
which is row without a header.  The HtmlBody object with the constants hanging off of it are probably readily deduced but I apologize for it even still.  I came from the world where if you have " in your code, it should either be constant or localizable.