【问题标题】:Parsing domain from a URL从 URL 解析域
【发布时间】:2010-09-21 13:10:39
【问题描述】:

我需要构建一个从 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

【问题讨论】:

  • @LightnessRacesinOrbit 这不仅仅是“查看手册”。 PHP 的 parse_url() 返回 host,而不是 domain
  • @w3dk:这仍然是一个很棒的起点,让这个问题与parse_url 的限制有关,而不是模糊的“我能做什么”。
  • @LightnessRacesinOrbit 鉴于您的声誉,您的辩护是虚伪的 - 更简单地说,您可以承认您没有完全阅读问题
  • @LightnessRacesinOrbit 不一定。 support.suso.com/supki/…

标签: php


【解决方案1】:

查看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,那就没问题了。

【讨论】:

  • parse_url() 不做的一件事就是只返回域。如果您添加 www.google.com 或 www.google.co.uk,它也会返回主机。有什么建议吗?
  • parse_url 不处理子域,但 Purl 可以:github.com/jwage/purl
  • parse_url() 可能会错误地解析包含破折号的域的 URL。找不到确切的证据,但请查看this bugFILTER_VALIDATE_URL 在内部使用 parse_url()
  • 或者简单地说:print parse_url($url, PHP_URL_HOST)),如果你不需要 $parse 数组来做其他事情。
【解决方案2】:
$domain = str_ireplace('www.', '', parse_url($url, PHP_URL_HOST));

这将为http://google.com/...和http://www.google.com/...返回google.com...

【讨论】:

  • '因为如果您输入“server.google.com”或“www3.google.com”,它仍然会返回服务器...
  • 并非所有子域都是 www,crawl-66-249-66-1.googlebot.com、myblog.blogspot.com 就是一些例子。
【解决方案3】:

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

出于某种奇怪的原因,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 

【讨论】:

  • 不要忘记引用您的字符串,例如 hostpath
  • 如果我使用 example.com,php 会显示一条通知:Message: Undefined index: host 有什么想法可以解决这个问题吗?
  • 不幸的是,这种方法仍然包含子域,请参阅您的示例 #3。
  • @Zim3r 将三元的第一部分改为!empty($parseUrl['host'])
  • LOL 如果它没有方案,它就不是 URL。
【解决方案4】:
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
  • 这是一个很好的答案,值得更多赞扬。只需将此行添加为函数的第一行,它也解决了 MangeshSathe 和 jenlampton 的问题: if((substr($url,0,strlen('http://')) 'http://' ) && (substr($url,0,strlen('https://')) 'https://')) $url = 'http://'.$url;
【解决方案5】:

本应 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

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

【讨论】:

【解决方案6】:

如果您想从字符串 http://google.com/dhasjkdas/sadsdds/sdda/sdads.html 中提取主机,则使用 parse_url() 是您可以接受的解决方案。

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

我推荐TLDExtract 进行域解析,这里是显示差异的示例代码:

$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 域时才采用最后 2 个元素。例如,考虑解析 forums.bbc.co.uk:上面的简单拆分方法将为您提供“co”作为域,将“uk”作为TLD,而不是 'bbc' 和 'co.uk'。”
  • 分裂点的结果虽然不是我们想要在我们心爱的 .co.uk 域上发生的结果,但实际上是正确的结果,co 是第二级,uk 是顶级。网站管理员通常没有意识到这一点。
【解决方案7】:

请考虑将接受的解决方案替换为以下内容:

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 Domain Parser 允许您从 url 解析域名。 League URI Hostname Parser 也可以完成这项工作。

【讨论】:

  • 嗨,这很好,但它不适用于 IP 地址。尽管如此,还是很棒的工作。
【解决方案8】:

您可以将 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'

【讨论】:

  • 这与上面的答案基本相同,但是问题是需要 domain,它不一定与 host相同>.
  • 请参阅上面关于方案的评论:由于某些奇怪的原因,当输入 url 中未提供方案时,parse_url 返回主机(例如 example.com)作为路径。所以我写了一个快速函数来获取真正的主机:
【解决方案9】:

我发现@philfreo 的解决方案(引用自 php.net)非常好,可以得到很好的结果,但在某些情况下,它会显示 php 的“通知”和“严格标准”消息。这是这段代码的固定版本。

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

【讨论】:

    【解决方案10】:

    这是我编写的代码,它 100% 只找到域名,因为它需要 mozilla 子 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 日 07:59 BST。 " (Source)
    【解决方案11】:
    $domain = parse_url($url, PHP_URL_HOST);
    echo implode('.', array_slice(explode('.', $domain), -2, 2))
    
    【解决方案12】:
    function getTrimmedUrl($link)
    {
        $str = str_replace(["www.","https://","http://"],[''],$link);
        $link = explode("/",$str);
        return strtolower($link[0]);                
    }
    

    【讨论】:

      【解决方案13】:

      parse_url 对我不起作用。它只返回了路径。使用 php5.3+ 切换到基础:

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

      【讨论】:

        【解决方案14】:

        我已经为你编辑了:

        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.

        【讨论】:

          【解决方案15】:

          我很晚才添加这个答案,因为这是在 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)。
          【解决方案16】:

          如果输入的 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

          【讨论】:

            【解决方案17】:

            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
            

            【讨论】:

            • 这是有限的解决方案
            【解决方案18】:

            就像下面这样使用...

            <?php
               echo $_SERVER['SERVER_NAME'];
            ?>
            

            【讨论】:

            • 这是假设服务器是您要从中检索域的 url。事实并非如此。
            猜你喜欢
            • 1970-01-01
            • 2012-08-30
            • 1970-01-01
            • 2016-06-12
            • 2020-01-19
            • 1970-01-01
            • 1970-01-01
            • 2012-08-05
            • 2016-01-13
            相关资源
            最近更新 更多