如何使用 cURL 找到我将被重定向的位置?

Posted

技术标签:

【中文标题】如何使用 cURL 找到我将被重定向的位置?【英文标题】:How can I find where I will be redirected using cURL in PHP? 【发布时间】:2011-04-01 00:01:37 【问题描述】:

我正在尝试让 curl 跟随重定向,但我无法让它正常工作。我有一个字符串,我想将其作为 GET 参数发送到服务器并获取结果 URL。

例子:

String = 狗头人害虫 网址 = www.wowhead.com/search?q=Kobold+Worker

如果您访问该网址,它会将您重定向到“www.wowhead.com/npc=257”。我希望 curl 将此 URL 返回到我的 php 代码,以便我可以提取“npc=257”并使用它。

当前代码:

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

然而,这会返回 www.wowhead.com/search?q=Kobold+Worker 而不是 www.wowhead.com/npc=257

我怀疑 PHP 在外部重定向发生之前返回。我该如何解决这个问题?

【问题讨论】:

这是“curl follow redirects”的热门问题之一。要使用curl 命令自动跟踪重定向,请传递-L--location 标志。例如。 curl -L http://example.com/ 【参考方案1】:

要使 cURL 跟随重定向,请使用:

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

Erm...我认为您实际上并没有执行 curl...尝试:

curl_exec($ch);

...设置选项之后,curl_getinfo() 调用之前。

编辑:如果您只想找出页面重定向到的位置,我会使用建议 here,然后使用 Curl 获取标题并从中提取 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]);

【讨论】:

这使得 php 跟随重定向。我不想关注重定向,我只想知道重定向页面的 url。 哦,所以您实际上并不想获取该页面?只知道位置?在这种情况下,我建议这里使用的策略:zzz.rezo.net/HowTo-Expand-Short-URLs.html - 基本上只是从重定向页面中获取标题,然后从中获取 Location: 标题。不管怎样,你仍然需要为 Curl 执行 exec() 来真正任何事情...... 我建议看看下面的 Luca Camillos 解决方案,因为这个解决方案没有考虑多个重定向。 此解决方案在同一网址中打开新网页。我还想更改 url 并将参数发布到该 url。我怎样才能做到这一点? @MattGibson 当我使用 $httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);将 CURLOPT_FOLLOWLOCATION 设置为 true 时,httpcode 将是什么。我的意思是它是用于第一个 url 还是用于重定向 url【参考方案2】:

上面的答案在我的一台服务器上对我不起作用,这与 basedir 有关,所以我重新散列了一点。下面的代码适用于我所有的服务器。

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

【讨论】:

Location: 标头并不总是跟随重定向。另请参阅明确说明的问题:curl follow location error【参考方案3】:

这里选择的答案是不错的,但它区分大小写,不能防止相对的 location: 标题(某些网站会这样做)或可能实际上在其内容中包含短语 Location: 的页面......(zillow目前有)。

有点草率,但为了让这更智能一些快速编辑是:

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;

请注意,这仍然只进行 1 次重定向。要更深入,您实际上需要获取内容并遵循重定向。

【讨论】:

你能提供获取最终网址的代码吗?【参考方案4】:

有时您需要获取 HTTP 标头,但同时又不想返回这些标头。**

这个框架使用递归处理 cookie 和 HTTP 重定向。这里的主要思想是避免将 HTTP 标头返回到客户端代码。

你可以在它之上构建一个非常强大的 curl 类。添加 POST 功能等。

<?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]);
  


【讨论】:

【参考方案5】:

你可以使用:

$redirectURL = curl_getinfo($ch,CURLINFO_REDIRECT_URL);

【讨论】:

+1。 CURLINFO_REDIRECT_URL 对我有用,但 CURLINFO_EFFECTIVE_URL 不是。 This 就是原因。【参考方案6】:

将此行添加到 curl 初始化

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

在 curl_close 之前使用 getinfo

$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_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);

【讨论】:

我认为这是更好的解决方案,因为它还展开了多个重定向。 记住:(ok,duh)重定向后不会重新提交 POST 数据。就我而言,这发生了,后来我觉得很愚蠢,因为:只需使用适当的 url 就可以了。 使用curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 是一个安全漏洞。它本质上说“如果 SSL 错误被破坏,请忽略它 - 就像信任未加密的 URL 一样。”。【参考方案7】:

这里有很多正则表达式,尽管我真的很喜欢这种方式,但对我来说可能更稳定:

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

位置部分是 apache 发送的 HTML 中的链接。所以 Xpath 非常适合恢复它。

【讨论】:

以上是关于如何使用 cURL 找到我将被重定向的位置?的主要内容,如果未能解决你的问题,请参考以下文章

如何根据点击的目标使用 navigateToURL 将用户发送到不同的 URL?

新注册用户将被重定向到密码重置屏幕

注销重定向到管理仪表板 laravel

即使位置 url 被重定向,如何连续执行 web worker 任务?

如何显示拒绝访问而不是重定向到登录页面?

地理位置重定向到不同域的同一页面?