从URL获取子域


100

首先,从URL获取子域听起来很容易。

http://www.domain.example

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

那你就记得

http://super.duper.domain.example

哦。因此,您会认为,好吧,找到最后一个时期,再说一句话,然后才得到所有东西!

那你就记得

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

然后您回到正题。除了存储所有TLD列表之外,还有其他好主意吗?


在这里已经问过这个问题:获取URL的一部分编辑:在这里已经问过类似的问题:)
jb。

凸轮你澄清你想要什么?似乎您在URL的“正式”域部分(即domain.co.uk)之后,而不管它之前出现了多少DNS标签?
Alnitak

我不认为这是同一个问题-这似乎更多是关于域名的行政裁员,仅通过查看字符串就
无法解决

我同意。进一步扩展您的最终目标。
BuddyJoe

Answers:


73

除了存储所有TLD列表之外,还有其他好主意吗?

否,因为每个顶级域名(TLD)的不同之处在于子域名,二级域名等。

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

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

因此,这个问题仍然比刚开始时更为复杂,这取决于如何管理每个TLD。您将需要一个包含所有TLD的数据库,其中包括它们的特定分区,以及属于第二级域和子域的内容。但是,由于TLD数量不多,因此该列表可合理管理,但是收集所有这些信息并非易事。可能已经有这样一个列表。

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

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

公共后缀列表是Mozilla基金会的一项倡议。它可以在任何软件中使用,但最初是为了满足浏览器制造商的需求而创建的。它允许浏览器执行以下操作:

  • 避免为高级域名后缀设置破坏隐私的“ supercookies”
  • 在用户界面中突出显示域名中最重要的部分
  • 按站点准确分类历史记录条目

浏览列表,您会发现这不是一个小问题。我认为列表是完成此操作的唯一正确方法。


Mozilla具有使用此服务的代码。该项目被剥离是因为原始的cookie规范已将TLD链接到对cookie的信任,但从未成功。“ Cookie Monster”错误是第一个问题,并且该体系结构从未得到修复或替换。
08年

未列出解决此问题的首选语言,但有一个开源项目使用C#代码在此使用此列表:code.google.com/p/domainname-parser
Dan Esparza,2009年

一个域是否是“公共后缀”,应该通过DNS协议本身(也许通过EDNS标志)真正可用。在这种情况下,所有者可以设置它,而无需维护单独的列表。
Pieter Ennes

@PieterEnnes EDNS用于“传输相关”标志,不能用于与内容相关的元数据。我同意将这些信息最好放在DNS本身中。ISTR计划在即将到来的温哥华IETF上举行“ BoF会议”,以讨论此问题。
Alnitak

26

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

即便如此,也存在例外-例如,.uk有少数不在该级别上立即生效的域,不在中.co.uk,因此必须将其添加为例外。

当前,这是主流浏览器的操作方式-必须确保example.co.uk无法设置Cookie,.co.uk然后该Cookie 会被发送到下的任何其他网站.co.uk

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

IETF中也有一些工作来创建某种标准,以允许TLD声明其域结构。尽管由于的.uk.com类似,这有些复杂,但操作起来好像是一个公共后缀,但没有被.com注册表出售。


1
欧格(Eugh),IETF应该比让URL死掉更了解。现在可以在以下位置找到
IMSoP 2013年

IETF主题工作组(DBOUND)已关闭。
Patrick Mevzek '17

请注意,自从我撰写本文以来,.uk域注册表现在允许直接在第二级进行注册。相应地,这反映在PSL中。
Alnitak

22

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


2
但是请记住,这不仅仅是解析问题!Publicsuffix.org上的此列表是一个非官方的项目,该列表不完整(例如,缺少eu.org),不会自动反映TLD的策略,并且可能随时变得不维护。
bortzmeyer 2009年


7
Mozilla在publicsuffix.org上的列表并不是“非官方的”。鉴于Mozilla,Opera和Chrome使用了它,它不太可能无法维护。至于不完整,任何诸如eu.org之类的域的运营商都可以申请包含,并且他们知道这样做的后果。如果要添加域,请让所有者申请。是的,它不会自动反映TLD政策,但是什么也没做-没有该信息的编程来源。
Gervase Markham

dagger / android:okhttp将为您提供topPrivateDomain
bladerunner,

9

正如亚当和约翰所说,publicsuffix.org是正确的方法。但是,如果由于某种原因您无法使用此方法,以下是一种启发式假设,该假设适用于所有域的99%:

有一个属性可以区分(不是全部,但几乎是全部)“真实”域与子域和TLD,这就是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");
}

这就是IETF 在这里的建议吗?
Ellie Kesselman

1
甚至publicsuffix.org都说(参见第六段),执行此操作的正确方法是通过DNS,就像您在回答中所说的一样!
Ellie Kesselman

1
除了您可以完全拥有一个没有MX记录的域外。而且该算法将被通配符记录所欺骗。而在另一侧必须有MX记录(如顶级域名.ai.ax只是仅举几例)。
Patrick Mevzek,

@patrick:我完全同意;就像我在引言中所说的那样,该算法不是防弹算法,它只是一种启发式算法,效果惊人。
弗朗索瓦·布尔乔亚

2

如前所述,公共后缀列表只是正确解析域的一种方法。对于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'

1

刚刚根据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"}

1

对于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


1
我也有一个C / C ++库,它具有自己的列表,尽管也要根据publicsuffix.org列表进行检查。它称为libtld,可在Unix和MS-Windows下运行。snapwebsites.org/project/libtld
Alexis Wilke

0

它不能完全解决问题,但是您可以尝试通过逐段获取域并检查响应来获得有用的答案,即先获取“ http:// uk ”,然后获取“ http://co.uk ” ,然后是“ http://domain.co.uk ”。当您收到一个非错误的响应时,您就拥有了域,其余的是子域。

有时候,您只需要尝试一下 :)

编辑:

汤姆·莱伊斯(Tom Leys)在评论中指出,某些域仅在www子域上设置,这在上面的测试中给了我们错误的答案。好点子!也许最好的方法是用“ http:// www ”和“ http://” 来检查每个部分,并将命中点算为该域名的命中点?我们仍然会错过一些“替代”安排,例如“ web.domain.com”,但是我有一段时间没有碰到其中一种了:)


即使www.x.com做到了,也无法保证x.com指向端口80处的Web服务器。在这种情况下,www是有效的子域。也许自动化的Whois将在这里有所帮助。
汤姆·莱斯

好点子!Whois会清除它,尽管维护要使用哪个Whois服务器的列表(对于哪个tld / 2nd级)将意味着为边缘情况解决相同的问题。
jTresidder

您假设每个域中都运行HTTP服务器
Francois Bourgeois 2013年

不会为.DK其他人工作,而是按原样http://dk/工作。这种启发式方法不是走的路...
Patrick Mevzek '18年

0

使用URIBuilder,然后将URIBUilder.host属性拆分为“。”上的数组。您现在有了一个域被拆分的数组。


0
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;
}


0

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

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

如果要在浏览器中获取根域。您可以使用此lib 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是棘手的。


0

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

  1. 从publicsuffix.org获取顶级域名
汇入要求

url ='https://publicsuffix.org/list/public_suffix_list.dat'
页面= requests.get(URL)

域= []
用于page.text.splitlines()中的行:
    如果line.startswith('//'):
        继续
    其他:
        域= line.strip()
        如果域:
            domains.append(domain)

域= [d [2:],如果d.startswith('*。'),否则d表示d在域中]
print('found {} domains'.format(len(domains)))
  1. 建立正则表达式
汇入

_regex =''
对于域中的域:
    _regex + = r'{} |'.format(domain.replace('。','\。'))

subdomain_regex = r'/([[^/]*)\.[^/.]+\.({})/.*$'。format(_regex)
domain_regex = r'([[^ /。] + \。({}))/.*$'。format(_regex)
  1. 在网址列表上使用正则表达式
FILE_NAME =''#将CSV文件名放在此处
URL_COLNAME =''#将URL列名称放在此处

将熊猫作为pd导入

df = pd.read_csv(FILE_NAME)
urls = df [URL_COLNAME] .astype(str)+'/'#注意:添加/作为黑客来帮助正则表达式

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)


-3

快速浏览publicsuffix.org列表后,您似乎可以通过从最后一个段长为两个字符的域中删除最后三个段(这里的“段”表示两个点之间的部分)来做出合理的近似,假设它是国家/地区代码,并将进一步细分。如果最后一个段是“我们”,倒数第二个段也是两个字符,则删除最后四个段。在所有其他情况下,请删除最后两个部分。例如:

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

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

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

“我们”是两个字符,并且是“我们”,加上“ wy”也是两个字符,因此删除“ pvt.k12.wy.us”,保留“ foo”。

请注意,尽管这适用于到目前为止我在响应中看到的所有示例,但它仅是一个合理的近似值。这并不完全正确,尽管我怀疑它与您可能不准备/未获得供参考的实际列表差不多。


3
有很多失败的案例。这是浏览器尝试使用的那种算法。不要这样做,使用PSL-它可以工作,并且有一些库可以为您提供帮助。
Gervase Markham

也没有禁止将gTLD进行“细分”的情况,.NAME例如,在刚开始时只能购买firstname.lastname.name域名的情况。而且在相反的方向上,现在.US也变得平坦了,因此您x.y.z.whatever.us只需whatever.us在注册表中购买即可,然后您的算法将失败。
Patrick Mevzek '18年

1
同样关于(“段”在这里是指两个点之间的部分):在DNS世界中被称为标签,无需发明新名称。
Patrick Mevzek '18年
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.