Obtenha o subdomínio de um URL


100

Obter o subdomínio de um URL parece fácil no início.

http://www.domain.example

Procure o primeiro período e, em seguida, retorne o que veio depois de "http: //" ...

Então você lembra

http://super.duper.domain.example

Oh. Então você pensa, ok, encontre o último período, volte uma palavra e pegue tudo antes!

Então você lembra

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

E você está de volta à estaca zero. Alguém tem grandes ideias além de armazenar uma lista de todos os TLDs?


Esta pergunta já foi feita aqui: Obtendo partes de uma edição de URL : Uma pergunta semelhante foi feita aqui
:)

Você esclarece o que você quer? Parece que você está atrás da parte do domínio "oficial" da URL (ou seja, domain.co.uk), independentemente de quantos rótulos DNS aparecem antes dela?
Alnitak

Não acho que seja a mesma pergunta - parece ser mais sobre os cortes administrativos no nome de domínio que não podem ser resolvidos apenas olhando para a string
Alnitak

Concordo. Expanda mais sobre o seu objetivo final.
BuddyJoe

Respostas:


73

Alguém tem grandes ideias além de armazenar uma lista de todos os TLDs?

Não, porque cada TLD difere no que conta como subdomínio, domínio de segundo nível, etc.

Lembre-se de que existem domínios de nível superior, domínios de segundo nível e subdomínios. Tecnicamente falando, tudo, exceto o TLD, é um subdomínio.

No exemplo domain.com.uk, "domínio" é um subdomínio, "com" é um domínio de segundo nível e "uk" é o TLD.

Portanto, a questão continua mais complexa do que à primeira vista e depende de como cada TLD é gerenciado. Você precisará de um banco de dados de todos os TLDs que incluem seu particionamento específico e o que conta como um domínio de segundo nível e um subdomínio. Não há muitos TLDs, entretanto, a lista é razoavelmente gerenciável, mas coletar todas essas informações não é trivial. Pode já haver essa lista disponível.

Parece que http://publicsuffix.org/ é uma dessas listas — todos os sufixos comuns (.com, .co.uk, etc) em uma lista adequada para pesquisa. Ainda não será fácil analisá-lo, mas pelo menos você não precisa manter a lista.

Um "sufixo público" é aquele em que os usuários da Internet podem registrar nomes diretamente. Alguns exemplos de sufixos públicos são ".com", ".co.uk" e "pvt.k12.wy.us". A Lista de sufixos públicos é uma lista de todos os sufixos públicos conhecidos.

A Lista Pública de Sufixos é uma iniciativa da Fundação Mozilla. Ele está disponível para uso em qualquer software, mas foi originalmente criado para atender às necessidades dos fabricantes de navegadores. Ele permite que os navegadores, por exemplo:

  • Evite "supercookies" que prejudicam a privacidade sendo definidos para sufixos de nomes de domínio de alto nível
  • Destaque a parte mais importante de um nome de domínio na interface do usuário
  • Classifique com precisão as entradas do histórico por site

Olhando a lista , você pode ver que não é um problema trivial. Acho que uma lista é a única maneira correta de fazer isso ...


A Mozilla possui um código que usa este serviço. O projeto foi desmembrado porque a especificação original do cookie vinculava os TLDs à confiança em cookies, mas nunca funcionou. O bug "Cookie Monster" foi o primeiro problema, e a arquitetura nunca foi corrigida ou substituída.
benc

O idioma preferido para resolver isso não está listado, mas há um projeto de código aberto que usa essa lista em código C # aqui: code.google.com/p/domainname-parser
Dan Esparza

Se um domínio é um "sufixo público" ou não, ele deve ser disponibilizado por meio do próprio protocolo DNS, talvez por meio de um sinalizador EDNS. Nesse caso, o proprietário pode defini-lo e não há necessidade de manter uma lista separada.
Pieter Ennes

@PieterEnnes EDNS é para sinalizadores "relacionados ao transporte" e não pode ser usado para metadados relacionados ao conteúdo. Eu concordo que essas informações seriam mais bem colocadas no próprio DNS. ISTR há planos para uma "sessão BoF" na próxima IETF em Vancouver para discutir isso.
Alnitak

26

Como diz Adam, não é fácil e, atualmente, a única forma prática é usar uma lista.

Mesmo assim, há exceções - por exemplo, em .ukhá um punhado de domínios que são válidos imediatamente naquele nível que não estão .co.uk, portanto, eles devem ser adicionados como exceções.

Atualmente é assim que os navegadores convencionais fazem isso - é necessário garantir que example.co.uknão seja possível definir um cookie para o .co.ukqual seria enviado para qualquer outro site em .co.uk.

A boa notícia é que já existe uma lista disponível em http://publicsuffix.org/ .

A IETF também está trabalhando para criar algum tipo de padrão para permitir que os TLDs declarem sua estrutura de domínio. No entanto, isso é um pouco complicado por nomes como .uk.com, que é operado como se fosse um sufixo público, mas não é vendido pelo .comregistro.


1
Eugh, o IETF deveria saber melhor do que deixar seus URLs morrerem. O projecto (última actualização em setembro de 2012) agora pode ser alcançado aqui: tools.ietf.org/html/draft-pettersen-subtld-structure
IMSOP

O grupo de trabalho da IETF sobre o assunto (DBOUND) foi encerrado.
Patrick Mevzek de

Observe que, desde que escrevi isso, o .ukregistro de domínio agora permite registros diretamente no segundo nível. Isso é refletido em conformidade no PSL.
Alnitak

22

Publicsuffix.org parece a maneira de fazer. Existem muitas implementações por aí para analisar o conteúdo do arquivo de dados publicsuffix facilmente:


2
Mas lembre-se de que não é apenas uma questão de análise! Esta lista em Publicsuffix.org é um projeto não oficial, que está incompleto (falta eu.org, por exemplo), NÃO reflete automaticamente as políticas do TLD e pode deixar de ser mantida a qualquer momento.
bortzmeyer


7
A lista em publicsuffix.org não é "não oficial" mais do que qualquer outra coisa que a Mozilla faz. Dado que Mozilla, Opera e Chrome o usam, é improvável que deixe de ser mantido. Quanto a estar incompleto, qualquer operador de um domínio como eu.org pode candidatar-se à inclusão, se assim o desejar, e compreende as consequências de o fazer. Se você quiser que um domínio seja adicionado, peça ao proprietário para se inscrever. Sim, ele não reflete automaticamente a política de TLDs, mas nada o faz - não existe uma fonte programática dessas informações.
Gervase Markham de

dagger / android: okhttp lhe dará topPrivateDomain
bladerunner

9

Como já foi dito por Adam e John publicsuffix.org é o caminho correto a seguir. Mas, se por algum motivo você não puder usar essa abordagem, aqui está uma heurística baseada em uma suposição que funciona para 99% de todos os domínios:

Há uma propriedade que distingue (não todos, mas quase todos) domínios "reais" de subdomínios e TLDs: o registro MX do DNS. Você pode criar um algoritmo que procure por isso: Remova as partes do nome do host, uma a uma, e consulte o DNS até encontrar um registro MX. Exemplo:

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

Aqui está um exemplo em 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");
}

É isso que a IETF também está sugerindo aqui ?
Ellie Kesselman

1
Até mesmo publicsuffix.org diz (veja o sexto parágrafo) que a maneira correta de fazer isso é por meio do DNS, exatamente como você disse em sua resposta!
Ellie Kesselman

1
Exceto que você pode ter um domínio completamente sem um registro MX. E que o algoritmo será enganado por registros curinga. E, no lado oposto, você tem TLDs que têm registros MX (como .aiou .axpara citar apenas alguns).
Patrick Mevzek

@patrick: Concordo totalmente; como eu disse na introdução, esse algoritmo não é à prova de balas, é apenas uma heurística que funciona surpreendentemente bem.
François Bourgeois

2

Como já foi dito, a Lista Pública de Sufixos é apenas uma maneira de analisar o domínio corretamente. Para PHP, você pode tentar TLDExtract . Aqui está o código de amostra:

$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

Acabei de escrever um programa para isso em clojure baseado nas informações de publicsuffix.org:

https://github.com/isaksky/url_dom

Por exemplo:

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

1

Para uma biblioteca C (com geração de tabela de dados em Python), escrevi http://code.google.com/p/domain-registry-provider/ que é rápido e economiza espaço.

A biblioteca usa ~ 30kB para as tabelas de dados e ~ 10kB para o código C. Não há sobrecarga de inicialização, pois as tabelas são construídas em tempo de compilação. Consulte http://code.google.com/p/domain-registry-provider/wiki/DesignDoc para obter mais detalhes.

Para entender melhor o código de geração de tabela (Python), comece aqui: http://code.google.com/p/domain-registry-provider/source/browse/trunk/src/registry_tables_generator/registry_tables_generator.py

Para entender melhor a API C, consulte: http://code.google.com/p/domain-registry-provider/source/browse/trunk/src/domain_registry/domain_registry.h


1
Também tenho uma biblioteca C / C ++ que tem sua própria lista, embora também seja verificada na lista publicsuffix.org. É chamado de libtld e funciona em Unix e MS-Windows snapwebsites.org/project/libtld
Alexis Wilke

0

Não está funcionando exatamente, mas talvez você possa obter uma resposta útil tentando buscar o domínio peça por peça e verificando a resposta, ou seja, buscar ' http: // uk ' e, em seguida, ' http://co.uk ' e, em seguida, ' http://domain.co.uk '. Quando você obtém uma resposta sem erro, você tem o domínio e o resto é o subdomínio.

Às vezes você só tem que tentar :)

Editar:

Tom Leys aponta nos comentários, que alguns domínios são configurados apenas no subdomínio www, o que nos daria uma resposta incorreta no teste acima. Bom ponto! Talvez a melhor abordagem seja verificar cada parte com ' http: // www ' e também com 'http: //', e contar um acerto para qualquer um como um acerto para essa seção do nome de domínio. Ainda estaríamos perdendo alguns arranjos 'alternativos', como 'web.domain.com', mas eu não encontrei um deles por um tempo :)


Não há garantia de que x.com aponta para um servidor da web na porta 80, mesmo que www.x.com o faça. www é um subdomínio válido neste caso. Talvez um whois automatizado ajude aqui.
Tom Leys de

Bom ponto! Um whois limparia tudo, embora manter uma lista de quais servidores whois usar e para qual nível tld / 2 significaria resolver o mesmo problema para casos extremos.
jTresidder de

você está assumindo que existe um servidor HTTP em todos os domínios
Francois Bourgeois

Não funcionará para .DKe alguns outros, como http://dk/funciona como está. Este tipo de heurística não é o caminho a seguir ...
Patrick Mevzek

0

Use o URIBuilder e depois divida o atributo URIBUilder.host em um array em "." agora você tem uma matriz com o domínio dividido.


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

Você pode usar esta lib tld.js: API JavaScript para trabalhar com nomes de domínio complexos, subdomínios e URIs.

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

Se você está obtendo domínio raiz no navegador. Você pode usar este 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;
    }
  }
};

Usar cookies é complicado.


0

Se você deseja extrair subdomínios e / ou domínios de uma lista arbitrária de URLs, este script python pode ser útil. Porém, tenha cuidado, não é perfeito. Este é um problema complicado de resolver em geral e é muito útil se você tiver uma lista de permissões de domínios que está esperando.

  1. Obtenha domínios de nível superior em publicsuffix.org
pedidos de importação

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

domínios = []
para linha em page.text.splitlines ():
    if line.startswith ('//'):
        continuar
    outro:
        domínio = line.strip ()
        se domínio:
            domínios.append (domínio)

domínios = [d [2:] if d.startswith ('*.') else d for d em domínios]
imprimir ('encontrado {} domínios'.formato (len (domínios)))
  1. Compilar regex
importar re

_regex = ''
para domínio em domínios:
    _regex + = r '{} |' .format (domain.replace ('.', '\.'))

subdomain_regex = r '/([^/]*)\.[^/.]+\.({})/.*$'. format (_regex)
domain_regex = r '([^ /.] + \. ({})) /.*$'. formato (_regex)
  1. Use regex na lista de URLs
FILE_NAME = '' # coloque o nome do arquivo CSV aqui
URL_COLNAME = '' # coloque o nome da coluna do URL aqui

importar pandas como pd

df = pd.read_csv (FILE_NAME)
urls = df [URL_COLNAME] .astype (str) + '/' # observação: adicionando / como um hack para ajudar a regex

df ['sub_domain_extracted'] = urls.str.extract (pat = subdomain_regex, expandir = True) [0]
df ['domain_extracted'] = urls.str.extract (pat = domain_regex, expandir = True) [0]

df.to_csv ('extract_domains.csv', index = False)

-1

Lista de sufixos comuns (.co.uk, .com etc.) para remover junto com http: // e você terá apenas "sub.domain" para trabalhar em vez de " http: // sub. domain.suffix ", ou pelo menos é o que eu provavelmente faria.

O maior problema é a lista de sufixos possíveis. Afinal, há muito.


-3

Tendo dado uma rápida olhada na lista publicsuffix.org, parece que você poderia fazer uma aproximação razoável removendo os três segmentos finais ("segmento" aqui significa uma seção entre dois pontos) de domínios onde o segmento final tem dois caracteres, assumindo que é um código de país e será subdividido posteriormente. Se o segmento final for "nós" e o penúltimo segmento também tiver dois caracteres, remova os quatro últimos segmentos. Em todos os outros casos, remova os dois segmentos finais. por exemplo:

"exemplo" não tem dois caracteres, então remova "domínio.exemplo", deixando "www"

"exemplo" não tem dois caracteres, então remova "domínio.exemplo", deixando "super.duper"

"uk" tem dois caracteres (mas não "nós"), então remova "domain.co.uk", deixando "super.duper"

"us" tem dois caracteres e é "us", mais "wy" também tem dois caracteres, então remova "pvt.k12.wy.us", deixando "foo".

Observe que, embora isso funcione para todos os exemplos que vi nas respostas até agora, permanece apenas uma aproximação razoável. Não é totalmente correto, embora eu suspeite que seja o mais próximo que você provavelmente chegará sem fazer / obter uma lista real para usar como referência.


3
Existem muitos casos de falha. Esse é o tipo de algoritmo que os navegadores costumam experimentar e usar. Não faça isso, use o PSL - ele funciona e existem bibliotecas para ajudá-lo.
Gervase Markham de

Nada proíbe os gTLDs de serem "segmentados" também, esse era o caso no início de, .NAMEpor exemplo, quando você podia comprar apenas firstname.lastname.namenomes de domínio. E na direção oposta, agora .UStambém é plano, então você pode x.y.z.whatever.uscomprá-lo apenas whatever.usno registro e seu algoritmo falhará nele.
Patrick Mevzek

1
Também sobre ("segmento" aqui significa uma seção entre dois pontos) : isso é chamado de rótulo no mundo DNS, não há necessidade de inventar um novo nome.
Patrick Mevzek
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.