Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft (https://www.delphipraxis.net/196160-mit-idhttp-seite-aufrufen-und-memorystream-uebergeben-%3D-zeichen-fehlerhaft.html)

bogdan 27. Apr 2018 08:09

Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Mit dem folgenden Code lese ich den HTML Code der geladenen Web-Seiten auf:

Code:
function GetHTMLCode(xUrl: String): String;
var
  xResult : TStringList;
  xResMemo : TMemoStream;
begin
  xResult := TStringList.Create;
  xResMemo : TMemoStream.Create;
  try
    try
      IdHTTP1.Get(xUrl, xResMemo);
      xResMemo.Position := 0;
      xResult.Text := StreamToText(xResMemo, ' ', 00);
    except
      on E: EIdHTTPProtocolException do
      begin
        xResult.Add('E.Message: ' + E.Message);
        xResult.Add('E.ErrorMessage:');
        xResult.Add(E.ErrorMessage);
      end;
    end;
  finally
    Result := xResult.Text;
    xResult.Free;
    xResMemo.Free;
  end;
end;

function StreamToText(sStream: TStream; sNewSign: string; sOldSign: Integer): AnsiString;
var
  xLen, j, xSize : Integer;
  xTemp : string;
begin
  try
    xLen := sStream.Size;
    SetLength(Result, xLen);
    if xLen > 0 then
    begin
      sStream.ReadBuffer(Result[1], xLen);
      for j := 1 to xLen do
      begin
        if Ord(Result[j]) = sOldSign then
        begin
          xTemp := xTemp + sNewSign
        end else
        begin
          xTemp := xTemp + Result[j];
        end;
      end;
    end;
  finally
    Result := xTemp;
  end;
end;
Damit der HTML Code der aufgerufenen Seiten vollständig als Text angezeigt werden kann muss ich ein MemoryStream nutzen, dazu habe ich schon in einem anderen Beitrag das Problem erläutert.
Allerdings habe ich jetzt das Problem das kyrillische, chinesische, usw. Zeichen als Hieroglyphen angezeigt werden.
Wie kann man die Zeichen aus der Funktion richtig in einen Text kodieren?

KodeZwerg 27. Apr 2018 08:14

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Du greifst auf Result zu?

Neutral General 27. Apr 2018 08:15

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Ich schätze fast ein TStringstream mit dem richtigen Encoding macht einerseits deine StreamToText procedure überflüssig und würde wahrscheinlich das richtige Ergebnis liefern.

Zitat:

Zitat von KodeZwerg (Beitrag 1400674)
Du greifst auf Result zu?

?

sStream.ReadBuffer(Result[1], xLen);

schreibt Daten in Result, falls das die Frage war.

mjustin 27. Apr 2018 08:17

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Welches Encoding wird verwendet (UTF-8, UTF-16, ...)?

mjustin 27. Apr 2018 08:25

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Zitat:

Zitat von mjustin (Beitrag 1400677)
Welches Encoding wird verwendet (UTF-8, UTF-16, ...)?

Edit: ok, es ist UTF-8, das wird von Indy unterstützt.

Allerdings scheint die Seite mit einem speziellen Content-Encoding geliefert zu werden (im Browser-Debugger von Firefox wird dies angezeigt):

Content-Encoding: br

Indy unterstützt dieses Encoding anscheinend nicht (Brotli).

Neutral General 27. Apr 2018 08:28

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Zitat:

Zitat von mjustin (Beitrag 1400679)
Zitat:

Zitat von mjustin (Beitrag 1400677)
Welches Encoding wird verwendet (UTF-8, UTF-16, ...)?

Edit: ok, es ist UTF-8, das wird von Indy unterstützt.

Allerdings scheint die Seite mit einem spezielles Content-Encoding geliefert zu werden (im Browser-Debugger von Firefox wird dies angezeigt):

Content-Encoding: br

Indy unterstützt dieses Encoding anscheinend nicht (Brotli).

Woher weißt du um welche Seite es sich handelt? :shock:

mjustin 27. Apr 2018 08:29

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Zitat:

Zitat von Neutral General (Beitrag 1400680)
Woher weißt du um welche Seite es sich handelt? :shock:

Aus Beitrag #10

Neutral General 27. Apr 2018 08:48

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Zitat:

Zitat von mjustin (Beitrag 1400681)
Zitat:

Zitat von Neutral General (Beitrag 1400680)
Woher weißt du um welche Seite es sich handelt? :shock:

Aus Beitrag #10

Ehm okay. Back to the future schätze ich.
Dir ist klar, dass der Beitrag den ich gerade von dir zitiere #7 ist?
Meiner wird #8 sein. #9+ existieren (noch) nicht..

KodeZwerg 27. Apr 2018 08:51

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
So hier meinte ich, nur grob hier hin gekritzelt, ich hoffe das klappt.
Delphi-Quellcode:
Function MyFilter ( inStream: TStream ): AnsiString;
var
  xBuf: TBytes;
  i : Integer;
begin
  try
    SetLength(xBuf, inStream.Size); // puffer größe bestimmen
    inStream.ReadBuffer(Pointer(xBuf)^, Length(xBuf)); // puffer füllen
    for I := 0 to Length(xBuf) -1 do
      if xBuf[i] = 0 then xBuf[i] := 20; // mache aus 0 ein Leerzeichen
  finally
    SetLength(Result, Length(xBuf)); // ausgabegröße
    for I := 0 to Length(xBuf) -1 do
      Result[i+1] := AnsiChar(xBuf[i]); // alles aus puffer ins Result
  end;
end;

Neutral General 27. Apr 2018 09:12

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Also so funktionierts bei mir.
Damit kann ich http://www.visitkorea.or.kr/intro.html korrekt abrufen.
Delphi-Quellcode:
function TForm1.GetHTMLCode(xUrl: String): String;
var xResMemo : TStringStream;
begin
  xResMemo := TStringStream.Create('', TEncoding.UTF8);
  try
    IdHTTP1.Get(xUrl, xResMemo);
    xResMemo.Position := 0;
    Result := xResMemo.DataString
  finally
    xResMemo.Free;
  end;
end;

mjustin 27. Apr 2018 10:44

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Zitat:

Zitat von Neutral General (Beitrag 1400685)
Ehm okay. Back to the future schätze ich.

:spin2:

"dazu habe ich schon in einem anderen Beitrag das Problem erläutert." -> Post #10 aus dem anderen Beitrag (von gestern, weiter unten) enthält die URL einer Warez-Seite, und diese verwendet das Content-Encoding "br"

bogdan 27. Apr 2018 11:43

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Vielen Dank schon mal für die zahlreichen Antworten.

KodeZwerg:
Mit deinem Code werden leider bei mir die kyrillischen Zeichen immer noch als Hieroglyphen angezeigt.
Getestet habe ich es mit der folgenden Seite: mail.ru.

Neutral General:
Ich kann leider TStringStream nicht nutzen, da ich sonst den HTML Code bestimmter Seiten als Text nicht komplett angezeigt bekomme.

Neutral General 27. Apr 2018 11:45

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Zitat:

Zitat von bogdan (Beitrag 1400746)
Neutral General:
Ich kann leider TStringStream nicht nutzen, da ich sonst den HTML Code bestimmter Seiten als Text nicht komplett angezeigt bekomme.

Wieso? Warum sollte das passieren?
Welche Seite denn? Und welche Delphi-Version benutzt du?

bogdan 27. Apr 2018 11:51

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Wegen der unvollständigen Anzeige des HTML Codes, habe ich das Problem in diesem Thema erläutert:
https://www.delphipraxis.net/196148-...g-geladen.html

bogdan 27. Apr 2018 11:53

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Auf dieser "dubiosen" Seite wurden heute weitere Beiträge hinzgefügt, somit hat sich der unvollständige HTML Code auf die Seite 37 verschoben:
https://ddl-warez.to/downloads/haupt...rt/D/seite/37/

KodeZwerg 27. Apr 2018 12:15

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Stell doch mal dein komplettes Projekt (Source) ohne kompilierung gepackt als .zip/.rar Anhang rein, kann ja nicht groß sein, vielleicht liegt der Fehler an ganz anderer Stelle.

bogdan 27. Apr 2018 12:43

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Code:
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IdHTTP, Vcl.StdCtrls, Vcl.ExtCtrls,
  IdZLibCompressorBase, IdCompressorZLib, IdCookieManager, IdBaseComponent,
  IdComponent, IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack, IdSSL,
  IdSSLOpenSSL, IdSSLOpenSSLHeaders, IdCTypes;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Memo1: TMemo;
    Edit1: TEdit;
    Button1: TButton;
    IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL;
    IdCookieManager1: TIdCookieManager;
    IdCompressorZLib1: TIdCompressorZLib;
    procedure Button1Click(Sender: TObject);
    procedure IdSSLIOHandlerSocketOpenSSL1StatusInfoEx(ASender: TObject;
      const AsslSocket: PSSL; const AWhere, Aret: Integer; const AType,
      AMsg: string);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
    procedure SetNewIdHTTP;
  end;

var
  Form1: TForm1;
  gIdHTTP : TIdHTTP;

implementation

{$R *.dfm}

procedure TForm1.IdSSLIOHandlerSocketOpenSSL1StatusInfoEx(ASender: TObject;
  const AsslSocket: PSSL; const AWhere, Aret: Integer; const AType,
  AMsg: string);
begin
  SSL_set_tlsext_host_name(AsslSocket, gIdHTTP.Request.Host);
end;


procedure TForm1.SetNewIdHTTP;
begin
  if Assigned(gIdHTTP) then
  begin
    Memo1.Clear;
    gIdHTTP.Free;
  end;
  gIdHTTP := TIdHTTP.Create(nil);
  gIdHTTP.AllowCookies := True;
  gIdHTTP.HandleRedirects := True;
  gIdHTTP.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
  gIdHTTP.CookieManager := IdCookieManager1;
  gIdHTTP.Compressor := IdCompressorZLib1;
end;

function StreamToText(sStream: TStream; sOldSign, sNewSign: Integer): AnsiString;
var
  xBuf: TBytes;
  i : Integer;
begin
  try
    SetLength(xBuf, sStream.Size); // puffer größe bestimmen
    sStream.ReadBuffer(Pointer(xBuf)^, Length(xBuf)); // puffer füllen
    for I := 0 to Length(xBuf) -1 do
      if xBuf[i] = sOldSign then xBuf[i] := sNewSign; // mache aus 0 ein Leerzeichen
  finally
    SetLength(Result, Length(xBuf)); // ausgabegröße
    for I := 0 to Length(xBuf) -1 do
      Result[i+1] := AnsiChar(xBuf[i]); // alles aus puffer ins Result
  end;
end;

function IdHTTP_Get(sHTTP: TIdHTTP; sURL: String): String;
var
  xResult : TStringList;
  xMemoRes : TMemoryStream;
begin
  xResult := TStringList.Create;
  xMemoRes := TMemoryStream.Create;
  try
    try
      sHTTP.Get(sURL, xMemoRes);
      xMemoRes.Position := 0;
      xResult.Text := StreamToText(xMemoRes, 0, 20);
    except
      on E: EIdHTTPProtocolException do
      begin
        xResult.Add('E.Message: ' + E.Message);
        xResult.Add('E.ErrorMessage:');
        xResult.Add(E.ErrorMessage);
      end;
    end;
  finally
    Result := xResult.Text;
    xResult.Free;
    xMemoRes.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  SetNewIdHTTP;
  Memo1.Text := IdHTTP_Get(gIdHTTP, Edit1.Text);
end;

end.

KodeZwerg 27. Apr 2018 18:12

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Hallo bogdan,
gut wäre ein Projekt-Upload gewesen für alle Properties (die stehen in der .dfm Datei) die Du so gesetzt hast.
Auch gut zu Wissen was in Edit1.Text steht, nur eine blanko Webadresse ohne Endung?
Soll das Ziel sein den HTML Source-Code zu laden oder eine bestimmte Datei?


edit
Hier ein Beispiel um HTTP HTML Sourcecode zu laden:
Delphi-Quellcode:
function DownloadHTMLSource( var xUrl : ansiString ):ansiString;
const
  INET_USERAGENT   = 'Mozilla/4.0, Indy Library (Windows; en-US)'; // Damit sagst Du "Hallo, ich bin..."
  INET_TIMEOUT_SECS = 3;                                           // Wieviele Sekunden warten bevor Timeout kommt?
  INET_REDIRECT_MAX = 10;                                          // Maximale Redirect-Tiefe
begin
 with tIdHttp.create(nil) do begin        // Create Indy http object
  request.userAgent := INET_USERAGENT;    // Custom user agent string
  redirectMaximum := INET_REDIRECT_MAX;   // Maximum redirects
  handleRedirects := INET_REDIRECT_MAX<>0; // Handle redirects
  readTimeOut := INET_TIMEOUT_SECS*1000;  // Read timeout msec
  try                                     // Catch errors
   Result := Get(xUrl);                   // Do the request
   if URL.Port = '80' then URL.Port := ''; // Remove port 80 from final URL
   xUrl := URL.GetFullURI                 // Return final URL <<- da könnte nun eine andere URL stehen als wie Du eingegeben hast
  except                                  // Return an error message if failed
    on E: Exception do
    Result := 'Error! HTTP Adress: ' +xUrl+ #13#10 + 'E.Message: ' + E.Message;  // Return Error Message
  end;
  free                                    // Free the http object
 end
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  temp1, temp2: ansiString;
  i : Integer;
begin
  Memo1.Clear;
  Memo1.Lines.Add('Working...');
  Temp1 := Edit1.Text;
  if Length(Edit1.Text) > 0 then Temp2 := DownloadHTMLSource(Temp1);
  // hier noch irgendwas mit ansiString anstellen, zum Beispiel nullen entfernen
  for I := 1 to Length(Temp2) do
    if Temp2[i] = Char(Ord($00)) then Temp2[i] := Char(Ord($20));
  Memo1.Lines.Text := Temp2;
  Memo1.Lines.Add('HTTP HTML Source from: '+Temp1);
end;
Alles noch unoptimiert aber vielleicht ist es ja das was Du wolltest?
Viel Spass damit.
Ps: Diesen Code solltest Du in einem leeren Projekt testen ohne Indy Komponenten aufm Formular aber in Uses.

Neutral General 27. Apr 2018 21:11

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Zitat:

DownloadHTMLSource(var xUrl : ansiString): ansiString;
Das kann schon mal nichts werden ;)

KodeZwerg 27. Apr 2018 21:29

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Zitat:

Zitat von Neutral General (Beitrag 1400793)
Zitat:

DownloadHTMLSource(var xUrl : ansiString): ansiString;
Das kann schon mal nichts werden ;)

Sorry und sorry, 1. weil ja falsch und 2. weil ich nicht dahinter komme warum ich ansiString nicht als puffer nehmen sollte. Ich mach mich hier bestimmt gerade voll zum Honk aber bitte erleuchte mich damit ich aus meinen Fehlern auch lernen kann. Danke & nochmal sorry für den falschen Code!

Neutral General 27. Apr 2018 21:53

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Liste der Anhänge anzeigen (Anzahl: 1)
Siehe Bild.

Und 0-Bytes aus einem AnsiString rauszufiltern ist nie Sinn der Sache.

KodeZwerg 27. Apr 2018 22:04

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Ohhh oder viel besser Uhhhnicode ok, ich fange an zu verstehen und lerne daraus, Danke!

edit
Ich habe mal eine mini-Kontrolle eingebaut um zu schauen ob was verloren geht, Memo1 scheint bei diesen Korea Zeichen mehr Chars zu verwenden als reinkommen oder ist das wegen CRLF?
Delphi-Quellcode:
  i := Length(Memo1.Lines.Text) ;
  Memo1.Lines.Add('HTTP HTML Source from: '+Temp1);
  Memo1.Lines.Add('Downloaded: '+IntToStr(Length(Temp2)) +' bytes, displaying: ' +IntToStr(i)+ ' chars. ' +IntToStr(Length(Temp2)-i)+ ' bytes are lost!');
Zitat:

HTTP HTML Source from: http://www.visitkorea.or.kr/intro.html
Downloaded: 7263 bytes, displaying: 7427 chars. -164 bytes are lost!

KodeZwerg 28. Apr 2018 00:57

AW: Mit IdHTTP Seite aufrufen und an MemoryStream übergeben = Zeichen fehlerhaft
 
Hallo nochmal,
hier zwei neue Samples die auch HTTPS Unterstützen, diesmal sollte nichts schiefgehen, aber mal abwarten was so kommt :lol:
Delphi-Quellcode:
Uses WinInet;

function GetUrlContent(const Url: string): UTF8String;
var
  NetHandle: HINTERNET;
  UrlHandle: HINTERNET;
  Buffer: array[0..1023] of byte;
  BytesRead: dWord;
  StrBuffer: UTF8String;
begin
  Result := '';
  NetHandle := InternetOpen('Delphi-PRAXiS RockZ', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  if Assigned(NetHandle) then
    try
      UrlHandle := InternetOpenUrl(NetHandle, PChar(Url), nil, 0, INTERNET_FLAG_RELOAD, 0);
      if Assigned(UrlHandle) then
        try
          repeat
            InternetReadFile(UrlHandle, @Buffer, SizeOf(Buffer), BytesRead);
            SetString(StrBuffer, PAnsiChar(@Buffer[0]), BytesRead);
            Result := Result + StrBuffer;
          until BytesRead = 0;
        finally
          InternetCloseHandle(UrlHandle);
        end
      else
        raise Exception.CreateFmt('Cannot open URL %s', [Url]);
    finally
      InternetCloseHandle(NetHandle);
    end
  else
    raise Exception.Create('Unable to initialize Wininet');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  temp1, temp2: String;
  i : Integer;
begin
  Memo1.Clear;
  Temp1 := ''; Temp2 := '';
  Memo1.Lines.Add('Downloading Data from ' +Edit1.Text);
  Memo1.Lines.Add('Please Wait...');
  Temp1 := Edit1.Text;
  if Length(Temp1) > 0 then Temp2 := GetUrlContent(Temp1);
  Memo1.Lines.Text := Temp2;
  i := Length(Memo1.Lines.Text) ;
  Memo1.Lines.Add('HTTP/S HTML Source from: '+Temp1);
  Memo1.Lines.Add('Downloaded: '+IntToStr(Length(Temp2)) +' bytes, displaying: ' +IntToStr(i)+ ' chars. ' +IntToStr(Length(Temp2)-i)+ ' bytes are different!');
end;
Und hier die WinApi Variante, sollte auch HTTPS tauglich sein.
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  http: variant;
  temp1, temp2: String;
  i : Integer;
begin
  Memo1.Clear;
  Temp1 := ''; Temp2 := '';
  Memo1.Lines.Add('Downloading Data from ' +Edit1.Text);
  Memo1.Lines.Add('Please Wait...');
  Temp1 := Edit1.Text;
  http:=createoleobject('WinHttp.WinHttpRequest.5.1');
  http.open('GET', Temp1, false);
  http.send;
  Temp2 := http.responsetext;
  Memo1.Lines.Text := Temp2;
  i := Length(Memo1.Lines.Text) ;
  Memo1.Lines.Add('HTTP/S HTML Source from: '+Temp1);
  Memo1.Lines.Add('Downloaded: '+IntToStr(Length(Temp2)) +' bytes, displaying: ' +IntToStr(i)+ ' chars. ' +IntToStr(Length(Temp2)-i)+ ' bytes are different!');
end;
Total Oldschool way, ich hoffe es gefällt Dir und löst Dein Problem!
Hier der link zur Quelle.
Falls "Nullen" zum Problem werden, schau in den anderen Code und passe das so an.

edit
PS: Und es tut mir leid das ich am Titel-Thema mehr als vorbeigerauscht sein sollte (beide Samples benötigen kein Indy und beide Samples enthalten keinen MemoryStream)
PPS: Hier ein HTTPS Ergebnis :P
Zitat:

HTTP/S HTML Source from: https://www.delphipraxis.net/
Downloaded: 35261 bytes, displaying: 36143 chars. -882 bytes are different!


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:02 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