我举个例子,首先决定要模拟什么浏览器,在这种情况下,我选择了 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头同步是维护负担,而且我从未遇到过标题顺序重要的情况)