PHP 使用的 cURL 依靠 libcurl 库,libcurl 很好的支持 http、https,因而在登入和获取页面时完全可以充当浏览器的角色。

下面介绍一下我常用到的登入和获取页面代码,功能上,记录了登入 cookie,可以一次登入后免登入直至 cookie 过期。

应用场景:某些小型服务网站没有提供 API,但是要经常性的获取某些登入后的数据。

首先是登入函数

function curl_login($url, $ch, $cookie_jar, $loginvars) {

if (is_null($ch))
    $ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_REFERER, $url);
//curl_setopt($ch, CURLOPT_AUTOREFERER, 1); //某些场景同上
curl_setopt ($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0");
//curl_setopt($ch, CURLOPT_NOBODY, 1);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_jar);
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_jar);
// 将curl_exec()获取的信息以文件流的形式返回,而不是直接输出
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($loginvars));
//$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$content = curl_exec($ch);
if ($error = curl_error($ch)) {
    echo "Curl error: $error";
    return false;
}
return $ch;
}

接下来是获取页面函数

function curl_get($url, $ch, $cookie_jar, $post=''){

$timeout=30;
if (is_null($ch))
    $ch = curl_init();

curl_setopt ($ch, CURLOPT_URL, $url);
curl_setopt ($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt ($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0");
//curl_setopt ($ch, CURLOPT_HEADER, 1);
//curl_setopt($ch, CURLINFO_HEADER_OUT, 1); //To use this, have to set CURLINFO_HEADER_OUT=1
//curl_setopt ($ch, CURLOPT_COOKIESESSION, 0);
curl_setopt ($ch, CURLOPT_COOKIEFILE, $cookie_jar);
curl_setopt ($ch, CURLOPT_COOKIEJAR, $cookie_jar);
if (!empty($post))
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post));
else
    curl_setopt($ch, CURLOPT_HTTPGET, 1); //If the previous connecting use POST, this connecting will be use post as default. So force it to use GET.
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0);

$content = curl_exec ($ch);

if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == 302) { //The most of server return 302 and redirect to login page.
    echo "Waiting for login...\n";
    return false;
}

if ($error = curl_error($ch)) {
    echo "Curl error: $error\n";
    return NULL;
}
return $content;
}

如何使用函数?

$cookie_jar = dirname(__FILE__).DIRECTORY_SEPARATOR."curl.cookie";
$curl = NULL;
$page_url = 'https://www.example.com/admin';
$content = curl_get_page($page_url, $curl, $cookie_jar);

if ($content === false) { //Cookie expired, then login in and fetch again.
    $login_url = "https://www.example.com/login";
    $loginvars = array(
        'UserName' => 'user_007',
        'Password' => 'passwd',
        'rememberme' => 'true',
    );
    $curl = curl_login($login_url, $curl, $cookie_jar, $loginvars);
    $content = curl_get($page_url, $curl, $cookie_jar);
}

这里的流程是首先尝试获取页面,如果如果服务器发送 http 302 将你跳转到登入页面,你再执行登入并记录 cookie,如果获取到了之前保存的未过期 cookie,那么就会省去登入环节。

需要注意的是

  1. 我这里先设置 $curl 为 NULL 是为了配合登入和延续获取页面的会话,如果直接在两个函数里写 $ch = curl_init(); 那么登入成功后获取页面的 $ch 被重置了,也就销毁了登入会话,服务器还是当你没有登入。
  2. 将 cookie 文件保存在安全的位置
  3. 不能使用 $cookie_jar = “curl.cookie” 这样的相对路径
  4. curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0); 应用于 https 页面,也可以自行下载证书来认证而非忽略。

延伸

某些站点开启了机器检查,一种是判断 cookie,这里已经解决了,另外一种是在登入页面加了一个动态的请求验证令牌(Request Verification Token),只要先使用 curl_get()获取到这个令牌,然后在登陆的 POST 数据里提交这个令牌即可。

站内 Wiki : PHP cURL

参见:PHP 手册 – Client URL Library

在 “PHP cURL 登入与获取页面” 有 1 条评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据