Page 1 of 1

Get captcha image

Posted: Wed Mar 17, 2021 8:42 pm
by yaser6403
hi, i want to get image of simple captcha to solve by ocr, but i can't found any way.
if download image by DownloadURL, i get new image and it's incorrect. i can't use screenshot because Visible of browser is False. i try to use OnResourceLoadComplete event but not successful because no access to memory stream. please help me if any way exists

Re: Get captcha image

Posted: Thu Mar 18, 2021 10:37 am
by demonslord
Hi yaser6403,

You need register your custom TCefv8HandlerOwn on GlobalCEFApp_OnWebKitInitializedEvent, which contain javascript code to grab your captcha image:

Code: Select all

procedure GlobalCEFApp_OnWebKitInitializedEvent;
var
  TempHandler       : ICefv8Handler;
  JS: TScriptList;
begin
  JS:= TScriptList.Create;
  try
    JS.Add('var myextension;');
    JS.Add('if (!myextension)');
    JS.Add('  myextension = {};');
    JS.Add('(function() {');
    JS.Add(' myextension.getImage = function(img) {');
    JS.Add('   var canvas = document.createElement("canvas");');
    JS.Add('   canvas.width = img.width;');
    JS.Add('   canvas.height = img.height;');
    JS.Add('   var pixels = "";');
    JS.Add('   if (img.naturalWidth && img.naturalHeight) {');
    JS.Add('     var ctx = canvas.getContext("2d");');
    JS.Add('     ctx.drawImage(img, 0, 0);');
    JS.Add('     pixels = canvas.toDataURL("image/png");');
    JS.Add('   }');
    JS.Add('   native function SetImage();');
    JS.Add('   return SetImage(img.width,img.height,pixels,"'+CHROMIUM_JS_SET_IMAGE_MSGNAME+'");');
    JS.Add(' }');
    JS.Add('})();');

    TempHandler := TMyImageHandler.Create;
    CefRegisterExtension('myextension', JS.Text, TempHandler)
  finally
    TempHandler:= nil;
    JS.Free;
  end;
end;
On TCefv8HandlerOwn.Execute, you need handle the message send by browser and handle.

Code: Select all

  TMyImageHandler = class(TCefv8HandlerOwn)
  protected
    function Execute(const name: ustring; const obj: ICefv8Value; const arguments: TCefv8ValueArray; var retval: ICefv8Value; var exception: ustring): Boolean; override;
  end;

Code: Select all

function TMyImageHandler.Execute(const name: ustring; const obj: ICefv8Value; const arguments: TCefv8ValueArray; var retval: ICefv8Value;
  var exception: ustring): Boolean;
var
  TempMessage : ICefProcessMessage;
  TempFrame   : ICefFrame;
  Pixels: string;
  p: Integer;
  EventMessageName: string;
begin
  Result := False;
  try
    if (name = 'SetImage') then
    begin
      Pixels:= arguments[2].GetStringValue;
      p:= Pos(',',Pixels);
      if p > 0 then
        System.Delete(Pixels,1,p);

      EventMessageName:= arguments[3].GetStringValue;

      TempMessage := TCefProcessMessageRef.New(EventMessageName);
      TempMessage.ArgumentList.SetString(0, Pixels);

      TempFrame := TCefv8ContextRef.Current.Browser.MainFrame;
      if (TempFrame <> nil) and TempFrame.IsValid then
        TempFrame.SendProcessMessage(PID_BROWSER, TempMessage);

      Result := True;
    end;

  finally
    TempMessage := nil;
  end;
end;
On Browser_OnProcessMessageReceived event, you need handle the base64 of image, like this:

Code: Select all

if (message.Name = CHROMIUM_JS_SET_IMAGE_MSGNAME) then
  begin
    Pixels:= message.ArgumentList.GetString(0);
    if Pixels <> '' then
    begin
      Stream:= TMemoryStream.Create;
      MyBase64Decode(Pixels,Stream);
      Png:= TPngImage.Create;
      Stream.Position:= 0;
      Png.LoadFromStream(Stream);
      DoImage(Png);
    end else
    begin
      DoImage(nil);
    end;
  end;
BR

Re: Get captcha image

Posted: Mon Mar 22, 2021 9:57 am
by dilfich
Watch the Demo with DownloadImage, specify the link and that's it, if the image is already downloaded, then through DownloadImage you will get it from the cache, which means it will not be new.
(the main thing is that the link of the ball is the same)