【问题标题】:Posting Form Data With idHTTP in Delphi在 Delphi 中使用 idHTTP 发布表单数据
【发布时间】:2016-01-26 16:55:26
【问题描述】:

我正在使用带有 IdHTTP 库的 Delphi 从该 URL (http://apply.dataprocessors.com.au) 获取表单,处理难题并发布正确答案。但是,显然我没有正确处理 POST 方法,一旦检索到的响应是:“提交问题。请重试。”

下面是Delphi源码:

procedure TForm1.Button1Click(Sender: TObject);
var
  sGET, sPOST, sGET_count: String;
  countCircles: integer;
  parameters: TStringList;
begin
  parameters := TStringList.Create;
  sGET := http.Get('http://apply.dataprocessors.com.au');
  sGET_count := StringReplace(sGET, '$', ' ', [rfReplaceAll, rfIgnoreCase]);
  sGET_count := StringReplace(sGET_count, 'unfilled', '$', [rfReplaceAll, rfIgnoreCase]);
  countCircles := 120 - sGET_count.CountChar('$');

  parameters.Add('title=submit');
  parameters.Add('value=' + countCircles.ToString());
  parameters.Add('submit=Submit');
  http.Request.ContentType := 'application/x-www-form-urlencoded';
  sPOST := http.Post('http://apply.dataprocessors.com.au/?', parameters);
  txtPost.Lines.Add(sPOST);
end; 

Form retrieved

HTML 代码:

<html>
  <head>
  </head>
<body>
<form action="" method="POST">
      <input type="hidden" name="title" value="submit">
      <p>How many filled circles do you see:? <br><img src="unfilled_O.gif" height="12" width="12"><br></p>
      <p>Answer: <input type="text" name="value" value="" size="10"></p>
      <p><input type="Submit" value="Submit">
</form></body>
</html>

从 POST 方法检索到的响应:

<html>
  <head>
  </head>
  <body>
  <p>Problem with submission. Please try again.</p></body>
</html>

【问题讨论】:

  • 使用 HTTP 代理来捕获您的浏览器通信并查看“工作”请求的样子
  • 你有什么推荐的程序吗?
  • 不需要,在任何现代浏览器中使用 F12 功能(或使用 firebug 和 firefox)并检查网络请求。
  • 好像没有设置TIdHttp.Request.ContentType(应该是'application/x-www-form-urlencoded')。另请参阅此答案stackoverflow.com/a/7769356/1744164
  • @SirRufo:TIdHTTP.Post()TStrings 版本自动将Request.ContentType 设置为application/x-www.webform-urlencoded(如果尚未设置)。

标签: delphi post indy


【解决方案1】:

我认为您的TIdHTTP 代码没有问题(尽管Post() 的URL 末尾的/? 不是必需的)。您的@987654324 中缺少步骤@ 代码。详情请参见下文。

我还发现您的StringReplace() 代码有问题:

sGET := http.Get('http://apply.dataprocessors.com.au');
sGET_count := StringReplace(sGET, '$', ' ', [rfReplaceAll, rfIgnoreCase]);
sGET_count := StringReplace(sGET, 'unfilled', '$', [rfReplaceAll, rfIgnoreCase]);

注意第二个StringReplace() 的第一个参数。在第二次调用中,您正在搜索由TIdHTTP.Get() 返回的原始 HTML。您不是在搜索第一个 StringReplace() 的结果。然后,当您重新分配 sGET_count 时,您将完全丢弃第一个 StringReplace() 的结果,从而有效地使第一次调用成为无操作。您可能打算这样做:

sGET := http.Get('http://apply.dataprocessors.com.au');
sGET_count := StringReplace(sGET, '$', ' ', [rfReplaceAll, rfIgnoreCase]);
sGET_count := StringReplace({sGET}sGET_count, 'unfilled', '$', [rfReplaceAll, rfIgnoreCase]);

更新:当我在浏览器中访问 URL 时,我发现您的代码没有考虑到一些事情。一,网络表单包含一个jobref 字段,您未在POST 提交中包含该字段。但更重要的是,当网页被真正的网络浏览器检索时,有问题的网页会调用 JavaScript 函数,而不是在您当前的TIdHTTP 设置检索时。该 Javascript 位于 http://apply.dataprocessors.com.au/funcs1.js 并包含一个用于将 cookie 动态添加到网络浏览器的命令:

function init() {
  document.cookie = 'pc3o=d2r22; path=';
}

此命令由 HTML 调用:

<html>
  <head>
    ...
    <script type="text/javascript" src="funcs1.js"></script>
  </head>
<body onLoad="init()">
  ...
  </html>

除了在检索 HTML 时 HTTP 标头中存在的普通 PHP cookie 之外,当网络浏览器提交网络表单时,额外的 cookie 会被发送回网络服务器。您需要将TIdHTTP.AllowCookies 属性设置为True(默认为False),以便TIdHTTP.Post() 可以发送回cookie,但是Post() 默认只会发送PHP cookie,而不是Javascript cookie,原因很明显( TIdHTTP 不调用客户端脚本)。

如果您更改 TIdHTTP.Request.UserAgent 属性以模仿真实的网络浏览器,而不是使用 Indy 的默认 UserAgent,下载的 HTML 将包含该 JavaScript 引用。这是可选的,因为您可以手动将 Javascript cookie(幸运的是它是静态的,因此您不需要实际解析 Javascript,但如果您愿意也可以)添加到 TIdHTTP.CookieManager,而不管 UserAgentPost() 仍然会发送它,服务器似乎并不关心 HTML 是否包含 JavaScript 引用。

话虽如此,以下代码对我有用(我让服务器向POST 发送成功响应):

procedure TForm1.Button1Click(Sender: TObject);
var
  sGET, sPOST, sGET_count: String;
  countCircles: integer;
  parameters: TStringList;
begin
  parameters := TStringList.Create;
  try
    http.AllowCookies := True;
    // optional: http.Request.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko';

    sGET := http.Get('http://apply.dataprocessors.com.au');
    http.CookieManager.AddServerCookie('pc3o=d2r22; path=', http.URL);

    sGET_count := StringReplace(sGET, '$', '', [rfReplaceAll, rfIgnoreCase]);
    sGET_count := StringReplace(sGET_count, '"filled_O.gif"', '$', [rfReplaceAll, rfIgnoreCase]);
    countCircles := sGET_count.CountChar('$');

    parameters.Add('title=submit');
    parameters.Add('jobref=');
    parameters.Add('value=' + countCircles.ToString());

    http.Request.ContentType := 'application/x-www-form-urlencoded';
    http.Request.Referer := 'http://apply.dataprocessors.com.au';

    sPOST := http.Post('http://apply.dataprocessors.com.au', parameters);
    ShowMessage(sPOST);
  finally
    parameters.Free;
  end;
end;

【讨论】:

  • 没错,第二个 StringReplace() 错了,我的意思就是你解释的。但是第一个 StringReplace() 只是为了安全,以防万一 HTML 中有“$”会破坏数学。正如预期的那样,即使解决了这个问题,我仍然收到相同的消息。无论如何,谢谢你的帮助。
  • 真的很有趣。提到“如果您更改 TIdHTTP.Request.UserAgent 属性以模仿真正的网络浏览器,而不是使用 Indy 的默认 UserAgent,下载的 HTML 将包含该 JavaScript 引用。”您能解释一下吗?为什么?提前谢谢你。
  • 一些网络服务器向不同的用户代理发送不同的内容。某些浏览器具有与其他浏览器不同的怪癖/行为,因此有时网页作者会创建特定于浏览器的内容、特定于浏览器的脚本等。在这种情况下,apply.dataprocessors.com.au 显然是用户代理感知的,因为它根据不同的浏览器调整 HTML User-Agent 标头值。
  • 显然太多useragent-aware :D 太明显了,我想不通。再次感谢楼主
  • 允许并添加 cookie 有效。感谢您对解决方案的帮助和出色的解释。
【解决方案2】:

使用 F12(或 HTTP 代理)捕获您的浏览器通信并查看“工作”请求的样子。

工作请求是否使用 cookie?如果是,请为 IdHttp 启用 cookie 支持。

【讨论】:

  • 我在 google chrome 上检查了 POST 方法,我已经得到了相同的响应,但只是在为拼图发送错误答案时。当我发送正确答案时,我得到:提交问题。请重试。
  • 实际上,您发送的是正确答案,您只是没有发送服务器期望您发送的所有内容。详情见我的回答。
猜你喜欢
  • 1970-01-01
  • 2020-01-29
  • 1970-01-01
  • 1970-01-01
  • 2017-06-05
  • 2013-01-20
  • 2020-12-02
  • 2014-11-10
  • 2015-08-19
相关资源
最近更新 更多