从字符串中提取主机名

2022-08-29 23:55:14

我只想匹配URL的根目录,而不是文本字符串中的整个URL。鉴于:

http://www.youtube.com/watch?v=ClkQA2Lb_iE
http://youtu.be/ClkQA2Lb_iE
http://www.example.com/12xy45
http://example.com/random

我想让最后 2 个实例解析为 or 域。www.example.comexample.com

我听说正则表达式很慢,这将是我在页面上的第二个正则表达式,所以如果没有正则表达式,请告诉我。

我正在寻找此解决方案的JS / jQuery版本。


答案 1

一个不使用正则表达式的巧妙技巧:

var tmp        = document.createElement ('a');
;   tmp.href   = "http://www.example.com/12xy45";

// tmp.hostname will now contain 'www.example.com'
// tmp.host will now contain hostname and port 'www.example.com:80'

将上述内容包装在如下所示的函数中,您自己就有了从URI中抢夺域部分的绝佳方法。

function url_domain(data) {
  var    a      = document.createElement('a');
         a.href = data;
  return a.hostname;
}

答案 2

我建议使用npm包psl(公共后缀列表)。“公共后缀列表”是所有有效域后缀和规则的列表,不仅仅是国家代码顶级域,还包括被视为根域的 unicode 字符(即 www.食狮.公司.cn、b.c.kobe.jp 等)。在此处阅读更多相关信息。

尝试:

npm install --save psl

然后用我的“extractHostname”实现运行:

let psl = require('psl');
let url = 'http://www.youtube.com/watch?v=ClkQA2Lb_iE';
psl.get(extractHostname(url)); // returns youtube.com

我不能使用npm包,所以下面只测试提取Hostname。

function extractHostname(url) {
  var hostname;
  //find & remove protocol (http, ftp, etc.) and get hostname

  if (url.indexOf("//") > -1) {
    hostname = url.split('/')[2];
  } else {
    hostname = url.split('/')[0];
  }

  //find & remove port number
  hostname = hostname.split(':')[0];
  //find & remove "?"
  hostname = hostname.split('?')[0];

  return hostname;
}

// Warning: you can use this function to extract the "root" domain, but it will not be as accurate as using the psl package.

function extractRootDomain(url) {
  var domain = extractHostname(url),
  splitArr = domain.split('.'),
  arrLen = splitArr.length;

  //extracting the root domain here
  //if there is a subdomain
  if (arrLen > 2) {
    domain = splitArr[arrLen - 2] + '.' + splitArr[arrLen - 1];
    //check to see if it's using a Country Code Top Level Domain (ccTLD) (i.e. ".me.uk")
    if (splitArr[arrLen - 2].length == 2 && splitArr[arrLen - 1].length == 2) {
      //this is using a ccTLD
      domain = splitArr[arrLen - 3] + '.' + domain;
    }
  }
  return domain;
}

const urlHostname = url => {
  try {
    return new URL(url).hostname;
  }
  catch(e) { return e; }
};

const urls = [
    "http://www.blog.classroom.me.uk/index.php",
    "http://www.youtube.com/watch?v=ClkQA2Lb_iE",
    "https://www.youtube.com/watch?v=ClkQA2Lb_iE",
    "www.youtube.com/watch?v=ClkQA2Lb_iE",
    "ftps://ftp.websitename.com/dir/file.txt",
    "websitename.com:1234/dir/file.txt",
    "ftps://websitename.com:1234/dir/file.txt",
    "example.com?param=value",
    "https://facebook.github.io/jest/",
    "//youtube.com/watch?v=ClkQA2Lb_iE",
    "www.食狮.公司.cn",
    "b.c.kobe.jp",
    "a.d.kyoto.or.jp",
    "http://localhost:4200/watch?v=ClkQA2Lb_iE"
];

const test = (method, arr) => console.log(
`=== Testing "${method.name}" ===\n${arr.map(url => method(url)).join("\n")}\n`);

test(extractHostname, urls);
test(extractRootDomain, urls);
test(urlHostname, urls);

无论具有协议甚至端口号,您都可以提取域。这是一个非常简化的非正则表达式解决方案,所以我认为这就可以了。

URL(url).hostname是一个有效的解决方案,但它不适用于我已经解决的一些边缘情况。正如您在我上次测试中看到的那样,它不喜欢某些URL。不过,您绝对可以使用我的解决方案的组合来使其全部工作。

*感谢@Timmerz,@renoirb,@rineez,@BigDong,@ra00l,@ILikeBeansTacos,@CharlesRobertson的建议!@ross-allen,感谢您报告该错误!