如何模拟一个与网页浏览器完全相同的 GET 请求?

60

有些网站,当我在浏览器上打开特定的ajax请求时,我可以获得结果页面。但是当我尝试使用curl加载它们时,我会从服务器收到错误。

如何正确地模拟一个get请求到服务器,以模拟浏览器?

这是我正在做的事情:

$url="https://new.aol.com/productsweb/subflows/ScreenNameFlow/AjaxSNAction.do?s=username&f=firstname&l=lastname";
ini_set('user_agent', 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT
5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)');
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL,$url);
$result=curl_exec($ch);
print $result;

你从服务器得到了什么错误? - bmb
网站返回一个XML错误信息:“标签错误”。 - ufk
几乎确定它会忽略 JS。 - mpen
2个回答

87

您确定 curl 模块会遵守 ini_set('user_agent',...) 吗?文档中有 CURLOPT_USERAGENT 选项的描述,请参阅http://docs.php.net/function.curl-setopt
服务器是否还有 cookie 测试?您可以使用 CURLOPT_COOKIE、CURLOPT_COOKIEFILE 和/或 CURLOPT_COOKIEJAR 来处理。

编辑:由于请求使用 https,可能还会出现验证证书的错误,请参阅 CURLOPT_SSL_VERIFYPEER。

$url="https://new.aol.com/productsweb/subflows/ScreenNameFlow/AjaxSNAction.do?s=username&f=firstname&l=lastname";
$agent= 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)';

$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, $agent);
curl_setopt($ch, CURLOPT_URL,$url);
$result=curl_exec($ch);
var_dump($result);

5
太棒了。我也遇到了同样的问题。将 SSL_VERIFYPEER=false 设置为真正解决了问题。谢谢! - beetree
1
Sisir:请查看https://dev59.com/NXM_5IYBdhLWcg3wQQtd - VolkerK
嘿,如果我想从“前端”页面访问到“目标”页面(同一域),该怎么办?我不知道为什么,但是当我直接访问“目标”页面时,它会响应:“试图制作机器人?”但是当我先通过浏览器访问“前端”页面时,响应是正常的。 - Anggie Aziz
1
你是否保存了cookie以在第二个URL步骤中使用? 使用curl_setopt($ch, CURLOPT_COOKIEJAR, 'file or path');设置第一步, 使用curl_setopt($ch, CURLOPT_COOKIEFILE, 'file or path');读取第二步。也许你还需要使用referer,例如curl_setopt($ch, CURLOPT_REFERER, true); 在这种情况下,你可以使用域名(或IP)。 - m3nda
设置 cookie jar 帮助我解决了我的问题。 - GTCrais
显示剩余2条评论

15

我举个例子,首先决定要模拟什么浏览器,在这种情况下,我选择了 Firefox 60.6.1esr (64-bit),并检查它发出了什么 GET 请求,可以通过简单的 netcat 服务器获取该请求(MacOS 捆绑了 netcat,大多数 Linux 发行版捆绑了 netcat,Windows 用户可以从.. Cygwin.org 等地方获取 netcat)。

将 netcat 服务器设置为侦听端口9999:nc -l 9999

现在在 firefox 中打开 http://127.0.0.1:9999,我得到:

$ nc -l 9999
GET / HTTP/1.1
Host: 127.0.0.1:9999
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1

现在让我们将其与这个简单脚本进行比较:

<?php
$ch=curl_init("http://127.0.0.1:9999");
curl_exec($ch);

我理解:

$ nc -l 9999
GET / HTTP/1.1
Host: 127.0.0.1:9999
Accept: */*

这里有几个缺失的头部,可以使用curl_setopt的CURLOPT_HTTPHEADER选项全部添加,但是特别需要用CURLOPT_USERAGENT设置User-Agent,因为它会持续存在于多次调用curl_exec(),并且如果你使用了CURLOPT_FOLLOWLOCATION,则它将在HTTP重定向中持久存在。而Accept-Encoding头部应该使用CURLOPT_ENCODING设置(如果使用CURLOPT_ENCODING设置,则如果服务器选择进行压缩,CURL将自动解压响应,但如果您通过CURLOPT_HTTPHEADER设置,则必须手动检测和解压内容,这是相当麻烦和完全没有必要的,通常说来),因此添加这些后如下:

<?php
$ch=curl_init("http://127.0.0.1:9999");
curl_setopt_array($ch,array(
        CURLOPT_USERAGENT=>'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0',
        CURLOPT_ENCODING=>'gzip, deflate',
        CURLOPT_HTTPHEADER=>array(
                'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                'Accept-Language: en-US,en;q=0.5',
                'Connection: keep-alive',
                'Upgrade-Insecure-Requests: 1',
        ),
));
curl_exec($ch);

现在运行该代码,我们的netcat服务器会得到:

$ nc -l 9999
GET / HTTP/1.1
Host: 127.0.0.1:9999
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept-Encoding: gzip, deflate
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Connection: keep-alive
Upgrade-Insecure-Requests: 1

大功告成!我们 PHP 模拟的 浏览器 GET 请求现在应该和真正的 Firefox GET 请求无法区分 :)

接下来这一部分只是一些微调,但是如果你仔细看,你会发现头文件的顺序不正确,Firefox 将 Accept-Encoding 头文件放在第 6 行,而我们模拟的 GET 请求将其放在第 3 行.. 为了解决这个问题,我们可以手动将 Accept-Encoding 头文件放在正确的行。

<?php
$ch=curl_init("http://127.0.0.1:9999");
curl_setopt_array($ch,array(
        CURLOPT_USERAGENT=>'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0',
        CURLOPT_ENCODING=>'gzip, deflate',
        CURLOPT_HTTPHEADER=>array(
                'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                'Accept-Language: en-US,en;q=0.5',
                'Accept-Encoding: gzip, deflate',
                'Connection: keep-alive',
                'Upgrade-Insecure-Requests: 1',
        ),
));
curl_exec($ch);

运行后,我们的 netcat 服务器会收到:

$ nc -l 9999
GET / HTTP/1.1
Host: 127.0.0.1:9999
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1

问题已经解决,现在标题甚至按正确的顺序排列,请求似乎与真正的 Firefox 请求完全无法区分 :)(我实际上不建议此最后一步,因为保持CURLOPT_ENCODING与自定义Accept-Encoding头同步是维护负担,而且我从未遇到过标题顺序重要的情况)


1
非常感谢,我很感激你的回答!Upgrade-Insecure-Requests: 1 修复了我的问题。 - sergej

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接