Comment puis-je trouver où je serai redirigé en utilisant cURL?

149

J'essaie de faire en sorte que curl suive une redirection mais je n'arrive pas à le faire fonctionner correctement. J'ai une chaîne que je veux envoyer en tant que paramètre GET à un serveur et obtenir l'URL résultante.

Exemple:

Chaîne = URL de la vermine de Kobold
= www.wowhead.com/search?q=Kobold+Worker

Si vous accédez à cette URL, il vous redirigera vers "www.wowhead.com/npc=257". Je veux que curl renvoie cette URL à mon code PHP afin que je puisse extraire le "npc = 257" et l'utiliser.

Code actuel:

function npcID($name) {
    $urltopost = "http://www.wowhead.com/search?q=" . $name;
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1");
    curl_setopt($ch, CURLOPT_URL, $urltopost);
    curl_setopt($ch, CURLOPT_REFERER, "http://www.wowhead.com");
    curl_setopt($ch, CURLOPT_HTTPHEADER, Array("Content-Type:application/x-www-form-urlencoded"));
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
    return curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
}

Cela renvoie cependant www.wowhead.com/search?q=Kobold+Worker et non www.wowhead.com/npc=257 .

Je soupçonne que PHP est de retour avant que la redirection externe ne se produise. Comment puis-je réparer cela?

Thomas Van Nuffel
la source
8
C'est l'une des principales questions pour "Curl Follow redirects". Pour suivre automatiquement les redirections à l'aide de la curlcommande, passez l' indicateur -Lou --location. Par exemplecurl -L http://example.com/
Rob W

Réponses:

256

Pour que cURL suive une redirection, utilisez:

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

Euh ... Je ne pense pas que vous exécutez réellement la boucle ... Essayez:

curl_exec($ch);

... après avoir défini les options et avant l' curl_getinfo()appel.

EDIT: Si vous voulez juste savoir vers où une page redirige, j'utiliserais les conseils ici , et utiliserais simplement Curl pour récupérer les en-têtes et en extraire l'en-tête Location::

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
if (preg_match('~Location: (.*)~i', $result, $match)) {
   $location = trim($match[1]);
}
Matt Gibson
la source
2
Cela oblige php à suivre la redirection. Je ne veux pas suivre la redirection, je veux juste connaître l'url de la page redirigée.
Thomas Van Nuffel
9
Oh, donc vous ne voulez pas vraiment aller chercher la page? Trouver juste l'emplacement? Dans ce cas, je suggérerais la tactique utilisée ici: zzz.rezo.net/HowTo-Expand-Short-URLs.html - il suffit de saisir l'en-tête de la page qui redirige et de saisir l'en-tête Location :. Quoi qu'il en soit, cependant, vous devez toujours faire l'exec () pour que Curl fasse quoi que ce soit ...
Matt Gibson
1
Je suggère de jeter un œil à la solution Luca Camillos ci-dessous, car cette solution ne prend pas en compte les redirections multiples.
Christian Engel
cette solution ouvre la nouvelle page Web dans la même URL. Je souhaite également modifier l'URL et publier les paramètres dans cette URL. Comment puis-je y parvenir?
amanpurohit
@MattGibson lorsque j'utilise $ httpCode = curl_getinfo ($ handle, CURLINFO_HTTP_CODE); avec CURLOPT_FOLLOWLOCATION défini sur true quel sera le httpcode. Je veux dire que ce sera pour la première URL ou pour l'URL de redirection
Manigandan Arjunan
27

Ajouter cette ligne pour curl initialisation

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

et utilisez getinfo avant curl_close

$redirectURL = curl_getinfo($ch,CURLINFO_EFFECTIVE_URL );

es:

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_USERAGENT,'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT ,0); 
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
$html = curl_exec($ch);
$redirectURL = curl_getinfo($ch,CURLINFO_EFFECTIVE_URL );
curl_close($ch);
Luca Camillo
la source
2
Je pense que celui-ci est la meilleure solution, car il déploie également plusieurs redirections.
Christian Engel
N'oubliez pas: (ok, duh) les données POST ne seront pas soumises à nouveau après une redirection. Dans mon cas, cela s'est produit et je me suis senti stupide par la suite parce que: utilisez simplement l'url appropriée et c'est corrigé.
deux fois
L'utilisation curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);est une faille de sécurité. Il dit essentiellement «Ignorez les erreurs SSL si elles sont brisées - faites confiance comme vous le feriez pour une URL non chiffrée».
Finesse
8

La réponse ci-dessus n'a pas fonctionné pour moi sur l'un de mes serveurs, quelque chose à avec basedir, donc je l'ai un peu hachée. Le code ci-dessous fonctionne sur tous mes serveurs.

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$a = curl_exec($ch);
curl_close( $ch ); 
// the returned headers
$headers = explode("\n",$a);
// if there is no redirection this will be the final url
$redir = $url;
// loop through the headers and check for a Location: str
$j = count($headers);
for($i = 0; $i < $j; $i++){
// if we find the Location header strip it and fill the redir var       
if(strpos($headers[$i],"Location:") !== false){
        $redir = trim(str_replace("Location:","",$headers[$i]));
        break;
    }
}
// do whatever you want with the result
echo redir;
GR1NN3R
la source
L'en- Location: tête n'est pas toujours de suivre une redirection. Veuillez également consulter une question qui concerne explicitement ceci: erreur de localisation de suivi de curl
hakre
5

La réponse choisie ici est décente mais sa sensibilité à la casse ne protège pas contre les en- location:têtes relatifs (ce que font certains sites) ou les pages qui pourraient en fait avoir la phrase Location:dans leur contenu ... (ce que zillow fait actuellement).

Un peu bâclé, mais quelques modifications rapides pour rendre cela un peu plus intelligent sont:

function getOriginalURL($url) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    $result = curl_exec($ch);
    $httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    // if it's not a redirection (3XX), move along
    if ($httpStatus < 300 || $httpStatus >= 400)
        return $url;

    // look for a location: header to find the target URL
    if(preg_match('/location: (.*)/i', $result, $r)) {
        $location = trim($r[1]);

        // if the location is a relative URL, attempt to make it absolute
        if (preg_match('/^\/(.*)/', $location)) {
            $urlParts = parse_url($url);
            if ($urlParts['scheme'])
                $baseURL = $urlParts['scheme'].'://';

            if ($urlParts['host'])
                $baseURL .= $urlParts['host'];

            if ($urlParts['port'])
                $baseURL .= ':'.$urlParts['port'];

            return $baseURL.$location;
        }

        return $location;
    }
    return $url;
}

Notez que cela ne va toujours qu'une redirection profonde. Pour aller plus loin, vous devez en fait obtenir le contenu et suivre les redirections.

Broox
la source
5

Parfois, vous devez obtenir des en-têtes HTTP, mais en même temps, vous ne voulez pas renvoyer ces en-têtes. **

Ce squelette prend en charge les cookies et les redirections HTTP en utilisant la récursivité. L'idée principale ici est d'éviter de renvoyer les en-têtes HTTP au code client.

Vous pouvez créer une classe curl très forte dessus. Ajouter la fonctionnalité POST, etc.

<?php

class curl {

  static private $cookie_file            = '';
  static private $user_agent             = '';  
  static private $max_redirects          = 10;  
  static private $followlocation_allowed = true;

  function __construct()
  {
    // set a file to store cookies
    self::$cookie_file = 'cookies.txt';

    // set some general User Agent
    self::$user_agent = 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)';

    if ( ! file_exists(self::$cookie_file) || ! is_writable(self::$cookie_file))
    {
      throw new Exception('Cookie file missing or not writable.');
    }

    // check for PHP settings that unfits
    // correct functioning of CURLOPT_FOLLOWLOCATION 
    if (ini_get('open_basedir') != '' || ini_get('safe_mode') == 'On')
    {
      self::$followlocation_allowed = false;
    }    
  }

  /**
   * Main method for GET requests
   * @param  string $url URI to get
   * @return string      request's body
   */
  static public function get($url)
  {
    $process = curl_init($url);    

    self::_set_basic_options($process);

    // this function is in charge of output request's body
    // so DO NOT include HTTP headers
    curl_setopt($process, CURLOPT_HEADER, 0);

    if (self::$followlocation_allowed)
    {
      // if PHP settings allow it use AUTOMATIC REDIRECTION
      curl_setopt($process, CURLOPT_FOLLOWLOCATION, true);
      curl_setopt($process, CURLOPT_MAXREDIRS, self::$max_redirects); 
    }
    else
    {
      curl_setopt($process, CURLOPT_FOLLOWLOCATION, false);
    }

    $return = curl_exec($process);

    if ($return === false)
    {
      throw new Exception('Curl error: ' . curl_error($process));
    }

    // test for redirection HTTP codes
    $code = curl_getinfo($process, CURLINFO_HTTP_CODE);
    if ($code == 301 || $code == 302)
    {
      curl_close($process);

      try
      {
        // go to extract new Location URI
        $location = self::_parse_redirection_header($url);
      }
      catch (Exception $e)
      {
        throw $e;
      }

      // IMPORTANT return 
      return self::get($location);
    }

    curl_close($process);

    return $return;
  }

  static function _set_basic_options($process)
  {

    curl_setopt($process, CURLOPT_USERAGENT, self::$user_agent);
    curl_setopt($process, CURLOPT_COOKIEFILE, self::$cookie_file);
    curl_setopt($process, CURLOPT_COOKIEJAR, self::$cookie_file);
    curl_setopt($process, CURLOPT_RETURNTRANSFER, 1);
    // curl_setopt($process, CURLOPT_VERBOSE, 1);
    // curl_setopt($process, CURLOPT_SSL_VERIFYHOST, false);
    // curl_setopt($process, CURLOPT_SSL_VERIFYPEER, false);
  }

  static function _parse_redirection_header($url)
  {
    $process = curl_init($url);    

    self::_set_basic_options($process);

    // NOW we need to parse HTTP headers
    curl_setopt($process, CURLOPT_HEADER, 1);

    $return = curl_exec($process);

    if ($return === false)
    {
      throw new Exception('Curl error: ' . curl_error($process));
    }

    curl_close($process);

    if ( ! preg_match('#Location: (.*)#', $return, $location))
    {
      throw new Exception('No Location found');
    }

    if (self::$max_redirects-- <= 0)
    {
      throw new Exception('Max redirections reached trying to get: ' . $url);
    }

    return trim($location[1]);
  }

}
Igor Parra
la source
0

Il y a beaucoup de regex ici, malgré le fait que je les aime vraiment de cette façon, cela pourrait être plus stable pour moi:

$resultCurl=curl_exec($curl); //get curl result
//Optional line if you want to store the http status code
$headerHttpCode=curl_getinfo($curl,CURLINFO_HTTP_CODE);

//let's use dom and xpath
$dom = new \DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML($resultCurl, LIBXML_HTML_NODEFDTD);
libxml_use_internal_errors(false);
$xpath = new \DOMXPath($dom);
$head=$xpath->query("/html/body/p/a/@href");

$newUrl=$head[0]->nodeValue;

La partie emplacement est un lien dans le HTML envoyé par apache. Donc Xpath est parfait pour le récupérer.

Patrick Valibus
la source
-1

Vous pouvez utiliser:

$redirectURL = curl_getinfo($ch,CURLINFO_REDIRECT_URL);
Abhilash Nayak
la source