Ich habe eine URL, die eines der folgenden Formate haben kann:
http://example.com
https://example.com
http://example.com/foo
http://example.com/foo/bar
www.example.com
example.com
foo.example.com
www.foo.example.com
foo.bar.example.com
http://foo.bar.example.com/foo/bar
example.net/foo/bar
Im Wesentlichen muss ich in der Lage sein, jede normale URL zu finden. Wie kann ich example.com
(oder .net, was auch immer das tld ist. Ich brauche das, um mit jeder TLD zu arbeiten.) Aus allen diesen Dateien über eine einzige Regex extrahieren?
Nun, Sie können parse_url
verwenden, um den Host zu erhalten:
$info = parse_url($url);
$Host = $info['Host'];
Dann können Sie einige tolle Sachen machen, um nur die TLD und den Host zu erhalten
$Host_names = explode(".", $Host);
$bottom_Host_name = $Host_names[count($Host_names)-2] . "." . $Host_names[count($Host_names)-1];
Nicht sehr elegant, sollte aber funktionieren.
Wenn Sie eine Erklärung wünschen, hier geht es:
Zuerst greifen wir alles zwischen dem Schema (http://
usw.) auf, indem wir die Funktionen von parse_url
verwenden, um ... naja ... URLs zu analysieren. :)
Dann nehmen wir den Hostnamen und trennen ihn in ein Array, basierend auf dem Ort, an dem die Perioden liegen, und test.world.hello.myname
würde zu:
array("test", "world", "hello", "myname");
Danach nehmen wir die Anzahl der Elemente im Array (4).
Dann subtrahieren wir 2 von ihm, um die vorletzte Zeichenfolge zu erhalten (den Hostnamen oder example
in Ihrem Beispiel).
Dann subtrahieren wir 1 von ihm, um die letzte Zeichenfolge zu erhalten (weil Array-Schlüssel bei 0 beginnen), die auch als TLD bezeichnet wird
Dann kombinieren wir diese beiden Teile mit einem Punkt und Sie haben Ihren Basis-Hostnamen.
Meine Lösung in https://Gist.github.com/pocesar/5366899
und die Tests sind hier http://codepad.viper-7.com/GAh1tP
Es funktioniert mit jeder TLD und abscheulichen Subdomain-Mustern (bis zu 3 Subdomains).
Es gibt einen Test mit vielen Domainnamen.
Fügt die Funktion hier nicht ein, da der Code in StackOverflow seltsamerweise eingerückt ist (Code-Blöcke wie "github" könnten abgeschnitten sein).
Es ist nicht möglich, den Domänennamen zu erhalten, ohne eine TLD-Liste zu verwenden, um zu vergleichen, da es viele Fälle gibt, deren Struktur und Länge völlig gleich sind:
Die öffentliche Suffixliste von Mozilla sollte die beste Option sein, da sie von allen major-Browsern verwendet wird:
https://publicsuffix.org/list/public_suffix_list.dat
Fühlen Sie sich frei, meine Funktion zu nutzen:
function tld_list($cache_dir=null) {
// we use "/tmp" if $cache_dir is not set
$cache_dir = isset($cache_dir) ? $cache_dir : sys_get_temp_dir();
$lock_dir = $cache_dir . '/public_suffix_list_lock/';
$list_dir = $cache_dir . '/public_suffix_list/';
// refresh list all 30 days
if (file_exists($list_dir) && @filemtime($list_dir) + 2592000 > time()) {
return $list_dir;
}
// use exclusive lock to avoid race conditions
if (!file_exists($lock_dir) && @mkdir($lock_dir)) {
// read from source
$list = @fopen('https://publicsuffix.org/list/public_suffix_list.dat', 'r');
if ($list) {
// the list is older than 30 days so delete everything first
if (file_exists($list_dir)) {
foreach (glob($list_dir . '*') as $filename) {
unlink($filename);
}
rmdir($list_dir);
}
// now set list directory with new timestamp
mkdir($list_dir);
// read line-by-line to avoid high memory usage
while ($line = fgets($list)) {
// skip comments and empty lines
if ($line[0] == '/' || !$line) {
continue;
}
// remove wildcard
if ($line[0] . $line[1] == '*.') {
$line = substr($line, 2);
}
// remove exclamation mark
if ($line[0] == '!') {
$line = substr($line, 1);
}
// reverse TLD and remove linebreak
$line = implode('.', array_reverse(explode('.', (trim($line)))));
// we split the TLD list to reduce memory usage
touch($list_dir . $line);
}
fclose($list);
}
@rmdir($lock_dir);
}
// repair locks (should never happen)
if (file_exists($lock_dir) && mt_Rand(0, 100) == 0 && @filemtime($lock_dir) + 86400 < time()) {
@rmdir($lock_dir);
}
return $list_dir;
}
function get_domain($url=null) {
// obtain location of public suffix list
$tld_dir = tld_list();
// no url = our own Host
$url = isset($url) ? $url : $_SERVER['SERVER_NAME'];
// add missing scheme ftp:// http:// ftps:// https://
$url = !isset($url[5]) || ($url[3] != ':' && $url[4] != ':' && $url[5] != ':') ? 'http://' . $url : $url;
// remove "/path/file.html", "/:80", etc.
$url = parse_url($url, PHP_URL_Host);
// replace absolute domain name by relative (http://www.dns-sd.org/TrailingDotsInDomainNames.html)
$url = trim($url, '.');
// check if TLD exists
$url = explode('.', $url);
$parts = array_reverse($url);
foreach ($parts as $key => $part) {
$tld = implode('.', $parts);
if (file_exists($tld_dir . $tld)) {
return !$key ? '' : implode('.', array_slice($url, $key - 1));
}
// remove last part
array_pop($parts);
}
return '';
}
Was es besonders macht:
get_domain()
nur durch file_exists()
prüfen muss, ob sie vorhanden ist, sodass keine große Datenbank in jede Anforderung eingefügt werden muss, wie TLDExtract .Prüfung:
$urls = array(
'http://www.example.com',// example.com
'http://subdomain.example.com',// example.com
'http://www.example.uk.com',// example.uk.com
'http://www.example.co.uk',// example.co.uk
'http://www.example.com.ac',// example.com.ac
'http://example.com.ac',// example.com.ac
'http://www.example.accident-prevention.aero',// example.accident-prevention.aero
'http://www.example.sub.ar',// sub.ar
'http://www.congresodelalengua3.ar',// congresodelalengua3.ar
'http://congresodelalengua3.ar',// congresodelalengua3.ar
'http://www.example.pvt.k12.ma.us',// example.pvt.k12.ma.us
'http://www.example.lib.wy.us',// example.lib.wy.us
'com',// empty
'.com',// empty
'http://big.uk.com',// big.uk.com
'uk.com',// empty
'www.uk.com',// www.uk.com
'.uk.com',// empty
'stackoverflow.com',// stackoverflow.com
'.foobarfoo',// empty
'',// empty
false,// empty
' ',// empty
1,// empty
'a',// empty
);
Aktuelle Version mit Erklärungen (deutsch):
http://www.programmierer-forum.de/domainnamen-ermitteln-t244185.htm
$onlyHostName = implode('.', array_slice(explode('.', parse_url($link, PHP_URL_Host)), -2));
Ich denke, der beste Weg, um dieses Problem zu lösen, ist:
$second_level_domains_regex = '/\.asn\.au$|\.com\.au$|\.net\.au$|\.id\.au$|\.org\.au$|\.edu\.au$|\.gov\.au$|\.csiro\.au$|\.act\.au$|\.nsw\.au$|\.nt\.au$|\.qld\.au$|\.sa\.au$|\.tas\.au$|\.vic\.au$|\.wa\.au$|\.co\.at$|\.or\.at$|\.priv\.at$|\.ac\.at$|\.avocat\.fr$|\.aeroport\.fr$|\.veterinaire\.fr$|\.co\.hu$|\.film\.hu$|\.lakas\.hu$|\.ingatlan\.hu$|\.sport\.hu$|\.hotel\.hu$|\.ac\.nz$|\.co\.nz$|\.geek\.nz$|\.gen\.nz$|\.kiwi\.nz$|\.maori\.nz$|\.net\.nz$|\.org\.nz$|\.school\.nz$|\.cri\.nz$|\.govt\.nz$|\.health\.nz$|\.iwi\.nz$|\.mil\.nz$|\.parliament\.nz$|\.ac\.za$|\.gov\.za$|\.law\.za$|\.mil\.za$|\.nom\.za$|\.school\.za$|\.net\.za$|\.co\.uk$|\.org\.uk$|\.me\.uk$|\.ltd\.uk$|\.plc\.uk$|\.net\.uk$|\.sch\.uk$|\.ac\.uk$|\.gov\.uk$|\.mod\.uk$|\.mil\.uk$|\.nhs\.uk$|\.police\.uk$/';
$domain = $_SERVER['HTTP_Host'];
$domain = explode('.', $domain);
$domain = array_reverse($domain);
if (preg_match($second_level_domains_regex, $_SERVER['HTTP_Host']) {
$domain = "$domain[2].$domain[1].$domain[0]";
} else {
$domain = "$domain[1].$domain[0]";
}
Ich empfehle die Verwendung von TLDExtract library für alle Operationen mit Domainnamen.
Es gibt zwei Möglichkeiten, eine Subdomain von einem Host zu extrahieren:
Die erste Methode, die genauer ist, besteht darin, eine Datenbank mit tlds (wie public_suffix_list.dat ) zu verwenden und die Domäne damit abzugleichen. Dies ist in manchen Fällen etwas schwer. Es gibt einige PHP - Klassen für die Verwendung wie php-domain-parser und TLDExtract .
Der zweite Weg ist nicht so genau wie der erste, aber er ist sehr schnell und kann in vielen Fällen die richtige Antwort geben. Ich habe diese Funktion dafür geschrieben:
function get_domaininfo($url) {
// regex can be replaced with parse_url
preg_match("/^(https|http|ftp):\/\/(.*?)\//", "$url/" , $matches);
$parts = explode(".", $matches[2]);
$tld = array_pop($parts);
$Host = array_pop($parts);
if ( strlen($tld) == 2 && strlen($Host) <= 3 ) {
$tld = "$Host.$tld";
$Host = array_pop($parts);
}
return array(
'protocol' => $matches[1],
'subdomain' => implode(".", $parts),
'domain' => "$Host.$tld",
'Host'=>$Host,'tld'=>$tld
);
}
Beispiel:
print_r(get_domaininfo('http://mysubdomain.domain.co.uk/index.php'));
Kehrt zurück:
Array
(
[protocol] => https
[subdomain] => mysubdomain
[domain] => domain.co.uk
[Host] => domain
[tld] => co.uk
)
Hier ist eine Funktion, die ich geschrieben habe, um die Domain ohne Subdomain (s) zu packen, unabhängig davon, ob die Domain eine ccTLD oder eine neue TLD mit langem Stil usw. verwendet ... Es gibt keine Suche oder ein großes Array bekannter TLDs und es gibt keinen Regex . Mit dem ternären Operator und der Verschachtelung kann es viel kürzer sein, aber ich habe es aus Gründen der Lesbarkeit erweitert.
// Per Wikipedia: "All ASCII ccTLD identifiers are two letters long,
// and all two-letter top-level domains are ccTLDs."
function topDomainFromURL($url) {
$url_parts = parse_url($url);
$domain_parts = explode('.', $url_parts['Host']);
if (strlen(end($domain_parts)) == 2 ) {
// ccTLD here, get last three parts
$top_domain_parts = array_slice($domain_parts, -3);
} else {
$top_domain_parts = array_slice($domain_parts, -2);
}
$top_domain = implode('.', $top_domain_parts);
return $top_domain;
}
Hier ist eine Funktion, die für alle Domänen geeignet ist, einschließlich Domänen mit Second-Level-Domänen wie "co.uk".
function strip_subdomains($url){
# credits to gavingmiller for maintaining this list
$second_level_domains = file_get_contents("https://raw.githubusercontent.com/gavingmiller/second-level-domains/master/SLDs.csv");
# presume sld first ...
$possible_sld = implode('.', array_slice(explode('.', $url), -2));
# and then verify it
if (strpos($second_level_domains, $possible_sld)){
return implode('.', array_slice(explode('.', $url), -3));
} else {
return implode('.', array_slice(explode('.', $url), -2));
}
}
Sieht aus, als gäbe es hier eine doppelte Frage: delete-subdomain-from-url-string-if-subdomain-is-found
echo getDomainOnly("http://example.com/foo/bar");
function getDomainOnly($Host){
$Host = strtolower(trim($Host));
$Host = ltrim(str_replace("http://","",str_replace("https://","",$Host)),"www.");
$count = substr_count($Host, '.');
if($count === 2){
if(strlen(explode('.', $Host)[1]) > 3) $Host = explode('.', $Host, 2)[1];
} else if($count > 2){
$Host = getDomainOnly(explode('.', $Host, 2)[1]);
}
$Host = explode('/',$Host);
return $Host[0];
}
Ich hatte Probleme mit der von pocesar bereitgestellten Lösung. Wenn ich beispielsweise subdomain.domain.nl verwende, würde es keine domain.nl zurückgeben. Stattdessen würde subdomain.domain.nl .__ zurückgegeben werden. Ein weiteres Problem war, dass domain.com.br com.br zurückgibt
Ich bin nicht sicher, aber ich habe diese Probleme mit dem folgenden Code behoben (ich hoffe, es hilft jemandem, wenn ich ein glücklicher Mann bin):
function get_domain($domain, $debug = false){
$original = $domain = strtolower($domain);
if (filter_var($domain, FILTER_VALIDATE_IP)) {
return $domain;
}
$debug ? print('<strong style="color:green">»</strong> Parsing: '.$original) : false;
$arr = array_slice(array_filter(explode('.', $domain, 4), function($value){
return $value !== 'www';
}), 0); //rebuild array indexes
if (count($arr) > 2){
$count = count($arr);
$_sub = explode('.', $count === 4 ? $arr[3] : $arr[2]);
$debug ? print(" (parts count: {$count})") : false;
if (count($_sub) === 2){ // two level TLD
$removed = array_shift($arr);
if ($count === 4){ // got a subdomain acting as a domain
$removed = array_shift($arr);
}
$debug ? print("<br>\n" . '[*] Two level TLD: <strong>' . join('.', $_sub) . '</strong> ') : false;
}elseif (count($_sub) === 1){ // one level TLD
$removed = array_shift($arr); //remove the subdomain
if (strlen($arr[0]) === 2 && $count === 3){ // TLD domain must be 2 letters
array_unshift($arr, $removed);
}elseif(strlen($arr[0]) === 3 && $count === 3){
array_unshift($arr, $removed);
}else{
// non country TLD according to IANA
$tlds = array(
'aero',
'arpa',
'asia',
'biz',
'cat',
'com',
'coop',
'edu',
'gov',
'info',
'jobs',
'mil',
'mobi',
'museum',
'name',
'net',
'org',
'post',
'pro',
'tel',
'travel',
'xxx',
);
if (count($arr) > 2 && in_array($_sub[0], $tlds) !== false){ //special TLD don't have a country
array_shift($arr);
}
}
$debug ? print("<br>\n" .'[*] One level TLD: <strong>'.join('.', $_sub).'</strong> ') : false;
}else{ // more than 3 levels, something is wrong
for ($i = count($_sub); $i > 1; $i--){
$removed = array_shift($arr);
}
$debug ? print("<br>\n" . '[*] Three level TLD: <strong>' . join('.', $_sub) . '</strong> ') : false;
}
}elseif (count($arr) === 2){
$arr0 = array_shift($arr);
if (strpos(join('.', $arr), '.') === false && in_array($arr[0], array('localhost','test','invalid')) === false){ // not a reserved domain
$debug ? print("<br>\n" .'Seems invalid domain: <strong>'.join('.', $arr).'</strong> re-adding: <strong>'.$arr0.'</strong> ') : false;
// seems invalid domain, restore it
array_unshift($arr, $arr0);
}
}
$debug ? print("<br>\n".'<strong style="color:gray">«</strong> Done parsing: <span style="color:red">' . $original . '</span> as <span style="color:blue">'. join('.', $arr) ."</span><br>\n") : false;
return join('.', $arr);
}
Probieren Sie einfach folgendes aus:
preg_match('/(www.)?([^.]+\.[^.]+)$/', $yourHost, $matches);
echo "domain name is: {$matches[0]}\n";
dies funktioniert für die Mehrheit der Domänen.