从URL解析域


144

我需要构建一个从URL解析域的函数。

因此,

http://google.com/dhasjkdas/sadsdds/sdda/sdads.html

要么

http://www.google.com/dhasjkdas/sadsdds/sdda/sdads.html

它应该返回 google.com

http://google.co.uk/dhasjkdas/sadsdds/sdda/sdads.html

它应该返回google.co.uk



9
@LightnessRacesinOrbit这不仅仅是“看手册”。PHP parse_url()返回的是主机,而不是domain
MrWhite 2016年

1
@ w3dk:仍然是一个奇妙的起点,允许这个问题是关于限制的,parse_url而不是模糊的“我该怎么办”。
Lightness Races in Orbit

5
@LightnessRacesinOrbit你的防御,是居心叵测给你的名声-更简单地说,你可以承认你并没有完全阅读问题
安迪·琼斯

4
@LightnessRacesinOrbit不一定。support.suso.com/supki/...
秋伦纳德

Answers:


297

签出parse_url()

$url = 'http://google.com/dhasjkdas/sadsdds/sdda/sdads.html';
$parse = parse_url($url);
echo $parse['host']; // prints 'google.com'

parse_url 不能很好地处理错误的URL,但是如果您普遍希望使用合适的URL,那很好。


35
parse_url()不能做的一件事就是仅返回域。如果您添加www.google.com或www.google.co.uk,它也会返回主机。有什么建议吗?
加文·罗伊,


6
parse_url不处理子域,但Purl
Damien

1
parse_url()可能会错误地解析网址中包含破折号的网址。找不到确定的证据,但请检查此错误。内部FILTER_VALIDATE_URL使用parse_url()
XedinUnknown 2015年

8
或简单地说:print parse_url($url, PHP_URL_HOST))如果您不需要该$parse数组来做其他任何事情。
rybo111 '16


23

来自http://us3.php.net/manual/en/function.parse-url.php#93983

由于某些奇怪的原因,当输入url中未提供任何方案时,parse_url返回主机(例如example.com)作为路径。因此,我编写了一个快速函数来获取真实主机:

function getHost($Address) { 
   $parseUrl = parse_url(trim($Address)); 
   return trim($parseUrl['host'] ? $parseUrl['host'] : array_shift(explode('/', $parseUrl['path'], 2))); 
} 

getHost("example.com"); // Gives example.com 
getHost("http://example.com"); // Gives example.com 
getHost("www.example.com"); // Gives www.example.com 
getHost("http://example.com/xyz"); // Gives example.com 

不要忘了像host和一样引用字符串path
Gumbo

1
如果我使用example.com,则php会显示一条通知:有Message: Undefined index: host任何解决办法?
Zim3r 2012年

1
不幸的是,该方法仍然包含子域,请参见示例3。
jenlampton

1
@ Zim3r将三进制的第一部分更改为!empty($parseUrl['host'])
Demonslay335

大声笑,如果没有方案,那么它不是URL。
miken32

12

本来可以100%工作的代码似乎并没有为我削减,但我确实对该示例进行了一些修补,但发现没有帮助的代码和存在问题的代码。因此,我将其更改为几个功能(以始终保存从Mozilla询问列表,并删除缓存系统)。已针对1000个URL进行了测试,并且似乎可以正常工作。

function domain($url)
{
    global $subtlds;
    $slds = "";
    $url = strtolower($url);

    $host = parse_url('http://'.$url,PHP_URL_HOST);

    preg_match("/[^\.\/]+\.[^\.\/]+$/", $host, $matches);
    foreach($subtlds as $sub){
        if (preg_match('/\.'.preg_quote($sub).'$/', $host, $xyz)){
            preg_match("/[^\.\/]+\.[^\.\/]+\.[^\.\/]+$/", $host, $matches);
        }
    }

    return @$matches[0];
}

function get_tlds() {
    $address = 'http://mxr.mozilla.org/mozilla-central/source/netwerk/dns/effective_tld_names.dat?raw=1';
    $content = file($address);
    foreach ($content as $num => $line) {
        $line = trim($line);
        if($line == '') continue;
        if(@substr($line[0], 0, 2) == '/') continue;
        $line = @preg_replace("/[^a-zA-Z0-9\.]/", '', $line);
        if($line == '') continue;  //$line = '.'.$line;
        if(@$line[0] == '.') $line = substr($line, 1);
        if(!strstr($line, '.')) continue;
        $subtlds[] = $line;
        //echo "{$num}: '{$line}'"; echo "<br>";
    }

    $subtlds = array_merge(array(
            'co.uk', 'me.uk', 'net.uk', 'org.uk', 'sch.uk', 'ac.uk', 
            'gov.uk', 'nhs.uk', 'police.uk', 'mod.uk', 'asn.au', 'com.au',
            'net.au', 'id.au', 'org.au', 'edu.au', 'gov.au', 'csiro.au'
        ), $subtlds);

    $subtlds = array_unique($subtlds);

    return $subtlds;    
}

然后像

$subtlds = get_tlds();
echo domain('www.example.com') //outputs: example.com
echo domain('www.example.uk.com') //outputs: example.uk.com
echo domain('www.example.fr') //outputs: example.fr

我知道我应该把它变成一堂课,但是没有时间。


11
function get_domain($url = SITE_URL)
{
    preg_match("/[a-z0-9\-]{1,63}\.[a-z\.]{2,6}$/", parse_url($url, PHP_URL_HOST), $_domain_tld);
    return $_domain_tld[0];
}

get_domain('http://www.cdl.gr'); //cdl.gr
get_domain('http://cdl.gr'); //cdl.gr
get_domain('http://www2.cdl.gr'); //cdl.gr

也对我不起作用:example.com //错误:空字符串example.com //正确:example.com www.example.com //错误:空字符串example.com/xyz //正确:example.com
jenlampton

这是一个很好的答案,应该得到更多的赞誉。只需将此行添加为函数中的第一行,它还解决了MangeshSathe和jenlampton的问题:if((substr($ url,0,strlen('http://'))<>'http://' )&&(substr($ url,0,strlen('https://'))<>'https://'))$ url ='http://'.$url;
里克

4

如果要从string中提取主机http://google.com/dhasjkdas/sadsdds/sdda/sdads.html,则可以使用parse_url()作为解决方案。

但是,如果要提取域或其部分,则需要使用Public Suffix List打包。是的,您可以使用字符串函数arround parse_url(),但有时会产生不正确的结果。

我建议将TLDExtract用于域解析,这是显示diff的示例代码:

$extract = new LayerShifter\TLDExtract\Extract();

# For 'http://google.com/dhasjkdas/sadsdds/sdda/sdads.html'

$url = 'http://google.com/dhasjkdas/sadsdds/sdda/sdads.html';

parse_url($url, PHP_URL_HOST); // will return google.com

$result = $extract->parse($url);
$result->getFullHost(); // will return 'google.com'
$result->getRegistrableDomain(); // will return 'google.com'
$result->getSuffix(); // will return 'com'

# For 'http://search.google.com/dhasjkdas/sadsdds/sdda/sdads.html'

$url = 'http://search.google.com/dhasjkdas/sadsdds/sdda/sdads.html';

parse_url($url, PHP_URL_HOST); // will return 'search.google.com'

$result = $extract->parse($url);
$result->getFullHost(); // will return 'search.google.com'
$result->getRegistrableDomain(); // will return 'google.com'

非常感谢您的建议。我讨厌为看似简单的任务添加另一个库,但是后来我看到他们的自述文件中引用了这句话:“每个人都错了。并且仅当您考虑简单的.com域时,才采用后两个元素会走很长的路要走。例如,以parsings forums.bbc.co.uk为例:上面的幼稚拆分方法会将“ co”作为域, “ uk”作为TLD,而不是分别为“ bbc”和“ co.uk”。”
Demonslay335

分割点而不是在我们喜欢的.co.uk域上不希望发生的结果实际上是正确的结果,co是第二层,而uk是最高层。网站管理员通常没有意识到这一点。
克里斯(Chris)

4

我发现@philfreo的解决方案(从php.net引用)相当不错,可以得到很好的结果,但是在某些情况下,它显示php的“ notice”和“ Strict Standards”消息。这里是此代码的固定版本。

function getHost($url) { 
   $parseUrl = parse_url(trim($url)); 
   if(isset($parseUrl['host']))
   {
       $host = $parseUrl['host'];
   }
   else
   {
        $path = explode('/', $parseUrl['path']);
        $host = $path[0];
   }
   return trim($host); 
} 

echo getHost("http://example.com/anything.html");           // example.com
echo getHost("http://www.example.net/directory/post.php");  // www.example.net
echo getHost("https://example.co.uk");                      // example.co.uk
echo getHost("www.example.net");                            // example.net
echo getHost("subdomain.example.net/anything");             // subdomain.example.net
echo getHost("example.net");                                // example.net

2

这是我使100%仅查找域名的代码,因为它将mozilla sub tlds考虑在内。唯一需要检查的是如何对该文件进行缓存,因此您不必每次都查询mozilla。

出于某些奇怪的原因,像co.uk这样的域名不在列表中,因此您必须进行一些修改并手动添加。它不是最干净的解决方案,但我希望它能对某人有所帮助。

//=====================================================
static function domain($url)
{
    $slds = "";
    $url = strtolower($url);

            $address = 'http://mxr.mozilla.org/mozilla-central/source/netwerk/dns/effective_tld_names.dat?raw=1';
    if(!$subtlds = @kohana::cache('subtlds', null, 60)) 
    {
        $content = file($address);
        foreach($content as $num => $line)
        {
            $line = trim($line);
            if($line == '') continue;
            if(@substr($line[0], 0, 2) == '/') continue;
            $line = @preg_replace("/[^a-zA-Z0-9\.]/", '', $line);
            if($line == '') continue;  //$line = '.'.$line;
            if(@$line[0] == '.') $line = substr($line, 1);
            if(!strstr($line, '.')) continue;
            $subtlds[] = $line;
            //echo "{$num}: '{$line}'"; echo "<br>";
        }
        $subtlds = array_merge(Array(
            'co.uk', 'me.uk', 'net.uk', 'org.uk', 'sch.uk', 'ac.uk', 
            'gov.uk', 'nhs.uk', 'police.uk', 'mod.uk', 'asn.au', 'com.au',
            'net.au', 'id.au', 'org.au', 'edu.au', 'gov.au', 'csiro.au',
            ),$subtlds);

        $subtlds = array_unique($subtlds);
        //echo var_dump($subtlds);
        @kohana::cache('subtlds', $subtlds);
    }


    preg_match('/^(http:[\/]{2,})?([^\/]+)/i', $url, $matches);
    //preg_match("/^(http:\/\/|https:\/\/|)[a-zA-Z-]([^\/]+)/i", $url, $matches);
    $host = @$matches[2];
    //echo var_dump($matches);

    preg_match("/[^\.\/]+\.[^\.\/]+$/", $host, $matches);
    foreach($subtlds as $sub) 
    {
        if (preg_match("/{$sub}$/", $host, $xyz))
        preg_match("/[^\.\/]+\.[^\.\/]+\.[^\.\/]+$/", $host, $matches);
    }

    return @$matches[0];
}

co.uk之所以不在列表中,是因为它是TLD的列表,而不是域的列表。自撰写此答案以来,ccTLD发生了很大变化。值得注意的是:“ Nominet自2014年6月10日08:00 BST开始接受.uk直属的新注册,但是对于已经拥有.co.uk,.org.uk,.me.uk的现有客户有保留期。 ,.net.uk,.ltd.uk或.plc.uk域名申请相应的.uk域名,该域名的有效期至2019年6月10日 BST为止。” (来源
ashleedawg

2

您可以将PHP_URL_HOST作为第二个参数传递给parse_url函数

$url = 'http://google.com/dhasjkdas/sadsdds/sdda/sdads.html';
$host = parse_url($url, PHP_URL_HOST);
print $host; // prints 'google.com'

2
这基本上与上面的答案相同,但是问题是需要,而不一定与主机相同。
MrWhite

请参阅上面有关scheme的评论:由于某些奇怪的原因,当输入url中未提供任何scheme时,parse_url返回主机(例如example.com)作为路径。所以我写了一个快速的功能来获得真正的主人:
jenlampton


2

请考虑用以下替代新的解决方案:

parse_url()将始终包含任何子域,因此此函数不能很好地解析域名。这里有些例子:

$url = 'http://www.google.com/dhasjkdas/sadsdds/sdda/sdads.html';
$parse = parse_url($url);
echo $parse['host']; // prints 'www.google.com'

echo parse_url('https://subdomain.example.com/foo/bar', PHP_URL_HOST);
// Output: subdomain.example.com

echo parse_url('https://subdomain.example.co.uk/foo/bar', PHP_URL_HOST);
// Output: subdomain.example.co.uk

相反,您可以考虑这种务实的解决方案。它将覆盖许多但不是全部域名-例如,不覆盖诸如sos.state.oh.us之类的较低级域名。

function getDomain($url) {
    $host = parse_url($url, PHP_URL_HOST);

    if(filter_var($host,FILTER_VALIDATE_IP)) {
        // IP address returned as domain
        return $host; //* or replace with null if you don't want an IP back
    }

    $domain_array = explode(".", str_replace('www.', '', $host));
    $count = count($domain_array);
    if( $count>=3 && strlen($domain_array[$count-2])==2 ) {
        // SLD (example.co.uk)
        return implode('.', array_splice($domain_array, $count-3,3));
    } else if( $count>=2 ) {
        // TLD (example.com)
        return implode('.', array_splice($domain_array, $count-2,2));
    }
}

// Your domains
    echo getDomain('http://google.com/dhasjkdas/sadsdds/sdda/sdads.html'); // google.com
    echo getDomain('http://www.google.com/dhasjkdas/sadsdds/sdda/sdads.html'); // google.com
    echo getDomain('http://google.co.uk/dhasjkdas/sadsdds/sdda/sdads.html'); // google.co.uk

// TLD
    echo getDomain('https://shop.example.com'); // example.com
    echo getDomain('https://foo.bar.example.com'); // example.com
    echo getDomain('https://www.example.com'); // example.com
    echo getDomain('https://example.com'); // example.com

// SLD
    echo getDomain('https://more.news.bbc.co.uk'); // bbc.co.uk
    echo getDomain('https://www.bbc.co.uk'); // bbc.co.uk
    echo getDomain('https://bbc.co.uk'); // bbc.co.uk

// IP
    echo getDomain('https://1.2.3.45');  // 1.2.3.45

最后,Jeremy Kendall的PHP域解析器允许您从URL解析域名。League URI主机名解析器也将完成此工作。


嗨,这很好,但不适用于IP地址。仍然,很棒的工作。
MeCe

1

parse_url对我不起作用。它仅返回路径。使用php5.3 +切换至基础知识:

$url  = str_replace('http://', '', strtolower( $s->website));
if (strpos($url, '/'))  $url = strstr($url, '/', true);

1

我为您编辑过:

function getHost($Address) { 
    $parseUrl = parse_url(trim($Address));
    $host = trim($parseUrl['host'] ? $parseUrl['host'] : array_shift(explode('/', $parseUrl['path'], 2))); 

    $parts = explode( '.', $host );
    $num_parts = count($parts);

    if ($parts[0] == "www") {
        for ($i=1; $i < $num_parts; $i++) { 
            $h .= $parts[$i] . '.';
        }
    }else {
        for ($i=0; $i < $num_parts; $i++) { 
            $h .= $parts[$i] . '.';
        }
    }
    return substr($h,0,-1);
}

所有类型的url(www.domain.ltd,sub1.subn.domain.ltd都将导致:domain.ltd。


1

我要稍后再添加此答案,因为这是Google上最常出现的答案...

您可以使用PHP来...

$url = "www.google.co.uk";
$host = parse_url($url, PHP_URL_HOST);
// $host == "www.google.co.uk"

主机,而不抢主机引用的私有域。(示例www.google.co.uk是主持人,但google.co.uk私有域)

要获取私有域,您必须知道一个公共后缀列表,可以在该公共后缀注册一个私有域。此列表碰巧是由Mozilla策划的 https://publicsuffix.org/上

当已经创建了公共后缀数组时,以下代码将起作用。只需致电

$domain = get_private_domain("www.google.co.uk");

剩下的代码...

// find some way to parse the above list of public suffix
// then add them to a PHP array
$suffix = [... all valid public suffix ...];

function get_public_suffix($host) {
  $parts = split("\.", $host);
  while (count($parts) > 0) {
    if (is_public_suffix(join(".", $parts)))
      return join(".", $parts);

    array_shift($parts);
  }

  return false;
}

function is_public_suffix($host) {
  global $suffix;
  return isset($suffix[$host]);
}

function get_private_domain($host) {
  $public = get_public_suffix($host);
  $public_parts = split("\.", $public);
  $all_parts = split("\.", $host);

  $private = [];

  for ($x = 0; $x < count($public_parts); ++$x) 
    $private[] = array_pop($all_parts);

  if (count($all_parts) > 0)
    $private[] = array_pop($all_parts);

  return join(".", array_reverse($private));
}

根据我的测试,parse_url需要格式正确的URL。如果仅提供“ www.someDomain.com/path”,则它将返回null。因此,它希望存在一个协议(例如http或https)。
安迪

0

如果输入的URL不是完全垃圾,这通常会很好地工作。它删除子域。

$host = parse_url( $Row->url, PHP_URL_HOST );
$parts = explode( '.', $host );
$parts = array_reverse( $parts );
$domain = $parts[1].'.'.$parts[0];

输入: http://www2.website.com:8080/some/file/structure?some=parameters

输出: website.com


0

worldofjrAlix Axel的答案组合成一个可以处理大多数用例的小函数:

function get_url_hostname($url) {

    $parse = parse_url($url);
    return str_ireplace('www.', '', $parse['host']);

}

get_url_hostname('http://www.google.com/example/path/file.html'); // google.com

这是有限的解决方案
MGE

0
function getTrimmedUrl($link)
{
    $str = str_replace(["www.","https://","http://"],[''],$link);
    $link = explode("/",$str);
    return strtolower($link[0]);                
}


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.