IdHttp + IdCookieManager + ASP.NET WebForm页面[关闭]

时间:2013-08-13 18:28:42

标签: delphi captcha indy delphi-xe3 idhttp

首先,抱歉我的英语不好!

我需要在www.nfe.fazenda.org.br网站上进行查询。 为了获得最佳性能,请使用TIdHTTP组件和TIdCookieManager。

本网站使用验证码进行控制访问。所以,我正在尝试获取页面和验证码以获取cookie。

用户输入验证码和NFe的密钥。所以,我发送到帖子页面。

但是,当我运行帖子时,我被重定向到错误页面。

这是我的测试代码并请你帮助我。 谢谢!

unit Forms.MainForm;

interface

uses
  Winapi.Windows, Winapi.Messages,
  System.SysUtils, System.Variants, System.Classes,
  Vcl.Forms, Vcl.Graphics, Vcl.Dialogs, Vcl.Controls, Vcl.ExtCtrls,
  Vcl.StdCtrls,
  IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP,
  IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack, IdSSL, IdSSLOpenSSL,
  IdCookieManager, IdCookie, IdURI,
  GIFImg, WinInet;

type
  TMainForm = class(TForm)
    mem: TMemo;
    IdHttp: TIdHTTP;
    IdSSLHandlerSocket: TIdSSLIOHandlerSocketOpenSSL;
    IdCookieManager: TIdCookieManager;
    panBottom: TPanel;
    btnGo: TButton;
    imgCaptcha: TImage;
    edtKey: TEdit;
    edtCode: TEdit;
    lblInit: TLabel;
    procedure FormShow(Sender: TObject);
    procedure lblInitClick(Sender: TObject);
    procedure btnGoClick(Sender: TObject);
  private
    Cookies: TIdCookies;
    viewState, eventValidate: string;
    procedure GetHiddenFieldValues(html: string);
    procedure p_Execute;
  end;

var
  MainForm: TMainForm;

const
  HOST         = 'http://www.nfe.fazenda.gov.br';
  URLIMG       = 'http://www.nfe.fazenda.gov.br/scripts/srf/intercepta/captcha.aspx?opt=image';
  URLGET       = 'http://www.nfe.fazenda.gov.br/portal/consulta.aspx?tipoConsulta=completa&tipoConteudo=XbSeqxE8pl8=';
  URLPOST      = 'http://www.nfe.fazenda.gov.br/portal/consultaCompleta.aspx?tipoConteudo=XbSeqxE8pl8=';
  CONTENT_TYPE = 'application/x-www-form-urlencoded';

implementation

{$R *.dfm}

procedure TMainForm.FormShow(Sender: TObject);
begin
  lblInitClick(Sender);
end;

procedure TMainForm.lblInitClick(Sender: TObject);
var
  response: TMemoryStream;
  gif: TGIFImage;
  html: string;
begin
  response := TMemoryStream.Create;
  gif := TGIFImage.Create;
  try
    html := IdHttp.Get(URLGET);
    mem.Text := html;
    GetHiddenFieldValues(html);

    IdHttp.Get(URLIMG, response);
    response.Position := 0;
    gif.LoadFromStream(response);
    imgCaptcha.Picture.Assign(gif);

    Cookies := IdCookieManager.CookieCollection;
  finally
    gif.Free;
    response.Free;
  end;
end;

procedure TMainForm.btnGoClick(Sender: TObject);
begin
  p_Execute;
end;

procedure TMainForm.GetHiddenFieldValues(html: string);
var
  nIni, nLen: integer;
  cVal: string;
const
  TAG_VIEWSTATE = '<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="';
  TAG_EVENTVALIDATION = '<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="';
begin
  nIni := Pos(TAG_VIEWSTATE, html);
  nLen := Length(TAG_VIEWSTATE);
  cVal := Copy(html,nIni+nLen, Length(html));
  cVal := Copy(cVal, 1, Pos('" />', cVal)-1);
  viewState := cVal;

  nIni := Pos(TAG_EVENTVALIDATION, html);
  nLen := Length(TAG_EVENTVALIDATION);
  cVal := Copy(html,nIni+nLen, Length(html));
  cVal := Copy(cVal, 1, Pos('" />', cVal)-1);
  eventValidate := cVal;
end;

procedure TMainForm.p_Execute;
var
  params: TStringList;
  Uri: TIdURI;
  nI: Integer;
begin
  params := TStringList.Create;
  Uri := TIdURI.Create(Cookies[0].Domain);
  try
    for nI := 0 to Pred(Cookies.Count) do
      begin
        IdCookieManager.AddServerCookie(Cookies[nI].ClientCookie, Uri);
        if nI = 0 then
          IdHttp.Request.CustomHeaders.Values['Cookie'] := Cookies[nI].ClientCookie
        else
          IdHttp.Request.CustomHeaders.Values['Cookie'] := IdHttp.Request.CustomHeaders.Values['Cookie'] + '; ' + Cookies[nI].ClientCookie;
      end;

    params.Add('__VIEWSTATE=' + viewState);
    params.Add('__EVENTVALIDATION=' + eventValidate);

    params.Add('__EVENTTARGET=');
    params.Add('__EVENTARGUMENT=');

    params.Add('ctl00$txtPalavraChave=');

    params.Add('ctl00$ContentPlaceHolder1$txtChaveAcessoCompleta=' + edtKey.Text);
    params.Add('ctl00$ContentPlaceHolder1$txtCaptcha=' + edtCode.Text);

    params.Add('ctl00$ContentPlaceHolder1$btnConsultar=Continuar');
    params.Add('hiddenInputToUpdateATBuffer_CommonToolkitScripts=1');

    IdHttp.Request.ContentType := CONTENT_TYPE;
    mem.Text := IdHttp.Post(URLPOST, params);
  finally
    params.Free;
    Uri.Free;
  end;
end;

end.

2 个答案:

答案 0 :(得分:0)

您错误管理了服务器的Cookie。 TIdURI.Create()需要一个完整的网址,而AddServerCookie()需要一个完整的网址,以便它可以处理路径,区分HTTP和HTTPS Cookie等。但是您只传递TIdURI个域名,这还不够。就此而言,当TIdCookieManager中已存在Cookie时,为什么要将Cookie重新添加回TIdCookieManager?为什么要手动设置CustomHeaders.Values['Cookie']属性?不要做那些事情。您需要做的就是将TIdCookieManager分配给TIdHTTP.CookieManager属性,并确保TIdHTTP.AllowCookies属性设置为True。而已。然后,TIdHTTPTIdCookieManager将为您完成接收,管理和发送Cookie的所有艰苦工作。只要您对多个HTTP请求使用相同的TIdCookieManager对象(即使您不使用相同的TIdHTTP对象),cookie也会根据需要自动从一个请求持续到下一个请求。就此而言,如果您重复使用相同的TIdHTTP对象,那么您根本不必担心创建TIdCookieManager,因为如果需要,TIdHTTP将在内部创建一个,它将用于TIdHTTP对象的生命周期。

请改为尝试:

unit Forms.MainForm;

interface

uses
  Winapi.Windows, Winapi.Messages,
  System.SysUtils, System.Variants, System.Classes,
  Vcl.Forms, Vcl.Graphics, Vcl.Dialogs, Vcl.Controls, Vcl.ExtCtrls,
  Vcl.StdCtrls,
  IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP,
  IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack, IdSSL, IdSSLOpenSSL,
  GIFImg;

type
  TMainForm = class(TForm)
    mem: TMemo;
    IdHttp: TIdHTTP;
    IdSSLHandlerSocket: TIdSSLIOHandlerSocketOpenSSL;
    panBottom: TPanel;
    btnGo: TButton;
    imgCaptcha: TImage;
    edtKey: TEdit;
    edtCode: TEdit;
    lblInit: TLabel;
    procedure FormShow(Sender: TObject);
    procedure lblInitClick(Sender: TObject);
    procedure btnGoClick(Sender: TObject);
  private
    viewState, eventValidate: string;
    procedure GetHiddenFieldValues(html: string);
    procedure p_Execute;
  end;

var
  MainForm: TMainForm;

const
  HOST         = 'http://www.nfe.fazenda.gov.br';
  URLIMG       = HOST+'/scripts/srf/intercepta/captcha.aspx?opt=image';
  URLGET       = HOST+'/portal/consulta.aspx?tipoConsulta=completa&tipoConteudo=XbSeqxE8pl8=';
  URLPOST      = HOST+'/portal/consultaCompleta.aspx?tipoConteudo=XbSeqxE8pl8=';

implementation

{$R *.dfm}

procedure TMainForm.FormShow(Sender: TObject);
begin
  lblInitClick(Sender);
end;

procedure TMainForm.lblInitClick(Sender: TObject);
var
  response: TMemoryStream;
  gif: TGIFImage;
  html: string;
begin
  html := IdHttp.Get(URLGET);
  mem.Text := html;
  GetHiddenFieldValues(html);

  gif := TGIFImage.Create;
  try
    response := TMemoryStream.Create;
    try
      IdHttp.Get(URLIMG, response);
      response.Position := 0;
      gif.LoadFromStream(response);
    finally
      response.Free;
    end;
    imgCaptcha.Picture.Assign(gif);
  finally
    gif.Free;
  end;
end;

procedure TMainForm.btnGoClick(Sender: TObject);
begin
  p_Execute;
end;

procedure TMainForm.GetHiddenFieldValues(html: string);
var
  nIni, nLen: integer;
  cVal: string;
const
  TAG_VIEWSTATE = '<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="';
  TAG_EVENTVALIDATION = '<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="';
begin
  nIni := Pos(TAG_VIEWSTATE, html);
  nLen := Length(TAG_VIEWSTATE);
  cVal := Copy(html,nIni+nLen, Length(html));
  cVal := Copy(cVal, 1, Pos('" />', cVal)-1);
  viewState := cVal;

  nIni := Pos(TAG_EVENTVALIDATION, html);
  nLen := Length(TAG_EVENTVALIDATION);
  cVal := Copy(html,nIni+nLen, Length(html));
  cVal := Copy(cVal, 1, Pos('" />', cVal)-1);
  eventValidate := cVal;
end;

procedure TMainForm.p_Execute;
var
  params: TStringList;
begin
  params := TStringList.Create;
  try    
    params.Add('__VIEWSTATE=' + viewState);
    params.Add('__EVENTVALIDATION=' + eventValidate);

    params.Add('__EVENTTARGET=');
    params.Add('__EVENTARGUMENT=');

    params.Add('ctl00$txtPalavraChave=');

    params.Add('ctl00$ContentPlaceHolder1$txtChaveAcessoCompleta=' + edtKey.Text);
    params.Add('ctl00$ContentPlaceHolder1$txtCaptcha=' + edtCode.Text);

    params.Add('ctl00$ContentPlaceHolder1$btnConsultar=Continuar');
    params.Add('hiddenInputToUpdateATBuffer_CommonToolkitScripts=1');

    mem.Text := IdHttp.Post(URLPOST, params);
  finally
    params.Free;
  end;
end;

end.

现在,说到这一点,还有其他问题:

1)您将params发布到http://www.nfe.fazenda.gov.br/portal/consultaCompleta.aspx?tipoConteudo=XbSeqxE8pl8=,但是当我使用网络浏览器访问登录网址并查看HTML时,我发现它确实希望将表单发布到{ {1}}而是。您错过了http://www.nfe.fazenda.gov.br/consulta.aspx?tipoConsulta=completa&amp;tipoConteudo=XbSeqxE8pl8%3d部分。

2)您正在解析HTML中的实际tipoConsulta=completa__VIEWSTATE值,但是会发送空白__EVENTVALIDATION__EVENTTARGET值。这些值在HTML中是空白的,但它们实际上是通过客户端脚本动态填充的。

3)HTML中还有其他__EVENTARGUMENT字段未发布。

网络浏览器会发布分配给它的非空<input>的每个<input>字段,无论该值是静态还是动态分配的。您需要在应用程序中执行相同的操作。 HTTP服务器可能期望发送所有这些值。使用数据包嗅探器(如Wireshark或Fiddler)查看Web浏览器实际发布的内容,然后在代码中复制相同的行为。

答案 1 :(得分:0)

非常感谢!
问题解决了!

通过您的信息和Wireshark,我意识到我需要一个新的“GET”。 解决!

unit Forms.MainForm;

interface

uses
  Winapi.Windows, Winapi.Messages,
  System.SysUtils, System.Variants, System.Classes,
  Vcl.Forms, Vcl.Graphics, Vcl.Dialogs, Vcl.Controls, Vcl.ExtCtrls,
  Vcl.StdCtrls,
  IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP,
  IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack, IdSSL, IdSSLOpenSSL,
  IdCookieManager, IdCookie, IdURI,
  GIFImg;

type
  TMainForm = class(TForm)
    mem: TMemo;
    IdHttp: TIdHTTP;
    IdSSLHandlerSocket: TIdSSLIOHandlerSocketOpenSSL;
    panBottom: TPanel;
    btnGo: TButton;
    imgCaptcha: TImage;
    edtKey: TEdit;
    edtCode: TEdit;
    lblInit: TLabel;
    procedure FormShow(Sender: TObject);
    procedure lblInitClick(Sender: TObject);
    procedure btnGoClick(Sender: TObject);
  private
    viewState, eventValidate: string;
    procedure GetHiddenFieldValues(html: string);
    procedure p_Execute;
  end;

var
  MainForm: TMainForm;

const
  HOST         = 'http://www.nfe.fazenda.gov.br';
  URLIMG       = HOST + '/scripts/srf/intercepta/captcha.aspx?opt=image';
  URLGET       = HOST + '/portal/consulta.aspx?tipoConsulta=completa&tipoConteudo=XbSeqxE8pl8=';
  URLPOST      = HOST + '/portal/consulta.aspx?tipoConsulta=completa&tipoConteudo=XbSeqxE8pl8%3d';
  URLGETRESULT = HOST + '/portal/consultaCompleta.aspx?tipoConteudo=XbSeqxE8pl8=';
  CONTENT_TYPE = 'application/x-www-form-urlencoded';

implementation

{$R *.dfm}

procedure TMainForm.FormShow(Sender: TObject);
begin
  lblInitClick(Sender);
end;

procedure TMainForm.lblInitClick(Sender: TObject);
var
  response: TMemoryStream;
  gif: TGIFImage;
  html: string;
begin
  html := IdHttp.Get(URLGET);
  mem.Text := html;
  GetHiddenFieldValues(html);

  response := TMemoryStream.Create;
  gif := TGIFImage.Create;
  try
    IdHttp.Get(URLIMG, response);
    response.Position := 0;
    gif.LoadFromStream(response);
    imgCaptcha.Picture.Assign(gif);
  finally
    gif.Free;
    response.Free;
  end;
end;

procedure TMainForm.btnGoClick(Sender: TObject);
begin
  p_Execute;
end;

procedure TMainForm.GetHiddenFieldValues(html: string);
var
  nIni, nLen: integer;
  cVal: string;
const
  TAG_VIEWSTATE = '<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="';
  TAG_EVENTVALIDATION = '<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="';
begin
  nIni := Pos(TAG_VIEWSTATE, html);
  nLen := Length(TAG_VIEWSTATE);
  cVal := Copy(html,nIni+nLen, Length(html));
  cVal := Copy(cVal, 1, Pos('" />', cVal)-1);
  viewState := cVal;

  nIni := Pos(TAG_EVENTVALIDATION, html);
  nLen := Length(TAG_EVENTVALIDATION);
  cVal := Copy(html,nIni+nLen, Length(html));
  cVal := Copy(cVal, 1, Pos('" />', cVal)-1);
  eventValidate := cVal;
end;

procedure TMainForm.p_Execute;
var
  params: TStringList;
begin
  params := TStringList.Create;
  try
    params.Add('__VIEWSTATE=' + viewState);
    params.Add('__EVENTVALIDATION=' + eventValidate);

    params.Add('__EVENTTARGET=');
    params.Add('__EVENTARGUMENT=');

    params.Add('ctl00$txtPalavraChave=');

    params.Add('ctl00$ContentPlaceHolder1$txtChaveAcessoCompleta=' + Trim(edtKey.Text));

    params.Add('ctl00$ContentPlaceHolder1$txtCaptcha=' + Trim(edtCode.Text));

    params.Add('ctl00$ContentPlaceHolder1$btnConsultar=Continuar');
    params.Add('hiddenInputToUpdateATBuffer_CommonToolkitScripts=1');

    mem.Text := IdHttp.Post(URLPOST, params);

    mem.Text := IdHttp.Get(URLGETRESULT);
  finally
    params.Free;
  end;
end;

end.