Récupérer le sous-domaine à partir d'une URL

101

Obtenir le sous-domaine à partir d'une URL semble facile au début.

http://www.domain.example

Scannez pour la première période puis retournez ce qui est venu après le "http: //" ...

Alors tu te souviens

http://super.duper.domain.example

Oh. Alors vous pensez, d'accord, trouvez la dernière période, revenez en arrière et récupérez tout avant!

Alors tu te souviens

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

Et vous êtes de retour à la case départ. Quelqu'un a-t-il de bonnes idées en plus de stocker une liste de tous les TLD?

jb.
la source
Cette question a déjà été posée ici: Obtenir des parties d'une URL Edit: Une question similaire a été posée ici
:)
Cam vous clarifiez ce que vous voulez? Il semble que vous recherchiez la partie "officielle" du domaine de l'URL (c'est-à-dire domain.co.uk), quel que soit le nombre d'étiquettes DNS qui la précèdent?
Alnitak
Je ne pense pas que ce soit la même question - cela semble être plus à propos des coupes administratives dans le nom de domaine qui ne peuvent pas être résolues simplement en regardant la chaîne
Alnitak
Je suis d'accord. Développez davantage votre objectif final.
BuddyJoe
Voir cette réponse: stackoverflow.com/a/39307593/530553
Ehsan Chavoshi

Réponses:

73

Quelqu'un a-t-il de bonnes idées en plus de stocker une liste de tous les TLD?

Non, car chaque TLD diffère sur ce qui compte comme un sous-domaine, un domaine de deuxième niveau, etc.

Gardez à l'esprit qu'il existe des domaines de premier niveau, des domaines de second niveau et des sous-domaines. Techniquement parlant, tout sauf le TLD est un sous-domaine.

Dans l'exemple domain.com.uk, "domain" est un sous-domaine, "com" est un domaine de second niveau et "uk" est le TLD.

La question reste donc plus complexe qu'à première vue, et cela dépend de la manière dont chaque TLD est géré. Vous aurez besoin d'une base de données de tous les TLD qui incluent leur partitionnement particulier, et ce qui compte comme un domaine de deuxième niveau et un sous-domaine. Cependant, il n'y a pas trop de TLD, donc la liste est raisonnablement gérable, mais la collecte de toutes ces informations n'est pas anodine. Une telle liste est peut-être déjà disponible.

On dirait que http://publicsuffix.org/ est une de ces listes - tous les suffixes courants (.com, .co.uk, etc.) dans une liste adaptée à la recherche. Il ne sera toujours pas facile de l'analyser, mais au moins vous n'avez pas à maintenir la liste.

Un «suffixe public» est celui sous lequel les internautes peuvent enregistrer directement des noms. Quelques exemples de suffixes publics sont ".com", ".co.uk" et "pvt.k12.wy.us". La liste des suffixes publics est une liste de tous les suffixes publics connus.

La liste publique des suffixes est une initiative de la Fondation Mozilla. Il est disponible pour être utilisé dans n'importe quel logiciel, mais a été créé à l'origine pour répondre aux besoins des fabricants de navigateurs. Il permet aux navigateurs, par exemple:

  • Évitez de définir des "supercookies" nuisibles à la confidentialité pour les suffixes de nom de domaine de haut niveau
  • Mettez en évidence la partie la plus importante d'un nom de domaine dans l'interface utilisateur
  • Trier avec précision les entrées d'historique par site

En parcourant la liste , vous pouvez voir que ce n'est pas un problème trivial. Je pense qu'une liste est la seule façon correcte d'accomplir cela ...

Adam Davis
la source
Mozilla a du code qui utilise ce service. Le projet a été dérivé car la spécification de cookie d'origine avait lié les TLD à la confiance dans les cookies, mais n'a jamais fonctionné. Le bogue "Cookie Monster" a été le premier problème, et l'architecture n'a jamais été corrigée ou remplacée.
benc
La langue préférée pour résoudre ce problème n'est pas répertoriée, mais il existe un projet Open Source
Dan Esparza
Qu'un domaine soit un "suffixe public" ou non devrait vraiment être rendu disponible via le protocole DNS lui-même, peut-être via un drapeau EDNS. Dans ce cas, le propriétaire peut le définir et il n'est pas nécessaire de conserver une liste séparée.
Pieter Ennes
@PieterEnnes EDNS est pour les indicateurs «liés au transport» et ne peut pas être utilisé pour les métadonnées liées au contenu. Je conviens que ces informations seraient mieux placées dans le DNS lui-même. ISTR il y a des plans pour une "session BoF" au prochain IETF à Vancouver pour en discuter.
Alnitak
26

Comme Adam le dit, ce n'est pas facile, et actuellement le seul moyen pratique est d'utiliser une liste.

Même dans ce cas, il y a des exceptions - par exemple, .ukil y a une poignée de domaines qui sont valides immédiatement à ce niveau qui ne sont pas inclus .co.uk, donc ceux-ci doivent être ajoutés comme exceptions.

C'est actuellement ainsi que font les navigateurs traditionnels - il est nécessaire de s'assurer que example.co.ukvous ne pouvez pas définir un cookie pour .co.uklequel il serait ensuite envoyé à un autre site Web sous .co.uk.

La bonne nouvelle est qu'il existe déjà une liste disponible sur http://publicsuffix.org/ .

Il y a aussi du travail dans l' IETF pour créer une sorte de norme permettant aux TLD de déclarer à quoi ressemble la structure de leur domaine. Ceci est un peu compliqué par les goûts de .uk.com, qui fonctionne comme s'il s'agissait d'un suffixe public, mais n'est pas vendu par le .comregistre.

Alnitak
la source
1
Eugh, l'IETF devrait savoir mieux que de laisser mourir leurs URL. Le projet (mis à jour pour la dernière fois en septembre 2012) peut maintenant être atteint ici: tools.ietf.org/html/draft-pettersen-subtld-structure
IMSoP
Le groupe de travail de l'IETF sur le sujet (DBOUND) a été fermé.
Patrick Mevzek
Notez que depuis que j'ai écrit ceci, le .ukregistre de domaine autorise désormais les enregistrements directement au deuxième niveau. Cela se reflète en conséquence dans le PSL.
Alnitak
22

Publicsuffix.org semble la voie à suivre. Il existe de nombreuses implémentations pour analyser facilement le contenu du fichier de données publicuffix:

JohnTESlade
la source
2
Mais rappelez-vous que ce n'est pas seulement une question d'analyse! Cette liste sur Publicsuffix.org est un projet non officiel, qui est incomplet (eu.org est manquant, par exemple), ne reflète PAS automatiquement les politiques de TLD et peut devenir non maintenu à tout moment.
bortzmeyer
En outre, Ruby: github.com/weppos/public_suffix_service
hargneux
7
La liste sur publicsuffix.org n'est pas "non officielle", pas plus que tout ce que fait Mozilla. Étant donné que Mozilla, Opera et Chrome l'utilisent, il est peu probable qu'il ne soit plus maintenu. Quant à être incomplet, tout opérateur d'un domaine comme eu.org peut demander l'inclusion s'il le souhaite, et il en comprend les conséquences. Si vous souhaitez ajouter un domaine, demandez au propriétaire de postuler. Oui, cela ne reflète pas automatiquement la politique du TLD, mais rien ne le fait - il n'y a pas de source programmatique de ces informations.
Gervase Markham
dagger / android: okhttp vous donnera topPrivateDomain
bladerunner
9

Comme l'ont déjà dit Adam et John, publicuffix.org est la bonne voie à suivre. Mais, si pour une raison quelconque vous ne pouvez pas utiliser cette approche, voici une heuristique basée sur une hypothèse qui fonctionne pour 99% de tous les domaines:

Il y a une propriété qui distingue (pas tous, mais presque tous) les domaines «réels» des sous-domaines et des TLD et c'est l'enregistrement MX du DNS. Vous pouvez créer un algorithme qui recherche ceci: supprimez les parties du nom d'hôte une par une et interrogez le DNS jusqu'à ce que vous trouviez un enregistrement MX. Exemple:

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

Voici un exemple en 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");
}
François Bourgeois
la source
Est-ce ce que l'IETF suggère également ici ?
Ellie Kesselman
1
Même publicuffix.org dit (voir le sixième paragraphe) que la bonne façon de le faire est d'utiliser le DNS, comme vous l'avez dit dans votre réponse!
Ellie Kesselman
1
Sauf que vous pouvez complètement avoir un domaine sans enregistrement MX. Et que l'algorithme sera trompé par des enregistrements génériques. Et de l'autre côté, vous avez des TLD qui ont des enregistrements MX (comme .aiou .axpour n'en nommer que quelques-uns).
Patrick Mevzek
@patrick: Je suis totalement d'accord; comme je l'ai dit dans l'introduction, cet algorithme n'est pas à l'épreuve des balles, c'est juste une heuristique qui fonctionne étonnamment bien.
Francois Bourgeois
2

Comme déjà dit, la liste des suffixes publics n'est qu'un moyen d'analyser correctement le domaine. Pour PHP, vous pouvez essayer TLDExtract . Voici un exemple de code:

$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'
Oleksandr Fediashov
la source
1

Je viens d'écrire un programme pour cela en clojure basé sur les informations de publicsuffix.org:

https://github.com/isaksky/url_dom

Par exemple:

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

Pour une bibliothèque C (avec génération de table de données en Python), j'ai écrit http://code.google.com/p/domain-registry-provider/ qui est à la fois rapide et peu encombrant.

La bibliothèque utilise ~ 30 Ko pour les tables de données et ~ 10 Ko pour le code C. Il n'y a pas de surcharge de démarrage puisque les tables sont construites au moment de la compilation. Consultez http://code.google.com/p/domain-registry-provider/wiki/DesignDoc pour plus de détails.

Pour mieux comprendre le code de génération de table (Python), commencez ici: http://code.google.com/p/domain-registry-provider/source/browse/trunk/src/registry_tables_generator/registry_tables_generator.py

Pour mieux comprendre l'API C, consultez: http://code.google.com/p/domain-registry-provider/source/browse/trunk/src/domain_registry/domain_registry.h

Bryan McQuade
la source
1
J'ai aussi une bibliothèque C / C ++ qui a sa propre liste bien qu'elle soit également vérifiée par rapport à la liste publicuffix.org. Il s'appelle la libtld et fonctionne sous Unix et MS-Windows snapwebsites.org/project/libtld
Alexis Wilke
0

Cela ne fonctionne pas exactement, mais vous pourriez peut-être obtenir une réponse utile en essayant de récupérer le domaine morceau par morceau et en vérifiant la réponse, c'est-à-dire en récupérant ' http: // uk ', puis ' http://co.uk ' , puis " http://domain.co.uk ". Lorsque vous obtenez une réponse sans erreur, vous obtenez le domaine et le reste est un sous-domaine.

Parfois tu dois juste l'essayer :)

Éditer:

Tom Leys souligne dans les commentaires que certains domaines sont configurés uniquement sur le sous-domaine www, ce qui nous donnerait une réponse incorrecte dans le test ci-dessus. Bon point! Peut-être que la meilleure approche serait de vérifier chaque partie avec « http: // www » ainsi que «http: //», et compter un hit soit comme un hit pour cette section du nom de domaine? Il nous manquerait encore des arrangements `` alternatifs '' tels que `` web.domain.com '', mais je n'en ai pas rencontré depuis un moment :)

jTresidder
la source
Il n'y a aucune garantie que x.com pointe vers un serveur Web sur le port 80, même si www.x.com le fait. www est un sous-domaine valide dans ce cas. Peut-être qu'un whois automatisé aiderait ici.
Tom Leys
Bon point! Un whois le clarifierait, tout en conservant une liste des serveurs whois à utiliser pour lesquels pour quel niveau tld / 2nd signifierait résoudre le même problème pour les cas extrêmes.
jTresidder
vous supposez qu'il existe un serveur HTTP dans chaque domaine
Francois Bourgeois
Ne fonctionnera pas pour .DKet certains autres, tel http://dk/quel. Ce genre de heuristiques ne sont pas la voie à suivre ...
Patrick Mevzek
0

Utilisez URIBuilder puis récupérez l'attribut URIBUilder.host en le divisant en un tableau sur "." vous avez maintenant un tableau avec le domaine divisé.

jrr
la source
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;
}
Mike
la source
0

Vous pouvez utiliser cette lib tld.js: API JavaScript pour travailler sur des noms de domaine complexes, des sous-domaines et des URI.

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

Si vous obtenez le domaine racine dans le navigateur. Vous pouvez utiliser cette 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;
    }
  }
};

L'utilisation de cookies est délicate.

xiaoyu2er
la source
0

Si vous cherchez à extraire des sous-domaines et / ou des domaines d'une liste arbitraire d'URL, ce script python peut être utile. Attention cependant, ce n'est pas parfait. C'est un problème délicat à résoudre en général et il est très utile si vous avez une liste blanche de domaines que vous attendez.

  1. Obtenez des domaines de premier niveau sur publicsuffix.org
demandes d'importation

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

domaines = []
pour la ligne dans page.text.splitlines ():
    si line.startswith ('//'):
        continuer
    autre:
        domaine = line.strip ()
        si domaine:
            domaines.append (domaine)

domaines = [d [2:] si d.startswith ('*.') else d pour d dans les domaines]
print ('found {} domaines'.format (len (domaines)))
  1. Construire une regex
importer re

_regex = ''
pour le domaine dans les domaines:
    _regex + = r '{} |' .format (domaine.regex ('.', '\.'))

subdomain_regex = r '/([^/ </font>*)\.[^/. </font>+\.({})/.*$'. format (_regex)
domain_regex = r '([^ /.] + \. ({})) /.*$'. format (_regex)
  1. Utiliser l'expression régulière sur la liste des URL
FILE_NAME = '' # mettre le nom du fichier CSV ici
URL_COLNAME = '' # mettre le nom de la colonne URL ici

importer des pandas en tant que pd

df = pd.read_csv (FILE_NAME)
urls = df [URL_COLNAME] .astype (str) + '/' # note: ajouter / comme un hack pour aider regex

df ['sous_domaine_extracté'] = urls.str.extract (pat = subdomain_regex, expand = True) [0]
df ['domaine_extracté'] = urls.str.extract (pat = domain_regex, expand = True) [0]

df.to_csv ('domaines_extraits.csv', index = Faux)
AlexG
la source
-1

Liste des suffixes courants (.co.uk, .com, et cetera) à supprimer avec http: // et vous n'aurez plus que "sub.domain" pour travailler au lieu de " http: // sub. domain.suffix ", ou du moins c'est ce que je ferais probablement.

Le plus gros problème est la liste des suffixes possibles. Il y en a beaucoup, après tout.

Peter C.
la source
-3

Après avoir jeté un coup d'œil à la liste publicuffix.org, il semble que vous puissiez faire une approximation raisonnable en supprimant les trois derniers segments ("segment" signifiant ici une section entre deux points) des domaines où le segment final est long de deux caractères, en supposant qu'il s'agit d'un code de pays et qu'il sera subdivisé ultérieurement. Si le dernier segment est "nous" et que l'avant-dernier segment est également composé de deux caractères, supprimez les quatre derniers segments. Dans tous les autres cas, supprimez les deux derniers segments. par exemple:

"exemple" n'est pas composé de deux caractères, supprimez donc "domain.example" en laissant "www"

"exemple" n'est pas composé de deux caractères, supprimez donc "domain.example" en laissant "super.duper"

"uk" est composé de deux caractères (mais pas "nous"), supprimez donc "domain.co.uk" en laissant "super.duper"

"nous" est composé de deux caractères et "nous", plus "wy" correspond également à deux caractères, supprimez donc "pvt.k12.wy.us" en laissant "foo".

Notez que, bien que cela fonctionne pour tous les exemples que j'ai vus dans les réponses jusqu'à présent, cela ne reste qu'une approximation raisonnable. Ce n'est pas tout à fait correct, même si je soupçonne que c'est à peu près aussi proche que vous pourriez l'obtenir sans faire / obtenir une liste réelle à utiliser comme référence.

Dave Sherohman
la source
3
Il existe de nombreux cas d'échec. C'est le genre de navigateurs d'algorithmes utilisés pour essayer et utiliser. Ne faites pas cela, utilisez le PSL - cela fonctionne, et il existe des bibliothèques pour vous aider.
Gervase Markham
Rien n'interdit aux gTLD d'être «segmentés» également, c'était le cas au début .NAMEpar exemple, lorsque vous ne pouviez acheter que des firstname.lastname.namenoms de domaine. Et dans la direction opposée, maintenant .USest également plat, donc vous pouvez avoir x.y.z.whatever.ussimplement en achetant whatever.usau registre et votre algorithme échouera.
Patrick Mevzek
1
Aussi à propos ("segment" signifiant ici une section entre deux points) : cela s'appelle une étiquette dans le monde DNS, pas besoin d'inventer un nouveau nom.
Patrick Mevzek