uses htmlutil; type TCell = string; TRow = array of TCell; TTableRec = record row: array of TRow; end; var InTable, InCell, InRow: boolean; RowCount, ColCount, TableCount: longword; TableRec: TTableRec; // put above globals inside your class below type TClass = class procedure OnTag... procedure OnText... procedure Parse... end; procedure TClass.Parse; var parser: TFastHtmlParser; const HTMLSTRING= 'SOME HTML <TABLE> <TR><TD>blah</TD></TR></TABLE>'; begin parser:= TFastHtmlParser.create(HTMLSTRING); parser.OnTag:= @self.Ontag; parser.OnText:= @self.OnText; parser.exec; parser.free; end; procedure TClass.OnTag(UpcaseTag: string); begin if htmlutil.GetTagName(UpcaseTag) = 'TABLE' then begin InTable = true; inc(tablecount); rowcount:= 0; colcount:= 0; { check an attribute value... if htmlutil.GetVal(UpcaseTag, 'class') = 'redContentTable' then correcttablefound:= true; } end; if htmlutil.GetTagName(UpcaseTag) = 'TR' then begin InRow = true; inc(rowcount); colcount = 0 ; // reset cell (column) count each time we hit a new row end; if htmlutil.GetTagName(UpcaseTag) = 'TD' then begin incell = true; inc(colcount); end; if UpcaseTag = '</TD>' then incell:= false; if UpcaseTag = '</TR>' then inrow:= false; if UpcaseTag = '</TABLE>' then intable:= false; end; procedure TClass.OnText(text: string); var tmp: string; begin { if tablecount <> 7 then exit (must be table 7?) if not correcttablefound then exit } if InTable then if InCell then begin tmp:= tablerec.row[rowcount][colcount]; tablerec.row[rowcount][colcount] := tmp + text; end; // above adds to your storage buffer // We concat since multiple text tags can be found and must be trapped { Above is similar to Excel ROW/CELL spreadsheet using a record, you could use a 2 dimensional array, a list, class, or other buffer Could track multiple tables too: tablerec = array of TTableRec; tablerec[tablecount].row[rowcount][colcount] := (concat here) } end;