Как получить окончательный URL после следующих перенаправлений HTTP в чистом PHP? - PullRequest
15 голосов
/ 26 сентября 2010

Что я хотел бы сделать, это выяснить, что является последним / последним URL после следования перенаправлениям .

Я бы предпочел не использовать cURL. Я хотел бы придерживаться чистого PHP (обертки потока).

Прямо сейчас у меня есть URL (скажем, http://domain.test),, и я использую get_headers () для получения определенных заголовков с этой страницы. Get_headers также возвратит несколько Location: заголовков (см. Edit ниже.) Есть ли способ использовать эти заголовки для создания окончательного URL-адреса? Или есть функция PHP, которая автоматически сделает это?

Редактировать: get_headers () следует за перенаправлениями и возвращает все заголовки для каждого ответа / перенаправления, поэтому у меня есть все Location: заголовки.

Ответы [ 4 ]

41 голосов
/ 26 сентября 2011
function getRedirectUrl ($url) {
    stream_context_set_default(array(
        'http' => array(
            'method' => 'HEAD'
        )
    ));
    $headers = get_headers($url, 1);
    if ($headers !== false && isset($headers['Location'])) {
        return $headers['Location'];
    }
    return false;
}

Дополнительно ...

Как упоминалось в комментарии, элемент final в $headers['Location'] будет вашим окончательным URL после всех перенаправлений. Однако важно отметить, что он не будет всегда массивом. Иногда это обычная переменная, не являющаяся массивом. В этом случае попытка доступа к последнему элементу массива, скорее всего, вернет один символ. Не идеально.

Если вас интересует только окончательный URL-адрес, после всех переадресаций я бы предложил изменить

return $headers['Location'];

до

return is_array($headers['Location']) ? array_pop($headers['Location']) : $headers['Location'];

... что просто если короткая комбинация для

if(is_array($headers['Location'])){
     return array_pop($headers['Location']);
}else{
     return $headers['Location'];
}

Это исправление позаботится о любом случае (массив, не массив) и избавит от необходимости отсеивать окончательный URL после вызова функции.

В случае отсутствия перенаправлений функция вернет false. Аналогично, функция также вернет false для недействительных URL-адресов (недействительных по любой причине). Поэтому очень важно проверить URL-адрес на достоверность до запуска этой функции или включить проверку перенаправления где-нибудь в вашу проверку.

29 голосов
/ 05 ноября 2010
/**
 * get_redirect_url()
 * Gets the address that the provided URL redirects to,
 * or FALSE if there's no redirect. 
 *
 * @param string $url
 * @return string
 */
function get_redirect_url($url){
    $redirect_url = null; 

    $url_parts = @parse_url($url);
    if (!$url_parts) return false;
    if (!isset($url_parts['host'])) return false; //can't process relative URLs
    if (!isset($url_parts['path'])) $url_parts['path'] = '/';

    $sock = fsockopen($url_parts['host'], (isset($url_parts['port']) ? (int)$url_parts['port'] : 80), $errno, $errstr, 30);
    if (!$sock) return false;

    $request = "HEAD " . $url_parts['path'] . (isset($url_parts['query']) ? '?'.$url_parts['query'] : '') . " HTTP/1.1\r\n"; 
    $request .= 'Host: ' . $url_parts['host'] . "\r\n"; 
    $request .= "Connection: Close\r\n\r\n"; 
    fwrite($sock, $request);
    $response = '';
    while(!feof($sock)) $response .= fread($sock, 8192);
    fclose($sock);

    if (preg_match('/^Location: (.+?)$/m', $response, $matches)){
        if ( substr($matches[1], 0, 1) == "/" )
            return $url_parts['scheme'] . "://" . $url_parts['host'] . trim($matches[1]);
        else
            return trim($matches[1]);

    } else {
        return false;
    }

}

/**
 * get_all_redirects()
 * Follows and collects all redirects, in order, for the given URL. 
 *
 * @param string $url
 * @return array
 */
function get_all_redirects($url){
    $redirects = array();
    while ($newurl = get_redirect_url($url)){
        if (in_array($newurl, $redirects)){
            break;
        }
        $redirects[] = $newurl;
        $url = $newurl;
    }
    return $redirects;
}

/**
 * get_final_url()
 * Gets the address that the URL ultimately leads to. 
 * Returns $url itself if it isn't a redirect.
 *
 * @param string $url
 * @return string
 */
function get_final_url($url){
    $redirects = get_all_redirects($url);
    if (count($redirects)>0){
        return array_pop($redirects);
    } else {
        return $url;
    }
}

И, как всегда, отдайте должное:

http://w -shadow.com / blog / 2008/07/05 / how-to-get-redirect-url-in-PHP /

3 голосов
/ 21 февраля 2017

Хотя ОП хотел избежать cURL, лучше использовать его, когда он будет доступен.Вот решение, которое имеет следующие преимущества:

  • использует curl для всей тяжелой работы, поэтому работает с https
  • и работает с серверами, которые возвращают имя заголовка в нижнем регистре location (оба xaavи ответы Webjay не справляются с этим)
  • позволяет вам контролировать, насколько глубоко вы хотите пойти, прежде чем отказаться от

Вот функция:

function findUltimateDestination($url, $maxRequests = 10)
{
    $ch = curl_init();

    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_NOBODY, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_MAXREDIRS, $maxRequests);
    curl_setopt($ch, CURLOPT_TIMEOUT, 15);

    //customize user agent if you desire...
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Link Checker)');

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_exec($ch);

    $url=curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);

    curl_close ($ch);
    return $url;
}

Вотболее подробная версия, которая позволяет вам проверять цепочку перенаправления, а не позволять curl следовать за ней.

function findUltimateDestination($url, $maxRequests = 10)
{
    $ch = curl_init();

    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_NOBODY, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 15);

    //customize user agent if you desire...
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Link Checker)');

    while ($maxRequests--) {

        //fetch
        curl_setopt($ch, CURLOPT_URL, $url);
        $response = curl_exec($ch);

        //try to determine redirection url
        $location = '';
        if (in_array(curl_getinfo($ch, CURLINFO_HTTP_CODE), [301, 302, 303, 307, 308])) {
            if (preg_match('/Location:(.*)/i', $response, $match)) {
                $location = trim($match[1]);
            }
        }

        if (empty($location)) {
            //we've reached the end of the chain...
            return $url;
        }

        //build next url
        if ($location[0] == '/') {
            $u = parse_url($url);
            $url = $u['scheme'] . '://' . $u['host'];
            if (isset($u['port'])) {
                $url .= ':' . $u['port'];
            }
            $url .= $location;
        } else {
            $url = $location;
        }
    }

    return null;
}

В качестве примера цепочки перенаправления, которую обрабатывает эта функция, а другие нет, попробуйте следующее:

echo findUltimateDestination('http://dx.doi.org/10.1016/j.infsof.2016.05.005')

На момент написания, это включает в себя 4 запроса, включающих в себя смесь Location и location заголовков.

2 голосов
/ 30 ноября 2014

xaav ответ очень хороший; за исключением следующих двух вопросов:

  • Он не поддерживает протокол HTTPS => Решение было предложено в качестве комментария на оригинальном сайте: http://w -shadow.com / blog / 2008/07/05 / how-to-get-redirect- URL-в-PHP /
  • Некоторые сайты не будут работать, так как они не будут распознавать базовый пользовательский агент (клиентский браузер) => Это просто исправляется добавлением поля заголовка User-agent: я добавил пользовательский агент Android (вы можете найти здесь http://www.useragentstring.com/pages/useragentstring.php другие примеры пользовательских агентов в соответствии с вашими потребностями):

    $ request. = "Пользовательский агент: Mozilla / 5.0 (Linux; U; Android 4.0.3; ко-кр; LG-L160L Build / IML74K) AppleWebkit / 534.30 (KHTML, как Gecko) Версия / 4.0 Mobile Safari /534.30\r\n";

Вот модифицированный ответ:

/**
 * get_redirect_url()
 * Gets the address that the provided URL redirects to,
 * or FALSE if there's no redirect. 
 *
 * @param string $url
 * @return string
 */
function get_redirect_url($url){
    $redirect_url = null; 

    $url_parts = @parse_url($url);
    if (!$url_parts) return false;
    if (!isset($url_parts['host'])) return false; //can't process relative URLs
    if (!isset($url_parts['path'])) $url_parts['path'] = '/';

    $sock = fsockopen($url_parts['host'], (isset($url_parts['port']) ? (int)$url_parts['port'] : 80), $errno, $errstr, 30);
    if (!$sock) return false;

    $request = "HEAD " . $url_parts['path'] . (isset($url_parts['query']) ? '?'.$url_parts['query'] : '') . " HTTP/1.1\r\n"; 
    $request .= 'Host: ' . $url_parts['host'] . "\r\n"; 
    $request .= "User-Agent: Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30\r\n";
    $request .= "Connection: Close\r\n\r\n"; 
    fwrite($sock, $request);
    $response = '';
    while(!feof($sock)) $response .= fread($sock, 8192);
    fclose($sock);

    if (preg_match('/^Location: (.+?)$/m', $response, $matches)){
        if ( substr($matches[1], 0, 1) == "/" )
            return $url_parts['scheme'] . "://" . $url_parts['host'] . trim($matches[1]);
        else
            return trim($matches[1]);

    } else {
        return false;
    }

}

/**
 * get_all_redirects()
 * Follows and collects all redirects, in order, for the given URL. 
 *
 * @param string $url
 * @return array
 */
function get_all_redirects($url){
    $redirects = array();
    while ($newurl = get_redirect_url($url)){
        if (in_array($newurl, $redirects)){
            break;
        }
        $redirects[] = $newurl;
        $url = $newurl;
    }
    return $redirects;
}

/**
 * get_final_url()
 * Gets the address that the URL ultimately leads to. 
 * Returns $url itself if it isn't a redirect.
 *
 * @param string $url
 * @return string
 */
function get_final_url($url){
    $redirects = get_all_redirects($url);
    if (count($redirects)>0){
        return array_pop($redirects);
    } else {
        return $url;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...