Подтвердить что ты не робот

Как получить окончательный URL-адрес после перенаправления HTTP в чистом PHP?

То, что я хотел бы сделать, это узнать, что является последним/окончательным URL после следующих перенаправления.

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

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

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

4b9b3361

Ответ 1

/**
 * get_redirect_url()
 * Gets the address that the provided URL redirects to,
 * or FALSE if there 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/

Ответ 2

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;
}

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

Как уже упоминалось в комментарии, конечный элемент в $headers['Location'] будет вашим окончательным URL после всех перенаправлений. Важно отметить, однако, что он не всегда будет массивом. Иногда это просто переменная run-of-the-mill, non-array. В этом случае попытка доступа к последнему элементу массива скорее всего вернет один символ. Не идеально.

Если вас интересует только конечный 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-адрес для валидности или включить проверку перенаправления в вашу проверку.

Ответ 3

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

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

    $request. = "User-Agent: Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; 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 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;
}

Ответ 4

В то время как OP хотел избежать 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;
}

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

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.