Extraindo tags de arquivos HTML
Como poderíamos extrair informação HTML Tag tais como Links/Images/Frames, contidas em uma determinada página?
Extrair informação tag do HTML sempre foi uma coisa complicada de fazer, pois teremos que prever a maioria dos erros, texto quebrado ou erros do código. Sempre procuramos uma função precisa e correta para extrair links de páginas HTML, mas todas as que achamos, tinham suas desvantagens com relação a linhas quebradas ou espaços mal colocados ou até mesmo de lentidão.
Assim, como a maioria dos programadores diz, depois de uma longa procura decidimos escrever NOSSA PRÓPRIA função para controlar tudo do melhor modo possível, esperando desta maneira, por um fim para todas essas funções bobas ou mal escritas que dizem que fazem o trabalho do melhor modo.
Por favor, repare que a função que estamos propondo poderá não ser a mais rápida, mas certamente funciona da melhor forma e pode inclusive ser expandida para no futuro extrair mais informação.
Eis aqui a função. Inserimos alguns comentários, e não nos aprofundamos a respeito de como a mesma trabalha, pois foi escrita de uma maneira simples, de forma que mesmo um iniciante poderá entendê-la e expandi-la para seu próprio uso:
function ExtractHtmlTagValues(const HtmlText: string; TagName, AttribName: string;
var Values: TStrings): integer;
function FindFirstCharAfterSpace(const Line: string; StartPos: integer): Integer;
var
i: integer;
begin
Result := -1;
for i := StartPos to Length(Line) do
begin
if (Line[i] <> ' ') then
begin
Result := i;
exit;
end;
end;
end;
function FindFirstSpaceAfterChars(const Line: string; StartPos: integer): Integer;
begin
Result := PosEx(' ', Line, StartPos);
end;
function FindFirstSpaceBeforeChars(const Line: string; StartPos: integer): Integer;
var
i: integer;
begin
Result := 1;
for i := StartPos downto 1 do
begin
if (Line[i] = ' ') then
begin
Result := i;
exit;
end;
end;
end;
var
InnerTag: string;
LastPos, LastInnerPos: Integer;
SPos, LPos, RPos: Integer;
AttribValue: string;
ClosingChar: char;
TempAttribName: string;
begin
Result := 0;
LastPos := 1;
while (true) do
begin
{ achar outer tags '<' & '>' }
LPos := PosEx('<', HtmlText, LastPos);
if (LPos <= 0) then
break;
RPos := PosEx('>', HtmlText, LPos+1);
if (RPos <= 0) then
LastPos := LPos + 1
else
LastPos := RPos + 1;
{ obter inner tag }
InnerTag := Copy(HtmlText, LPos+1, RPos-LPos-1);
InnerTag := Trim(InnerTag); // remove spaces
if (Length(InnerTag) < Length(TagName)) then
Continue;
{ verificar os tag name }
if (SameText(Copy(InnerTag, 1, Length(TagName)), TagName)) then
begin
{ tag achada }
AttribValue := '';
LastInnerPos := Length(TagName)+1;
while (LastInnerPos < Length(InnerTag)) do
begin
{ achar primeiro '=' after LastInnerPos }
RPos := PosEx('=', InnerTag, LastInnerPos);
if (RPos <= 0) then
break;
{ Deste modo, podemos verificar a existência de nomes de atributos múltiplos e não um atributo especifico }
SPos := FindFirstSpaceBeforeChars(InnerTag, RPos);
TempAttribName := Trim(Copy(InnerTag, SPos, RPos-SPos));
if (true) then
begin
{ achar a tag correta }
LPos := FindFirstCharAfterSpace(InnerTag, RPos+1);
if (LPos <= 0) then
begin
LastInnerPos := RPos + 1;
continue;
end;
LPos := FindFirstCharAfterSpace(InnerTag, LPos);
if (LPos <= 0) then
Continue;
if ((InnerTag[LPos] <> '"') and (InnerTag[LPos] <> '''')) then
begin
{ AttribValue não esta delimitado por '"' ou ''' portanto obte-lo }
RPos := FindFirstSpaceAfterChars(InnerTag, LPos+1);
if (RPos <= 0) then
AttribValue := Copy(InnerTag, LPos, Length(InnerTag)-LPos+1)
else
AttribValue := Copy(InnerTag, LPos, RPos-LPos+1);
end
else
begin
{ obter url delimitada por '"' ou ''' }
ClosingChar := InnerTag[LPos];
RPos := PosEx(ClosingChar, InnerTag, LPos+1);
if (RPos <= 0) then
AttribValue := Copy(InnerTag, LPos+1, Length(InnerTag)-LPos-1)
else
AttribValue := Copy(InnerTag, LPos+1, RPos-LPos-1)
end;
if (SameText(TempAttribName, AttribName)) and (AttribValue <> '') then
begin
Values.Add(AttribValue);
inc(Result);
end;
end;
if (RPos <= 0) then
LastInnerPos := Length(InnerTag)
else
LastInnerPos := RPos+1;
end;
end;
end;
end;
Por exemplo, se quisermos extrair todos os links contidos em uma página, é só fazer o seguinte:
var
Links: TStrings;
Html: TStrings;
begin
Links := TStringList.Create;
Html := TStringList.Create;
Html.LoadFromFile('arquivo.htm');
try
LinksFound := ExtractHtmlTagValues(Html.Text, 'A', 'HREF', Links);
Memo1.Lines := Links;
finally
Links.Free;
end;
end;