як отримати файли cookie з php curl в змінну


126

Тож якийсь хлопець в якійсь іншій компанії вважав, що це буде дивним, якщо замість використання мила або xml-rpc або відпочинку або будь-якого іншого розумного протоколу зв’язку він просто вклав у свій заголовок всю свою відповідь як куки.

Мені потрібно витягнути ці файли cookie, як сподіваємось, масив із цієї відповіді на завивку. Якщо мені доведеться витрачати купу свого життя, пишучи парсер для цього, я буду дуже нещасний.

Хтось знає, як це можна зробити просто, бажано, не записуючи нічого у файл?

Я буду дуже вдячний, якщо хтось може мені допомогти у цьому.

Відповіді:


174
$ch = curl_init('http://www.google.com/');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// get headers too with this line
curl_setopt($ch, CURLOPT_HEADER, 1);
$result = curl_exec($ch);
// get cookie
// multi-cookie variant contributed by @Combuster in comments
preg_match_all('/^Set-Cookie:\s*([^;]*)/mi', $result, $matches);
$cookies = array();
foreach($matches[1] as $item) {
    parse_str($item, $cookie);
    $cookies = array_merge($cookies, $cookie);
}
var_dump($cookies);

31
На жаль, у мене є відчуття, що це правильна відповідь. Я думаю, що це смішно, що завиток не може просто вручити мені накреслений масив.
спраг9393

3
Я дам вам це, але preg_match помилився. Я не просто хотів сеансу, тому я розумію, чому ви так подумаєте. Але геній, який створив свою систему, завантажує файл cookie цілою картою відповідей, як-от із отриманням або публікацією. Лайно так: Set-Cookie: price = 1 Set-Cookie: status = Прийняти мені потрібен preg_match_all з '/ ^ Set-Cookie: (. *?) = (. *?) $ / Sm'
thirsty93

7
@ thirsty93 Curl не може подати вам картографічний масив. Але вам показаний спосіб її врятуватиcurl_setopt($ch, CURLOPT_HEADERFUNCTION, 'callback_SaveHeaders');
Шиплу Мокаддім

2
Залежно від поверненої структури файлів cookie, останній рядок, можливо, буде потрібно змінити на щось подібне parse_str($m[1], $cookies), що $cookies
введе

7
Для комбінованих виправлень, які захоплюють більше одного файлу cookie: preg_match_all('/^Set-Cookie:\s*([^;]*)/mi', $result, $matches); $cookies = array(); foreach($matches[1] as $item) { parse_str($item, $cookie); $cookies = array_merge($cookies, $cookie); }
Combuster

39

Хоча це запитання досить старе, і прийнята відповідь є дійсною, я вважаю це трохи незручним, оскільки вміст відповіді HTTP (HTML, XML, JSON, двійковий або інше) змішується з заголовками.

Я знайшов іншу альтернативу. CURL надає опцію (CURLOPT_HEADERFUNCTION ) встановити зворотний виклик, який буде викликаний для кожного рядка заголовка відповіді. Функція отримає об'єкт curl та рядок із рядком заголовка.

Ви можете використовувати такий код (адаптований з TML-відповіді):

$cookies = Array();
$ch = curl_init('http://www.google.com/');
// Ask for the callback.
curl_setopt($ch, CURLOPT_HEADERFUNCTION, "curlResponseHeaderCallback");
$result = curl_exec($ch);
var_dump($cookies);

function curlResponseHeaderCallback($ch, $headerLine) {
    global $cookies;
    if (preg_match('/^Set-Cookie:\s*([^;]*)/mi', $headerLine, $cookie) == 1)
        $cookies[] = $cookie;
    return strlen($headerLine); // Needed by curl
}

У цього рішення є недолік використання глобальної змінної, але я думаю, це не є проблемою для коротких сценаріїв. І ви завжди можете використовувати статичні методи та атрибути, якщо curl запускається в клас.


10
Замість глобального ви можете використовувати закриття з посиланням на $cookies. $curlResponseHeaderCallback = function ($ch, $headerLine) use (&$cookies) {то curl_setopt($ch, CURLOPT_HEADERFUNCTION, $curlResponseHeaderCallback);.
Seph

Що станеться, якщо у вас все це в класі? Як ви посилаєтесь на функцію класу $class->curlResponseHeaderCallback()? Або у вас просто curlResponseHeaderCallbackпоза класом?
Севенеарт

13

Це робить без повторних виразів, але вимагає розширення PECL HTTP .

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
$result = curl_exec($ch);
curl_close($ch);

$headers = http_parse_headers($result);
$cookobjs = Array();
foreach($headers AS $k => $v){
    if (strtolower($k)=="set-cookie"){
        foreach($v AS $k2 => $v2){
            $cookobjs[] = http_parse_cookie($v2);
        }
    }
}

$cookies = Array();
foreach($cookobjs AS $row){
    $cookies[] = $row->cookies;
}

$tmp = Array();
// sort k=>v format
foreach($cookies AS $v){
    foreach ($v  AS $k1 => $v1){
        $tmp[$k1]=$v1;
    }
}

$cookies = $tmp;
print_r($cookies);

2
Дякую за це Чітке, семантичне рішення варте клопоту встановити розширення.
Бен Джейкобс

2
Це було б найкращим рішенням, якби pecl installнасправді спрацювало. Grrr
Робін Уінслоу

11

Якщо ви використовуєте CURLOPT_COOKIE_FILE і CURLOPT_COOKIE_JAR curl буде читати / записувати файли cookie з / у файл. Після того, як з цим буде зроблено згортання, ви можете прочитати та / або змінити його, як тільки захочете.


12
Я думаю, що мета - не використовувати цей файл
Nicolas Thery

3

libcurl також надає CURLOPT_COOKIELIST, який витягує всі відомі файли cookie. Все, що вам потрібно, це переконатися, що прив'язка PHP / CURL може використовувати його.


12
Це не можна використовувати через PHP API.
Емре Язичі

1

хтось тут може вважати його корисним. hhb_curl_exec2 працює майже як curl_exec, але arg3 - це масив, який буде заповнений поверненими заголовками http (числовий індекс), а arg4 - масив, який буде заповнений поверненими файлами cookie ($ cookies ["закінчується"] => " Пт, 06 травня 2016 05:58:51 GMT "), а arg5 буде заповнено ... інформацією про необроблений запит, зроблений curl.

Недоліком є ​​те, що він вимагає включення CURLOPT_RETURNTRANSFER, в іншому випадку це помилка, і що він замінить CURLOPT_STDERR і CURLOPT_VERBOSE, якщо ви вже використовували їх для чогось іншого .. (я можу це виправити пізніше)

приклад того, як ним користуватися:

<?php
header("content-type: text/plain;charset=utf8");
$ch=curl_init();
$headers=array();
$cookies=array();
$debuginfo="";
$body="";
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
$body=hhb_curl_exec2($ch,'https://www.youtube.com/',$headers,$cookies,$debuginfo);
var_dump('$cookies:',$cookies,'$headers:',$headers,'$debuginfo:',$debuginfo,'$body:',$body);

і сама функція ..

function hhb_curl_exec2($ch, $url, &$returnHeaders = array(), &$returnCookies = array(), &$verboseDebugInfo = "")
{
    $returnHeaders    = array();
    $returnCookies    = array();
    $verboseDebugInfo = "";
    if (!is_resource($ch) || get_resource_type($ch) !== 'curl') {
        throw new InvalidArgumentException('$ch must be a curl handle!');
    }
    if (!is_string($url)) {
        throw new InvalidArgumentException('$url must be a string!');
    }
    $verbosefileh = tmpfile();
    $verbosefile  = stream_get_meta_data($verbosefileh);
    $verbosefile  = $verbosefile['uri'];
    curl_setopt($ch, CURLOPT_VERBOSE, 1);
    curl_setopt($ch, CURLOPT_STDERR, $verbosefileh);
    curl_setopt($ch, CURLOPT_HEADER, 1);
    $html             = hhb_curl_exec($ch, $url);
    $verboseDebugInfo = file_get_contents($verbosefile);
    curl_setopt($ch, CURLOPT_STDERR, NULL);
    fclose($verbosefileh);
    unset($verbosefile, $verbosefileh);
    $headers       = array();
    $crlf          = "\x0d\x0a";
    $thepos        = strpos($html, $crlf . $crlf, 0);
    $headersString = substr($html, 0, $thepos);
    $headerArr     = explode($crlf, $headersString);
    $returnHeaders = $headerArr;
    unset($headersString, $headerArr);
    $htmlBody = substr($html, $thepos + 4); //should work on utf8/ascii headers... utf32? not so sure..
    unset($html);
    //I REALLY HOPE THERE EXIST A BETTER WAY TO GET COOKIES.. good grief this looks ugly..
    //at least it's tested and seems to work perfectly...
    $grabCookieName = function($str)
    {
        $ret = "";
        $i   = 0;
        for ($i = 0; $i < strlen($str); ++$i) {
            if ($str[$i] === ' ') {
                continue;
            }
            if ($str[$i] === '=') {
                break;
            }
            $ret .= $str[$i];
        }
        return urldecode($ret);
    };
    foreach ($returnHeaders as $header) {
        //Set-Cookie: crlfcoookielol=crlf+is%0D%0A+and+newline+is+%0D%0A+and+semicolon+is%3B+and+not+sure+what+else
        /*Set-Cookie:ci_spill=a%3A4%3A%7Bs%3A10%3A%22session_id%22%3Bs%3A32%3A%22305d3d67b8016ca9661c3b032d4319df%22%3Bs%3A10%3A%22ip_address%22%3Bs%3A14%3A%2285.164.158.128%22%3Bs%3A10%3A%22user_agent%22%3Bs%3A109%3A%22Mozilla%2F5.0+%28Windows+NT+6.1%3B+WOW64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F43.0.2357.132+Safari%2F537.36%22%3Bs%3A13%3A%22last_activity%22%3Bi%3A1436874639%3B%7Dcab1dd09f4eca466660e8a767856d013; expires=Tue, 14-Jul-2015 13:50:39 GMT; path=/
        Set-Cookie: sessionToken=abc123; Expires=Wed, 09 Jun 2021 10:18:14 GMT;
        //Cookie names cannot contain any of the following '=,; \t\r\n\013\014'
        //
        */
        if (stripos($header, "Set-Cookie:") !== 0) {
            continue;
            /**/
        }
        $header = trim(substr($header, strlen("Set-Cookie:")));
        while (strlen($header) > 0) {
            $cookiename                 = $grabCookieName($header);
            $returnCookies[$cookiename] = '';
            $header                     = substr($header, strlen($cookiename) + 1); //also remove the = 
            if (strlen($header) < 1) {
                break;
            }
            ;
            $thepos = strpos($header, ';');
            if ($thepos === false) { //last cookie in this Set-Cookie.
                $returnCookies[$cookiename] = urldecode($header);
                break;
            }
            $returnCookies[$cookiename] = urldecode(substr($header, 0, $thepos));
            $header                     = trim(substr($header, $thepos + 1)); //also remove the ;
        }
    }
    unset($header, $cookiename, $thepos);
    return $htmlBody;
}

function hhb_curl_exec($ch, $url)
{
    static $hhb_curl_domainCache = "";
    //$hhb_curl_domainCache=&$this->hhb_curl_domainCache;
    //$ch=&$this->curlh;
    if (!is_resource($ch) || get_resource_type($ch) !== 'curl') {
        throw new InvalidArgumentException('$ch must be a curl handle!');
    }
    if (!is_string($url)) {
        throw new InvalidArgumentException('$url must be a string!');
    }

    $tmpvar = "";
    if (parse_url($url, PHP_URL_HOST) === null) {
        if (substr($url, 0, 1) !== '/') {
            $url = $hhb_curl_domainCache . '/' . $url;
        } else {
            $url = $hhb_curl_domainCache . $url;
        }
    }
    ;

    curl_setopt($ch, CURLOPT_URL, $url);
    $html = curl_exec($ch);
    if (curl_errno($ch)) {
        throw new Exception('Curl error (curl_errno=' . curl_errno($ch) . ') on url ' . var_export($url, true) . ': ' . curl_error($ch));
        // echo 'Curl error: ' . curl_error($ch);
    }
    if ($html === '' && 203 != ($tmpvar = curl_getinfo($ch, CURLINFO_HTTP_CODE)) /*203 is "success, but no output"..*/ ) {
        throw new Exception('Curl returned nothing for ' . var_export($url, true) . ' but HTTP_RESPONSE_CODE was ' . var_export($tmpvar, true));
    }
    ;
    //remember that curl (usually) auto-follows the "Location: " http redirects..
    $hhb_curl_domainCache = parse_url(curl_getinfo($ch, CURLINFO_EFFECTIVE_URL), PHP_URL_HOST);
    return $html;
}

1

Прийнята відповідь, схоже, буде шукати все повідомлення відповіді. Це може призвести до помилкових збігів заголовків файлів cookie, якщо слово "Set-Cookie" знаходиться на початку рядка. Хоча це повинно бути добре в більшості випадків. Більш безпечним способом може бути прочитання повідомлення від початку до першого порожнього рядка, який вказує кінець заголовків повідомлення. Це лише альтернативне рішення, яке повинно шукати перший порожній рядок, а потім використовувати preg_grep у цих рядках лише для того, щоб знайти "Set-Cookie".

    curl_setopt($ch, CURLOPT_HEADER, 1);
    //Return everything
    $res = curl_exec($ch);
    //Split into lines
    $lines = explode("\n", $res);
    $headers = array();
    $body = "";
    foreach($lines as $num => $line){
        $l = str_replace("\r", "", $line);
        //Empty line indicates the start of the message body and end of headers
        if(trim($l) == ""){
            $headers = array_slice($lines, 0, $num);
            $body = $lines[$num + 1];
            //Pull only cookies out of the headers
            $cookies = preg_grep('/^Set-Cookie:/', $headers);
            break;
        }
    }

1
Прийнята відповідь, схоже, буде шукати все повідомлення відповіді. Це може призвести до помилкових збігів заголовків файлів cookie, якщо слово "Set-Cookie" знаходиться на початку рядка. Хоча це повинно бути добре в більшості випадків. Більш безпечним способом може бути прочитання повідомлення від початку до першого порожнього рядка, який вказує кінець заголовків повідомлення. Це лише альтернативне рішення, яке повинно шукати перший порожній рядок, а потім використовувати preg_grep у цих рядках лише для того, щоб знайти "Set-Cookie".
Rich Wandell

0

Я розумію, що файли cookie curlповинні бути записані у файл ( curl -c cookie_file). Якщо ви працюєте curlчерез PHP execабо systemфункції (або що-небудь в цій родині), ви повинні мати можливість зберегти файли cookie у файл, а потім відкрити файл і прочитати їх.


4
Він майже напевно посилається на php.net/curl :)
TML
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.