2010-09-26 38 views
12

Lo que me gustaría hacer es averiguar cuál es la URL última/final después de seguir las redirecciones.¿Cómo obtener la URL final después de seguir las redirecciones HTTP en PHP puro?

Preferiría no utilizar cURL. Me gustaría seguir con PHP puro (envoltorios de flujo).

Ahora tengo una URL (digamos http://domain.test), y uso get_headers() para obtener encabezados específicos de esa página. get_headers también devolverá múltiples encabezados Location: (consulte Editar a continuación). ¿Hay alguna manera de usar esos encabezados para construir la URL final? o hay una función PHP que automáticamente haría esto?

Editar: get_headers() de la siguiente manera redirecciones y devuelve todas las cabeceras para cada respuesta/redirecciones, así que tener todas las Location: cabeceras.

+1

Hay * múltiples * 'Ubicación:' encabezados en una sola respuesta? – Tomalak

+0

get_headers sigue las redirecciones automáticamente por defualt, así que obtengo múltiples encabezados 'Location:'. Lo que me gustaría tener es la URL final completa (http: //domain.test/final/page.ext? Attr ...) – Weboide

+0

No entendí la pregunta :( – Stewie

Respuesta

25
/** 
* 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; 
    } 
} 

Y, como siempre, dar crédito:

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

+0

Usted señor, me salvó unas pocas horas de búsqueda. Todo funciona como se espera – Dave

+0

Tengo que decir que para mis pruebas esta solución con CURL es más confiable: http://stackoverflow.com/questions/17472329/php-get-url-of-redirect-from-source-url –

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

Además ...

Como se mencionó en un comentario, la última el artículo en $headers['Location'] será b e su URL final después de todas las redirecciones. Sin embargo, es importante tener en cuenta que no será siempre sea una matriz. Algunas veces es solo una variable no arreglada y sin arreglos. En este caso, tratar de acceder al último elemento de matriz probablemente devuelva un solo carácter. No es ideal.

Si sólo está interesado en la URL final, después de todas las redirecciones, sugeriría cambiar

return $headers['Location']; 

a

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

... que es sólo para if short-hand

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

Esta corrección se ocupará de cualquier caso (matriz, no matriz) y eliminará la necesidad de editó la URL final después de llamar a la función.

En caso de que no haya redireccionamientos, la función devolverá false. Del mismo modo, la función también devolverá false para URL inválidas (inválidas por cualquier razón). Por lo tanto, es importante check the URL for validityantes de ejecutar esta función, o bien incorporar el control de redirección en algún lugar de su validación.

+0

¿Esto sigue? todas las redirecciones y devuelve la URL final? – Weboide

+1

¡Impresionante! Esto merece más votos favorables. – Ashfame

+1

¡Funciona muy bien! +1 – user327843

3

xaav respuesta es muy buena; a excepción de las dos cuestiones siguientes:

  • No es compatible con el protocolo HTTPS => La solución se propone como un comentario en el sitio original: http://w-shadow.com/blog/2008/07/05/how-to-get-redirect-url-in-php/
  • Algunos sitios no funcionarán, ya que no reconocerán el subyacente agente de usuario (navegador del cliente) => Esto es más que fijo mediante la adición de un campo de cabecera user-Agent: he añadido un agente de usuario de Android (se puede encontrar aquí http://www.useragentstring.com/pages/useragentstring.php otros ejemplos de agente de usuario que según su necesidad): solicitud

    $ . = "User-Agent: Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML7 4K) AppleWebkit/534.30 (KHTML, como Gecko) Versión/4.0 Mobile Safari/534.30 \ r \ n ";

Aquí está la respuesta modificada:

/** 
* 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; 
} 
+0

Error 500 ejecutando este script. –

+0

¿Puede proporcionar el mensaje de error? –

2

Mientras que el PO quería evitar cURL, lo mejor es usarlo cuando esté disponible. He aquí una solución que tiene las siguientes ventajas

  • utiliza rizo para todo el trabajo pesado, por lo que trabaja con https
  • hace frente a los servidores que devuelven entubado inferior nombre de encabezado location (tanto xaav y respuestas de webjay No manipular este)
  • le permite controlar la profundidad que desea que vaya antes de renunciar a

Aquí está la función:

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

Aquí hay una versión más detallada que le permite inspeccionar la cadena de redirección en lugar de dejar que curl la siga.

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

Como un ejemplo de cadena de redirección que se encarga de esta función, pero los otros no, intenta esto:

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

En el momento de la escritura, esto implica 4 solicitudes, con una mezcla de Location y location encabezados involucrados.

Cuestiones relacionadas