从 URL 获取子域一开始听起来很简单。

http://www.domain.example

扫描第一个句点,然后返回“http://”之后的内容...

然后你记得

http://super.duper.domain.example

哦。然后你想,好吧,找到最后一个句点,返回一个单词并获得之前的所有内容!

然后你记得

http://super.duper.domain.co.uk

你又回到了原点。除了存储所有顶级域名 (TLD) 的列表之外,还有什么好主意吗?

有帮助吗?

解决方案

除了存储所有TLD列表外,还有什么好主意吗?

否,因为每个 TLD 在子域、二级域等的计算方式上有所不同。

请记住,存在顶级域、二级域和子域。从技术上讲,除了 TLD 之外的所有内容都是子域。

在domain.com.uk 示例中,“domain”是子域,“com”是二级域,“uk”是 TLD。

因此,问题仍然比乍一看更加复杂,并且取决于每个顶级域名 (TLD) 的管理方式。您需要一个包含所有 TLD 的数据库,其中包括其特定分区以及二级域名和子域名。不过,顶级域名 (TLD) 并不多,因此该列表相当易于管理,但收集所有这些信息并非易事。可能已经有这样的列表了。

好像 http://publicsuffix.org/ 就是这样一个列表 - 适合搜索的列表中的所有常见后缀(.com、.co.uk 等)。解析它仍然不容易,但至少您不必维护该列表。

“公共后缀”是互联网用户可以直接注册名称的一个。公共后缀的一些示例是“ .com”,“ .co.uk”和“ pvt.k12.wy.us”。公共后缀列表是所有已知公共后缀的列表。

公共后缀列表是Mozilla基金会的倡议。它可用于任何软件,但最初是为了满足浏览器制造商的需求而创建的。它允许浏览器,例如:

  • 避免为高级域名后缀设置隐私的“超级木匠”
  • 突出显示用户界面中域名最重要的部分
  • 按站点准确排序历史条目

浏览列表, ,你会发现这不是一个小问题。我认为列表是实现这一目标的唯一正确方法......

其他提示

正如亚当所说,这并不容易,目前唯一可行的方法是使用清单。

即使这样也有例外 - 例如在.uk中有一些域在该级别立即生效但不在.co.uk中,因此必须将这些域添加为例外。

这是目前主流浏览器如何做到这一点 - 有必要确保example.co.uk无法为.uk.com设置Cookie,然后将其发送到.com下的任何其他网站。

好消息是 http://publicsuffix.org/ 上已有列表。

IETF 中还有一些工作可以创建某种标准,允许TLD声明其域结构的外观。虽然<=>喜欢<=>,但它有点复杂,它的操作就好像是公共后缀,但不是由<=>注册表出售。

Publicsuffix.org 似乎是可行的方法。有很多实现可以轻松解析 publicsuffix 数据文件的内容:

正如亚当和约翰所说, publicsuffix.org 是正确的方法。但是,如果由于任何原因你不能使用这种方法,这里的启发式基于一个适用于99%的所有域的假设:

有一个属性区分(不是全部,但几乎所有)<!>“真实的<!>”;来自子域和顶级域名的域名,这是DNS的MX记录。您可以创建一个搜索此算法的算法:逐个删除主机名的各个部分并查询DNS,直到找到MX记录。例如:

super.duper.domain.co.uk => no MX record, proceed
duper.domain.co.uk       => no MX record, proceed
domain.co.uk             => MX record found! assume that's the domain

这是php中的一个例子:

function getDomainWithMX($url) {
    //parse hostname from URL 
    //http://www.example.co.uk/index.php => www.example.co.uk
    $urlParts = parse_url($url);
    if ($urlParts === false || empty($urlParts["host"])) 
        throw new InvalidArgumentException("Malformed URL");

    //find first partial name with MX record
    $hostnameParts = explode(".", $urlParts["host"]);
    do {
        $hostname = implode(".", $hostnameParts);
        if (checkdnsrr($hostname, "MX")) return $hostname;
    } while (array_shift($hostnameParts) !== null);

    throw new DomainException("No MX record found");
}

正如已经说过的那样,公共后缀列表只是正确解析域名的一种方法。对于PHP,您可以尝试 TLDExtract 。以下是示例代码:

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

$result = $extract->parse('super.duper.domain.co.uk');
$result->getSubdomain(); // will return (string) 'super.duper'
$result->getSubdomains(); // will return (array) ['super', 'duper']
$result->getHostname(); // will return (string) 'domain'
$result->getSuffix(); // will return (string) 'co.uk'

根据来自publicsuffix.org的信息,在clojure中编写了一个程序:

https://github.com/isaksky/url_dom

例如:

(parse "sub1.sub2.domain.co.uk") 
;=> {:public-suffix "co.uk", :domain "domain.co.uk", :rule-used "*.uk"}

对于C库(在Python中生成数据表),我写了 http:这是快速且节省空间的//code.google.com/p/domain-registry-provider/

库使用~30kB的数据表和~10kB的C代码。由于表是在编译时构造的,因此没有启动开销。请参阅 http://code.google.com/p/domain-registry- provider / wiki / DesignDoc 了解更多详情。

为了更好地理解表生成代码(Python),请从这里开始: http://code.google.com/p/domain-registry-provider/source/browse/trunk/src/registry_tables_generator/registry_tables_generator.py

要更好地了解C API,请参阅: http://code.google.com/p/domain-registry-provider/source/browse/trunk/src/domain_registry/domain_registry.h

它并没有完全解决这个问题,但你可以通过尝试逐段获取域并检查响应来获得有用的答案,即获取' http:// uk ',然后' http://co.uk ',然后' http://domain.co.uk '。当您收到非错误响应时,您已获得域名,其余域名为子域名。

有时你只需要尝试:)

编辑:

Tom Leys在评论中指出,某些域名仅在www子域名中设置,这将在上述测试中给出错误答案。好点子!也许最好的方法是用“ http:// www ”以及“http://”检查每个部分,并计算一个命中作为该域名的一部分?我们仍然缺少一些'替代'安排,例如'web.domain.com',但我有一段时间没有碰到其中一个:)

使用URIBuilder 然后获取URIBUilder.host属性 将其拆分为<!> quot;。<!>的数组。 你现在有一个分割出域名的数组。

echo tld('http://www.example.co.uk/test?123'); // co.uk

/**
 * http://publicsuffix.org/
 * http://www.alandix.com/blog/code/public-suffix/
 * http://tobyinkster.co.uk/blog/2007/07/19/php-domain-class/
 */
function tld($url_or_domain = null)
{
    $domain = $url_or_domain ?: $_SERVER['HTTP_HOST'];
    preg_match('/^[a-z]+:\/\//i', $domain) and 
        $domain = parse_url($domain, PHP_URL_HOST);
    $domain = mb_strtolower($domain, 'UTF-8');
    if (strpos($domain, '.') === false) return null;

    $url = 'http://mxr.mozilla.org/mozilla-central/source/netwerk/dns/effective_tld_names.dat?raw=1';

    if (($rules = file($url)) !== false)
    {
        $rules = array_filter(array_map('trim', $rules));
        array_walk($rules, function($v, $k) use(&$rules) { 
            if (strpos($v, '//') !== false) unset($rules[$k]);
        });

        $segments = '';
        foreach (array_reverse(explode('.', $domain)) as $s)
        {
            $wildcard = rtrim('*.'.$segments, '.');
            $segments = rtrim($s.'.'.$segments, '.');

            if (in_array('!'.$segments, $rules))
            {
                $tld = substr($wildcard, 2);
                break;
            }
            elseif (in_array($wildcard, $rules) or 
                    in_array($segments, $rules))
            {
                $tld = $segments;
            }
        }

        if (isset($tld)) return $tld;
    }

    return false;
}

我刚写了一个objc库: https://github.com/kejinlu/KKDomain

您可以使用此lib tld.js:JavaScript API来处理复杂的域名,子域和URI 。

tldjs.getDomain('mail.google.co.uk');
// -> 'google.co.uk'

如果您在浏览器中获得根域。您可以使用此库 AngusFu / browser-root-domain

var KEY = '__rT_dM__' + (+new Date());
var R = new RegExp('(^|;)\\s*' + KEY + '=1');
var Y1970 = (new Date(0)).toUTCString();

module.exports = function getRootDomain() {
  var domain = document.domain || location.hostname;
  var list = domain.split('.');
  var len = list.length;
  var temp = '';
  var temp2 = '';

  while (len--) {
    temp = list.slice(len).join('.');
    temp2 = KEY + '=1;domain=.' + temp;

    // try to set cookie
    document.cookie = temp2;

    if (R.test(document.cookie)) {
      // clear
      document.cookie = temp2 + ';expires=' + Y1970;
      return temp;
    }
  }
};

使用cookie很棘手。

如果您要从任意URL列表中提取子域和/或域,则此python脚本可能会有所帮助。但要小心,它并不完美。这通常是一个棘手的问题,如果你有一个你期望的域名白名单,那将非常有用。

  1. 从publicsuffix.org获取顶级域名
  2. import requests
    
    url = 'https://publicsuffix.org/list/public_suffix_list.dat'
    page = requests.get(url)
    
    domains = []
    for line in page.text.splitlines():
        if line.startswith('//'):
            continue
        else:
            domain = line.strip()
            if domain:
                domains.append(domain)
    
    domains = [d[2:] if d.startswith('*.') else d for d in domains]
    print('found {} domains'.format(len(domains)))
    
    1. 构建正则表达式
    2. import re
      
      _regex = ''
      for domain in domains:
          _regex += r'{}|'.format(domain.replace('.', '\.'))
      
      subdomain_regex = r'/([^/]*)\.[^/.]+\.({})/.*$'.format(_regex)
      domain_regex = r'([^/.]+\.({}))/.*$'.format(_regex)
      
      1. 在网址列表中使用正则表达式
      2. FILE_NAME = ''   # put CSV file name here
        URL_COLNAME = '' # put URL column name here
        
        import pandas as pd
        
        df = pd.read_csv(FILE_NAME)
        urls = df[URL_COLNAME].astype(str) + '/' # note: adding / as a hack to help regex
        
        df['sub_domain_extracted'] = urls.str.extract(pat=subdomain_regex, expand=True)[0]
        df['domain_extracted'] = urls.str.extract(pat=domain_regex, expand=True)[0]
        
        df.to_csv('extracted_domains.csv', index=False)
        

与http://一起删除的常见后缀(.co.uk,.com等等)的列表,然后你只有<!> quot; sub.domain <!> quot;使用而不是<!>“ http://sub.domain.suffix <!> quot; ,或者至少那是我可能会做的事情。

最大的问题是可能的后缀列表。 毕竟还有很多。

快速查看了 publicsuffix.org 列表后,您似乎可以通过从最终段长度为两个字符的域中删除最后三个段(此处的“段”表示两个点之间的部分)来进行合理的近似,假设它是一个国家代码并将进一步细分。如果最后一个段是“us”并且倒数第二个段也是两个字符,则删除最后四个段。在所有其他情况下,删除最后两段。例如。:

“example”不是两个字符,因此删除“domain.example”,留下“www”

“example”不是两个字符,因此删除“domain.example”,留下“super.duper”

“uk”是两个字符(但不是“us”),因此删除“domain.co.uk”,留下“super.duper”

“us”是两个字符,是“us”,加上“wy”也是两个字符,所以删除“pvt.k12.wy.us”,留下“foo”。

请注意,尽管这适用于我迄今为止在响应中看到的所有示例,但它仍然只是一个合理的近似值。它并不完全正确,尽管我怀疑它与您在不制作/获取实际列表以供参考的情况下可能得到的最接近。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top