Как получить окончательный URL после следующих http-перенаправлений в чистом PHP?
то, что я хотел бы сделать, это узнайте, что является последним / окончательным URL-адресом после перенаправления.
Я бы предпочел не использовать cURL. Я хотел бы придерживаться чистого PHP (stream wrappers).
прямо сейчас у меня есть URL (скажем http://domain.тест), и я использую get_headers () для получения определенных заголовков с этой страницы. get_headers также вернет несколько Location:
заголовки (см. редактировать ниже.) Есть ли способ использовать эти заголовки для создания окончательного URL-адреса? или есть функция PHP, которая автоматически сделает это?
Edit: get_headers () следует за перенаправлениями и возвращает все заголовки для каждого ответа/перенаправления, поэтому у меня есть все Location:
заголовки.
4 ответов
/**
* 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/
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 после всех перенаправлений. Важно отметить, однако, что это не всегда быть массивом. Иногда это просто заурядная переменная без массива. В этом случае попытка доступа к последнему элементу массива, скорее всего, вернет один символ. Не идеальный.
если вы только интересует конечный URL, после всех переадресаций, я бы предложил изменить
return $headers['Location'];
to
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 на действительность до запуск этой функции или включение проверки перенаправления где-то в вашу проверку.
xaav ответ очень хороший, за исключением следующих двух вопросов:
- он не поддерживает протокол HTTPS => решение было предложено в качестве комментария на исходном сайте:http://w-shadow.com/blog/2008/07/05/how-to-get-redirect-url-in-php/
-
некоторые сайты не будут работать, так как они не распознают базовый агент пользователя (браузер клиента) => Это просто исправлено путем добавления поля заголовка User-agent: я добавил Android User agent (вы можете найти здесь http://www.useragentstring.com/pages/useragentstring.php другие примеры агента потребителя согласовывая вас ваша потребность):
$запрос .= "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'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;
}
в то время как OP хотел избежать 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
заголовки участвует.