Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi Eigener HTML-Parser/Auslesen von HTML-Tables (https://www.delphipraxis.net/138562-eigener-html-parser-auslesen-von-html-tables.html)

NiceWeather 12. Aug 2009 15:18


Eigener HTML-Parser/Auslesen von HTML-Tables
 
Hallo zusammen,

habe ein Logik-Problem und hoffe es kann mir jemand weiterhelfen:

Eine Funktion soll eine HTML-Tabelle als string zurückliefern:

Folgende Deklaration der Funktion:
Delphi-Quellcode:
function GetHTMLTable (AUrl: string; ATableID: integer): string;
Der Parameter ATableID ist die Tabelle, die zurückgeliefert werden soll.

Folgende Variablen:
Delphi-Quellcode:
var
  cResult: string; //Der Rükgabewert
  cSourceCode: string;
  nTableStartPos: integer;
  nTableEndPos: integer;
  nOffSet: integer;
  aTableCoordinatesArray: Array of TTableCoordinates; //TTableCoordinates ist ein Record mit den beiden Felden TableStartPos und TableEndPos
  nTableCoordinatesArraySize: integer;
  nI: integer; //Schleifenvariable
Zunächst die Initialisierung einiger Variablen:

Delphi-Quellcode:
  cSourceCode := GetSourceCode(AUrl); //GetSourceCode ist eine eigene Funktion, die mittels TIdHTTP den Sourcecode einer Website liefert. AUrl wurde als Argument an die Funktion geliefert.

  nTableStartPos := Pos(tblStartIdentifier, cSourceCode); //defniert das erste Vorkommen von tblStartIdentifier (String-Konstante= '<table')
  nTableEndPos := Pos(tblEndIdentifier, cSourceCode); //defniert das erste Vorkommen von tblEndIdentifier (String-Konstante= '</table>')
  nTableCoordinatesArraySize := 0;
Dann wird das Array aTableCoordinatesArray mit allen vorkommenden Positionen von '<table' in cSourceCode gefüttert:

Delphi-Quellcode:
  while nTableStartPos > 0 do
    begin
      Inc(nTableCoordinatesArraySize); //Größe des Arrays definieren
      SetLength(aTableCoordinatesArray, nTableCoordinatesArraySize); //Größe des Arrays definieren
      aTableCoordinatesArray[nTableCoordinatesArraySize-1].TableStartPos := nTableStartPos;
      nOffSet := nTableStartPos + Length(tblStartIdentifier); //Offset hinter die letzte gefundene Position setzen
      nTableStartPos := PosEx(tblStartIdentifier, cSourceCode, nOffSet); //schauen, ob's ein weiteres '<table' gibt
    end;
So weit so gut. Es fehlen die Positionen der End-Tags. Die werden folgendermaßen eingelesen:

Delphi-Quellcode:
  for nI := 0 to High(aTableCoordinatesArray) do
    begin
      aTableCoordinatesArray[nI].TableEndPos := nTableEndPos;
      nOffSet := nTableEndPos + Length(tblEndIdentifier); //Offset hinter die letzte gefundene Position setzen
      nTableEndPos := PosEx(tblEndIdentifier, cSourceCode, nOffSet); //nach dem nächsten '</table>' schauen. Es wird angenommen, dass cSourceCode korrekt ist und genauso viele EndTags, wie StartTags vorhanden sind.
    end;
Vorweg: Der Ansatz war, alle vorkommenden Taellen im Quelltext zu kennen, um zu wissen, wo die Table anfängt und wo sie aufhört (Stichwort: Verschachtelte Tables).

Genau da liegt aber mein Problem, denn der End-Tag von
Delphi-Quellcode:
aTableCoordinatesArray[nI].TableStartPos
ist nicht unbedingt
Delphi-Quellcode:
 
aTableCoordinatesArray[nI].TableEndPos
Was die Definition des Rückgabewertes betrifft: Das ist nicht das Problem an sich.
Es fällt mir lediglich schwer, zuzuordnen welcher End-Tag zu welchem Start-Tag gehört.

Habe mir schon verschachtelte for-Schleifen überlegt, was allerdings alles nicht hinhaut.

Habt ihr eine Idee? Würdet ihr vom Ansatz her überhaupt so wie ich beginnen?

Vorab vielen Dank für euren Input!

Prototypjack 12. Aug 2009 16:21

Re: Eigener HTML-Parser/Auslesen von HTML-Tables
 
Moin,

Mit deinem Ansatz, wenn ich dich richtig verstanden habe, wirst du nicht weit kommen, denn dieser funktioniert nicht mehr, wenn Tables verschachtelt vorkommen.
Ich schlage daher folgendes vor:

Du machst nur einen Durchlauf. Bei jedem vorkommenden "<Table" fügst du einer Liste einen Eintrag hinzu, in welchem die Startposition der (gerade gefundenen) Tabelle steht. Wenn du jetzt auf ein "</Table" stößt, dann entfernst du einfach den letzten Eintrag in der Liste und fügst die Daten des letzten Eintrags (also den Tabellen-Start) zusammen mit der Position, des gerade gefundenen End-Tags in eine weitere Liste ein.

Wenn der HTML-Code korrekt ist, so wird die erste Liste nach einem Durchlauf wieder leer sein (schließlich sollte jedes Start-Tag auch ein korrespondierendes End-Tag besitzen). In der zweiten Liste befinden sich dann alle Tables zusammen mit deren (korrekten) Start- und End-Positionen.

Ich hoffe ich konnte mich halbwegs klar ausdrücken ;)

Grüße,
Max

NiceWeather 13. Aug 2009 15:46

Re: Eigener HTML-Parser/Auslesen von HTML-Tables
 
Danke! 8) :thumb:

Ging wirklich nicht wie ich ursprünglich wollte (habs auch nochmal kompliziert mit Sortierung, etc. versucht).

Am einfachsten ging es dann so:

Delphi-Quellcode:
function THTMLTables.GetHTMLTable(AUrl: string; ATableID: Integer): string;
var
  cResult: string; //Rückgabewert
  cSourceCode: string; //HTML-Quelltext
  nTablesCount: integer; //Anzahl vorhandener Tabellen im cSourceCode
  nStartOffSet: integer; //Da fang ich an nach '<table' zu suchen
  nEndOffSet: integer; //Da fang ich an nach '</table>' zu suchen
  nTableStartPos: integer; //Da fängt meine Table an
  nTableEndPos: integer; //...und da hört sie auf
  nNextTablePos: integer; //Start-Position der jeweils nächsten Table
  nResultLength: integer; //Anzahl der Zeichen, die cResult enthält.
  nI: integer;

begin

  cSourceCode := GetSourceCode(AUrl); //Eigene Funktion, die mittels TIdHTTP, den Quelltext ausgibt
  nTablesCount := TablesCount(cSourceCode, True); //Eigene Funktion zum zählen der Tables im Quelltext - Das boolsche Argument gibt an, ob der Quelltext validiert werden soll (d.h. wenn true und genausoviele Table-Start-Tags wie End-Tags gibt's ein Ergebnis, ansonsten 0)

  //Weiter mit der Initialisierung von Variablen
  nStartOffSet := 1;
  nTableStartPos := 1;
  nTableEndPos := 1;
  nNextTablePos := 1;


  if (ATableID > 0) and
     (ATableID <= nTablesCount) then
    begin

      //Findet die Position von ATableID (1 = Erster Table-Tag, etc.)
      for nI := 0 to ATableID-1 do
          begin
            nTableStartPos := PosEx(tblStartIdentifier, cSourceCode, nStartOffSet);
            nStartOffSet := nTableStartPos + Length(tblStartIdentifier);
          end;

      //Findet den Table-End-Tag
      nEndOffSet := nStartOffSet;
      while (nNextTablePos <= nTableEndPos) and
            (nNextTablePos <> 0) do
        begin
          nNextTablePos := PosEx(tblStartIdentifier, cSourceCode, nStartOffSet);
          nStartOffSet := nNextTablePos + Length(tblStartIdentifier);
          nTableEndPos := PosEx(tblEndIdentifier, cSourceCode, nEndOffSet);
          nEndOffSet := nTableEndPos + Length(tblEndIdentifier);
        end;

      nResultLength := nTableEndPos - nTableStartPos + Length(tblEndIdentifier);
      cResult := Copy(cSourceCode, nTableStartPos, nResultLength);

    end;

  Result := cResult;

end;
Wer möchte kann gerne Kritik an meinem Code üben! Bin immer für "Optimierungen" zu haben. :-D

himitsu 13. Aug 2009 16:09

Re: Eigener HTML-Parser/Auslesen von HTML-Tables
 
im Grunde kannst du nur ein Array anlegen
- jedesmal, wenn ein Open-Tag ( <tag ...> ) gefunden wird, dann wird dessen Anfang an das Array angehängt
- und sobald ein ClosedTag ( </tag> ) entdekt wird, dann wird der letze Eintrag aus dem Array entfernt

Wenn du jetzt noch je Array-Ebene/Verzeigungstiefe/Level die Aufrufe mitzählst, dann kannst du so auch erkennen, in welcher Tabelle du gerade bist.

leider ist HTML eine "kranke" Sprache, in welcher die komischten Dinge grad noch so erlaubt/gedultet werden, weshalb es da garnicht so einfach ist, soeine Datei zu parsen.

Hisoka 13. Aug 2009 17:35

Re: Eigener HTML-Parser/Auslesen von HTML-Tables
 
Hmm, hab die letzte Zeit wenig mit Pascal gearbeitet. Daher sage ich nur wie es relativ leicht gehen könnte.

1. DOMDocument erstellen und HTML Code übergeben
2. normalisieren lassen(ka ob das alle libs können)
3. Abfrage nach elementen mit dem Tag Namen table

und schon hat man alle HTML Tabellen. In C++ und mit QT geht das zumindest so recht fix.


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:10 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz