1

首先,对不起我的英语不好!

我需要在网站 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.
4

2 回答 2

0

您对服务器的 cookie 管理不善。 TIdURI.Create()需要一个完整的 URL,并且AddServerCookie()需要一个完整的 URL,以便它可以处理路径、区分 HTTP 和 HTTPS cookie 等。但是您只传递TIdURI一个域名本身,这还不够。

TIdCookieManager当它们已经存在于 中时,为什么要将它们重新添加回中TIdCookieManager?为什么要CustomHeaders.Values['Cookie']手动设置属性?不要做那些事情。您需要做的就是将 分配TIdCookieManager给该TIdHTTP.CookieManager属性并确保该TIdHTTP.AllowCookies属性设置为 True。而已。 TIdHTTP然后TIdCookieManager将为您完成接收、管理和发送 cookie 的所有艰苦工作。只要您TIdCookieManager对多个 HTTP 请求使用同一个对象(即使您不使用同一个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) 您正在发布您的paramsto http://www.nfe.fazenda.gov.br/portal/consultaCompleta.aspx?tipoConteudo=XbSeqxE8pl8=,但是当我使用 Web 浏览器访问登录 URL 并查看 HTML 时,我发现它实际上希望将表单发布到http://www.nfe.fazenda.gov.br/consulta.aspx?tipoConsulta=completa&tipoConteudo=XbSeqxE8pl8=。你错过了这tipoConsulta=completa部分。

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

3) HTML 中还有其他<input>字段您未发布。

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

于 2013-08-14T04:24:40.350 回答
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.
于 2013-08-14T13:24:45.900 回答