在php中获取域名(不是子域名)

时间:2010-04-21 00:36:18

标签: php regex domain-name

我有一个可以是以下任何格式的网址:

http://example.com
https://example.com
http://example.com/foo
http://example.com/foo/bar
www.example.com
example.com
foo.example.com
www.foo.example.com
foo.bar.example.com
http://foo.bar.example.com/foo/bar
example.net/foo/bar

基本上,我需要能够匹配任何普通的URL。如何通过单个正则表达式从所有这些中提取example.com(或.net,无论tld恰好是什么。我需要这个与任何TLD一起工作。)

18 个答案:

答案 0 :(得分:39)

您可以使用parse_url来获取主持人:

$info = parse_url($url);
$host = $info['host'];

然后,你可以做一些奇特的东西,只获得TLD和主机

$host_names = explode(".", $host);
$bottom_host_name = $host_names[count($host_names)-2] . "." . $host_names[count($host_names)-1];

不是很优雅,但应该有用。


如果你想要解释,请点击这里:

首先,我们通过使用http://的功能来获取方案(parse_url等)之间的所有内容......以及...解析URL。 :)

然后我们获取主机名,并根据句点下降的位置将其分成一个数组,因此test.world.hello.myname将成为:

array("test", "world", "hello", "myname");

之后,我们获取数组中元素的数量(4)。

然后,我们从中减去2以获得倒数第二个字符串(主机名,或者example,在您的示例中)

然后,我们从中减去1得到最后一个字符串(因为数组键从0开始),也称为TLD

然后我们将这两个部分组合成一个句点,并且你有了基本主机名。

答案 1 :(得分:13)

我在https://gist.github.com/pocesar/5366899

中的解决方案

并且测试在http://codepad.viper-7.com/GAh1tP

适用于任何TLD和可怕的子域模式(最多3个子域)。

许多域名都包含测试。

这里不会粘贴函数,因为StackOverflow中的代码有一些奇怪的缩进(可能有像github一样的受阻代码块)

答案 2 :(得分:6)

如果不使用TLD列表进行比较,则无法获取域名,因为它们存在许多具有完全相同结构和长度的案例:

  1. www.db.de(子域名)与bbc.co.uk(域名)
  2. big.uk.com(SLD)与www.uk.com(TLD)
  3. Mozilla的公开后缀列表应该是所有major browsers使用的最佳选项:
    https://publicsuffix.org/list/public_suffix_list.dat

    随意使用我的功能:

    function tld_list($cache_dir=null) {
        // we use "/tmp" if $cache_dir is not set
        $cache_dir = isset($cache_dir) ? $cache_dir : sys_get_temp_dir();
        $lock_dir = $cache_dir . '/public_suffix_list_lock/';
        $list_dir = $cache_dir . '/public_suffix_list/';
        // refresh list all 30 days
        if (file_exists($list_dir) && @filemtime($list_dir) + 2592000 > time()) {
            return $list_dir;
        }
        // use exclusive lock to avoid race conditions
        if (!file_exists($lock_dir) && @mkdir($lock_dir)) {
            // read from source
            $list = @fopen('https://publicsuffix.org/list/public_suffix_list.dat', 'r');
            if ($list) {
                // the list is older than 30 days so delete everything first
                if (file_exists($list_dir)) {
                    foreach (glob($list_dir . '*') as $filename) {
                        unlink($filename);
                    }
                    rmdir($list_dir);
                }
                // now set list directory with new timestamp
                mkdir($list_dir);
                // read line-by-line to avoid high memory usage
                while ($line = fgets($list)) {
                    // skip comments and empty lines
                    if ($line[0] == '/' || !$line) {
                        continue;
                    }
                    // remove wildcard
                    if ($line[0] . $line[1] == '*.') {
                        $line = substr($line, 2);
                    }
                    // remove exclamation mark
                    if ($line[0] == '!') {
                        $line = substr($line, 1);
                    }
                    // reverse TLD and remove linebreak
                    $line = implode('.', array_reverse(explode('.', (trim($line)))));
                    // we split the TLD list to reduce memory usage
                    touch($list_dir . $line);
                }
                fclose($list);
            }
            @rmdir($lock_dir);
        }
        // repair locks (should never happen)
        if (file_exists($lock_dir) && mt_rand(0, 100) == 0 && @filemtime($lock_dir) + 86400 < time()) {
            @rmdir($lock_dir);
        }
        return $list_dir;
    }
    function get_domain($url=null) {
        // obtain location of public suffix list
        $tld_dir = tld_list();
        // no url = our own host
        $url = isset($url) ? $url : $_SERVER['SERVER_NAME'];
        // add missing scheme      ftp://            http:// ftps://   https://
        $url = !isset($url[5]) || ($url[3] != ':' && $url[4] != ':' && $url[5] != ':') ? 'http://' . $url : $url;
        // remove "/path/file.html", "/:80", etc.
        $url = parse_url($url, PHP_URL_HOST);
        // replace absolute domain name by relative (http://www.dns-sd.org/TrailingDotsInDomainNames.html)
        $url = trim($url, '.');
        // check if TLD exists
        $url = explode('.', $url);
        $parts = array_reverse($url);
        foreach ($parts as $key => $part) {
            $tld = implode('.', $parts);
            if (file_exists($tld_dir . $tld)) {
                return !$key ? '' : implode('.', array_slice($url, $key - 1));
            }
            // remove last part
            array_pop($parts);
        }
        return '';
    }
    

    它的特殊之处:

    • 它接受带有或不带方案的URL,主机名或域等所有输入
    • 逐行下载列表以避免高内存使用
    • 它在缓存文件夹中为每个TLD创建一个新文件,因此get_domain()只需检查file_exists()是否存在,因此它不需要在每个请求中包含一个庞大的数据库,如{{3做到了。
    • 该列表将每30天自动更新

    测试:

    $urls = array(
        'http://www.example.com',// example.com
        'http://subdomain.example.com',// example.com
        'http://www.example.uk.com',// example.uk.com
        'http://www.example.co.uk',// example.co.uk
        'http://www.example.com.ac',// example.com.ac
        'http://example.com.ac',// example.com.ac
        'http://www.example.accident-prevention.aero',// example.accident-prevention.aero
        'http://www.example.sub.ar',// sub.ar
        'http://www.congresodelalengua3.ar',// congresodelalengua3.ar
        'http://congresodelalengua3.ar',// congresodelalengua3.ar
        'http://www.example.pvt.k12.ma.us',// example.pvt.k12.ma.us
        'http://www.example.lib.wy.us',// example.lib.wy.us
        'com',// empty
        '.com',// empty
        'http://big.uk.com',// big.uk.com
        'uk.com',// empty
        'www.uk.com',// www.uk.com
        '.uk.com',// empty
        'stackoverflow.com',// stackoverflow.com
        '.foobarfoo',// empty
        '',// empty
        false,// empty
        ' ',// empty
        1,// empty
        'a',// empty    
    );
    

    最新版本及解释(德语):
    TLDExtract

答案 3 :(得分:5)

$onlyHostName = implode('.', array_slice(explode('.', parse_url($link, PHP_URL_HOST)), -2));

答案 4 :(得分:5)

我认为解决此问题的最佳方法是:

$second_level_domains_regex = '/\.asn\.au$|\.com\.au$|\.net\.au$|\.id\.au$|\.org\.au$|\.edu\.au$|\.gov\.au$|\.csiro\.au$|\.act\.au$|\.nsw\.au$|\.nt\.au$|\.qld\.au$|\.sa\.au$|\.tas\.au$|\.vic\.au$|\.wa\.au$|\.co\.at$|\.or\.at$|\.priv\.at$|\.ac\.at$|\.avocat\.fr$|\.aeroport\.fr$|\.veterinaire\.fr$|\.co\.hu$|\.film\.hu$|\.lakas\.hu$|\.ingatlan\.hu$|\.sport\.hu$|\.hotel\.hu$|\.ac\.nz$|\.co\.nz$|\.geek\.nz$|\.gen\.nz$|\.kiwi\.nz$|\.maori\.nz$|\.net\.nz$|\.org\.nz$|\.school\.nz$|\.cri\.nz$|\.govt\.nz$|\.health\.nz$|\.iwi\.nz$|\.mil\.nz$|\.parliament\.nz$|\.ac\.za$|\.gov\.za$|\.law\.za$|\.mil\.za$|\.nom\.za$|\.school\.za$|\.net\.za$|\.co\.uk$|\.org\.uk$|\.me\.uk$|\.ltd\.uk$|\.plc\.uk$|\.net\.uk$|\.sch\.uk$|\.ac\.uk$|\.gov\.uk$|\.mod\.uk$|\.mil\.uk$|\.nhs\.uk$|\.police\.uk$/';
$domain = $_SERVER['HTTP_HOST'];
$domain = explode('.', $domain);
$domain = array_reverse($domain);
if (preg_match($second_level_domains_regex, $_SERVER['HTTP_HOST']) {
    $domain = "$domain[2].$domain[1].$domain[0]";
} else {
    $domain = "$domain[1].$domain[0]";
}

答案 5 :(得分:4)

我建议将TLDExtract库用于所有域名操作。

答案 6 :(得分:4)

从主机中提取子域有两种方法:

  1. 第一种更准确的方法是使用tld数据库(如public_suffix_list.dat)并将域与之匹配。在某些情况下,这有点重。有一些PHP类可以使用它,如php-domain-parserTLDExtract

  2. 第二种方式不如第一种方式准确,但速度非常快,在很多情况下它可以给出正确答案,我为它写了这个函数:

    function get_domaininfo($url) {
        // regex can be replaced with parse_url
        preg_match("/^(https|http|ftp):\/\/(.*?)\//", "$url/" , $matches);
        $parts = explode(".", $matches[2]);
        $tld = array_pop($parts);
        $host = array_pop($parts);
        if ( strlen($tld) == 2 && strlen($host) <= 3 ) {
            $tld = "$host.$tld";
            $host = array_pop($parts);
        }
    
        return array(
            'protocol' => $matches[1],
            'subdomain' => implode(".", $parts),
            'domain' => "$host.$tld",
            'host'=>$host,'tld'=>$tld
        );
    }
    

    示例:

    print_r(get_domaininfo('http://mysubdomain.domain.co.uk/index.php'));
    

    返回:

    Array
    (
        [protocol] => https
        [subdomain] => mysubdomain
        [domain] => domain.co.uk
        [host] => domain
        [tld] => co.uk
    )
    

答案 7 :(得分:3)

这里是我写的一个函数,用于获取没有子域名的域名,无论域名是使用ccTLD还是新式长TLD等等......没有已知的查找或大量数组TLD,没有正则表达式。使用三元运算符和嵌套可以缩短很多时间,但为了便于阅读,我对其进行了扩展。

// Per Wikipedia: "All ASCII ccTLD identifiers are two letters long, 
// and all two-letter top-level domains are ccTLDs."

function topDomainFromURL($url) {
  $url_parts = parse_url($url);
  $domain_parts = explode('.', $url_parts['host']);
  if (strlen(end($domain_parts)) == 2 ) { 
    // ccTLD here, get last three parts
    $top_domain_parts = array_slice($domain_parts, -3);
  } else {
    $top_domain_parts = array_slice($domain_parts, -2);
  }
  $top_domain = implode('.', $top_domain_parts);
  return $top_domain;
}

答案 8 :(得分:1)

这是适用于所有域名的域名,包括那些具有二级域名的域名,例如“co.uk”

$sql_5 = "SHOW TABLES FROM $db_name NOT LIKE `sondaggio attivo`";

这里看起来有一个重复的问题:delete-subdomain-from-url-string-if-subdomain-is-found

答案 9 :(得分:1)

很晚,我发现您将regex标记为关键字,并且我的功能像一个超级按钮一样工作,到目前为止,我还没有找到失败的网址:

function get_domain_regex($url){
  $pieces = parse_url($url);
  $domain = isset($pieces['host']) ? $pieces['host'] : '';
  if (preg_match('/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $domain, $regs)) {
    return $regs['domain'];
  }else{
    return false;
  }
}

如果您想要一个没有正则表达式的东西,我有这个,我相信我也从这篇文章中摘录了

function get_domain($url){
  $parseUrl = parse_url($url);
  $host = $parseUrl['host'];
  $host_array = explode(".", $host);
  $domain = $host_array[count($host_array)-2] . "." . $host_array[count($host_array)-1];
  return $domain;
}

它们的工作都很棒,但是,这花了我一段时间才意识到,如果网址不是以http://或https://开头,它将失败,因此请确保网址字符串以协议开头。

答案 10 :(得分:1)

我对pocesar提供的解决方案有疑问。 当我使用例如subdomain.domain.nl时,它不会返回domain.nl。相反,它将返回subdomain.domain.nl 另一个问题是domain.com.br将返回com.br

我不确定但是我用以下代码解决了这些问题(我希望它会帮助某人,如果是这样的话,我会很开心):

function get_domain($domain, $debug = false){
    $original = $domain = strtolower($domain);
    if (filter_var($domain, FILTER_VALIDATE_IP)) {
        return $domain;
    }
    $debug ? print('<strong style="color:green">&raquo;</strong> Parsing: '.$original) : false;
    $arr = array_slice(array_filter(explode('.', $domain, 4), function($value){
        return $value !== 'www';
    }), 0); //rebuild array indexes
    if (count($arr) > 2){
        $count = count($arr);
        $_sub = explode('.', $count === 4 ? $arr[3] : $arr[2]);
        $debug ? print(" (parts count: {$count})") : false;
        if (count($_sub) === 2){ // two level TLD
            $removed = array_shift($arr);
            if ($count === 4){ // got a subdomain acting as a domain
                $removed = array_shift($arr);
            }
            $debug ? print("<br>\n" . '[*] Two level TLD: <strong>' . join('.', $_sub) . '</strong> ') : false;
        }elseif (count($_sub) === 1){ // one level TLD
            $removed = array_shift($arr); //remove the subdomain
            if (strlen($arr[0]) === 2 && $count === 3){ // TLD domain must be 2 letters
                array_unshift($arr, $removed);
            }elseif(strlen($arr[0]) === 3 && $count === 3){
                array_unshift($arr, $removed);
            }else{
                // non country TLD according to IANA
                $tlds = array(
                    'aero',
                    'arpa',
                    'asia',
                    'biz',
                    'cat',
                    'com',
                    'coop',
                    'edu',
                    'gov',
                    'info',
                    'jobs',
                    'mil',
                    'mobi',
                    'museum',
                    'name',
                    'net',
                    'org',
                    'post',
                    'pro',
                    'tel',
                    'travel',
                    'xxx',
                );
                if (count($arr) > 2 && in_array($_sub[0], $tlds) !== false){ //special TLD don't have a country
                    array_shift($arr);
                }
            }
            $debug ? print("<br>\n" .'[*] One level TLD: <strong>'.join('.', $_sub).'</strong> ') : false;
        }else{ // more than 3 levels, something is wrong
            for ($i = count($_sub); $i > 1; $i--){
                $removed = array_shift($arr);
            }
            $debug ? print("<br>\n" . '[*] Three level TLD: <strong>' . join('.', $_sub) . '</strong> ') : false;
        }
    }elseif (count($arr) === 2){
        $arr0 = array_shift($arr);
        if (strpos(join('.', $arr), '.') === false && in_array($arr[0], array('localhost','test','invalid')) === false){ // not a reserved domain
            $debug ? print("<br>\n" .'Seems invalid domain: <strong>'.join('.', $arr).'</strong> re-adding: <strong>'.$arr0.'</strong> ') : false;
            // seems invalid domain, restore it
            array_unshift($arr, $arr0);
        }
    }
    $debug ? print("<br>\n".'<strong style="color:gray">&laquo;</strong> Done parsing: <span style="color:red">' . $original . '</span> as <span style="color:blue">'. join('.', $arr) ."</span><br>\n") : false;
    return join('.', $arr);
}

答案 11 :(得分:0)

只需尝试一下:

   preg_match('/(www.)?([^.]+\.[^.]+)$/', $yourHost, $matches);

   echo "domain name is: {$matches[0]}\n"; 

这适用于大多数域。

答案 12 :(得分:0)

echo getDomainOnly("http://example.com/foo/bar");

function getDomainOnly($host){
    $host = strtolower(trim($host));
    $host = ltrim(str_replace("http://","",str_replace("https://","",$host)),"www.");
    $count = substr_count($host, '.');
    if($count === 2){
        if(strlen(explode('.', $host)[1]) > 3) $host = explode('.', $host, 2)[1];
    } else if($count > 2){
        $host = getDomainOnly(explode('.', $host, 2)[1]);
    }
    $host = explode('/',$host);
    return $host[0];
}

答案 13 :(得分:0)

即使您解析不带http://或https://

的url,此函数也将返回不带任何扩展名的域名。

您可以扩展此代码

(?:\.co)?(?:\.com)?(?:\.gov)?(?:\.net)?(?:\.org)?(?:\.id)?

具有更多扩展名,如果您想处理更多二级域名。

    function get_domain_name($url){
      $pieces = parse_url($url);
      $domain = isset($pieces['host']) ? $pieces['host'] : $url;
      $domain = strtolower($domain);
      $domain = preg_replace('/.international$/', '.com', $domain);
      if (preg_match('/(?P<domain>[a-z0-9][a-z0-9\-]{1,90}\.[a-z\.]{2,6})$/i', $domain, $regs)) {
          if (preg_match('/(.*?)((?:\.co)?(?:\.com)?(?:\.gov)?(?:\.net)?(?:\.org)?(?:\.id)?(?:\.asn)?.[a-z]{2,6})$/i', $regs['domain'], $matches)) {
              return $matches[1];
          }else  return $regs['domain'];
      }else{
        return $url;
      }
    }

答案 14 :(得分:0)

我用它来实现相同的目标,它总是有效,我希望它能帮助其他人。

$url          = https://use.fontawesome.com/releases/v5.11.2/css/all.css?ver=2.7.5
$handle       = pathinfo( parse_url( $url )['host'] )['filename'];
$final_handle = substr( $handle , strpos( $handle , '.' ) + 1 );

print_r($final_handle); // fontawesome 

答案 15 :(得分:0)

最简单的解决方案

@preg_replace('#\/(.)*#', '', @preg_replace('#^https?://(www.)?#', '', $url))

答案 16 :(得分:0)

function getDomain($url){
    $pieces = parse_url($url);
    $domain = isset($pieces['host']) ? $pieces['host'] : '';
    if(preg_match('/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $domain, $regs)){
        return $regs['domain'];
    }
    return FALSE;
}

echo getDomain("http://example.com"); // outputs 'example.com'
echo getDomain("http://www.example.com"); // outputs 'example.com'
echo getDomain("http://mail.example.co.uk"); // outputs 'example.co.uk'

答案 17 :(得分:-1)

试试这个:

{{1}}