如何在javascript中将URL解析为主机名和路径?

510

我想要获取一个字符串

var a = "http://example.com/aa/bb/"

将其处理成一个对象

a.hostname == "example.com"

a.pathname == "/aa/bb"

14
如果您正在处理当前的URL,您可以直接从location对象中访问hostnamepathname - rvighne
1
“lastPathPart”是什么意思? - Victor
不是正则表达式,而是Python模块tldextract可以完美实现此功能:https://github.com/john-kurkowski/tldextract - Oliver Oliver
26个回答

603

62
实验性技术:IE 不支持!https://developer.mozilla.org/zh-CN/docs/Web/API/URL/URL#Browser_compatibility - cwouter
14
它在 Edge 中能够工作,取代了 IE。 - rvighne
4
这是正确的做法,Edge浏览器已经比IE高出3个版本,所以这不重要。 - Claudiu Creanga
4
@justingordon说,URL类是Web标准,因此非浏览器应用程序不需要实现它。然而,最近版本的Nodejs提供了一个完全相同的库,require('url').URL - rvighne
15
JavaScript没有内置用于在浏览器或服务器上解析URL的方法,这一事实相当令人遗憾。 - Skitterm
显示剩余9条评论

383
var getLocation = function(href) {
    var l = document.createElement("a");
    l.href = href;
    return l;
};
var l = getLocation("http://example.com/path");
console.debug(l.hostname)
>> "example.com"
console.debug(l.pathname)
>> "/path"

15
你确定这是一个跨浏览器兼容的解决方案吗? - cllpse
76
需要注意的是,尽管这可能有助于/回答原帖作者的问题,但这个答案只适用于在浏览器中进行JS工作的人,因为它依赖于DOM来完成其工作。 - Adam Batkin
5
另一个简单性和机智并存的例子。 - Saeed Neamati
28
如果 href 是相对路径,则在 IE 中不起作用。l.hostname 会为空。如果只提供完整的 URL,则这将正常工作。 - Derek Prior
7
即使使用绝对URL,IE(在IE 11中测试)的行为与Chrome和Firefox不同。IE的“pathname”会删去前导斜杠,而其他浏览器则不会。因此,您将在浏览器中得到/pathpath,具体取决于您使用的浏览器。 - TrueWill
显示剩余13条评论

332

可以在这里找到:https://gist.github.com/jlong/2428561

var parser = document.createElement('a');
parser.href = "http://example.com:3000/pathname/?search=test#hash";

parser.protocol; // => "http:"
parser.host;     // => "example.com:3000"
parser.hostname; // => "example.com"
parser.port;     // => "3000"
parser.pathname; // => "/pathname/"
parser.hash;     // => "#hash"
parser.search;   // => "?search=test"
parser.origin;   // => "http://example.com:3000"

11
请注意,如果您只想获取当前浏览器位置的解析部分,则第一和第二行变为 parser = location;,并且所有后续行都可以正常工作。我刚在 Chrome 和 IE9 中尝试过了。 - Lee Meador
9
请注意,在IE浏览器中,pathname不包括前导斜杠。想想吧。:D - nevelis
3
对于IE浏览器,请使用"/" + 解析器的路径名。 - sbose
主机名实际上包括协议。在最新版本的Chrome上进行测试。 - Johann
IE11将SSL端口附加到“host”属性“google.com:443”,可能会导致不一致性。 - Andy Polhill
显示剩余5条评论

135

这是一个使用正则表达式模拟标签行为的简单函数。

优点

  • 可预测的行为(没有跨浏览器问题)
  • 不需要DOM
  • 非常简短。

缺点

  • 正则表达式有点难以阅读。

-

function getLocation(href) {
    var match = href.match(/^(https?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)([\/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/);
    return match && {
        href: href,
        protocol: match[1],
        host: match[2],
        hostname: match[3],
        port: match[4],
        pathname: match[5],
        search: match[6],
        hash: match[7]
    }
}
getLocation("http://example.com/");
/*
{
    "protocol": "http:",
    "host": "example.com",
    "hostname": "example.com",
    "port": undefined,
    "pathname": "/"
    "search": "",
    "hash": "",
}
*/

getLocation("http://example.com:3000/pathname/?search=test#hash");
/*
{
    "protocol": "http:",
    "host": "example.com:3000",
    "hostname": "example.com",
    "port": "3000",
    "pathname": "/pathname/",
    "search": "?search=test",
    "hash": "#hash"
}
*/

编辑:

以下是正则表达式的分解

var reURLInformation = new RegExp([
    '^(https?:)//', // protocol
    '(([^:/?#]*)(?::([0-9]+))?)', // host (hostname and port)
    '(/{0,1}[^?#]*)', // pathname
    '(\\?[^#]*|)', // search
    '(#.*|)$' // hash
].join(''));
var match = href.match(reURLInformation);

4
不能使用任何相对URL。在制作正则表达式时,是否遵循了RFC-3986规范?
getLocation("//example.com/"); null getLocation("/pathname/?search"); null getLocation("/pathname/"); null getLocation("relative"); null
- gregers
2
我喜欢这个不使用DOM的方法,但是Gregers说得很好。如果它能处理相对路径就更好了。这将需要使用window.location(一个a元素)来填充空白并添加代码。在这种情况下,该方法将变得矛盾。除非有其他替代方案,否则不确定如何完美解决这个问题。 - Turbo
添加了 href 键和原始 URL,这样可以在返回对象与 DOM 实现之间提供一致性。 - mattdlockyer
2
如果有人需要解析相对URL,这里是更新后的正则表达式: /^(?:(https?:)//)?(([^:/?#])(?::([0-9]+))?)([/]{0,1}[^?#])(?[^#]|)(#.|)$/ - shlensky

106
var loc = window.location;  // => "http://example.com:3000/pathname/?search=test#hash"

返回当前网址 currentUrl。

如果您想将自己的字符串作为 url 传递(不适用于 IE11):

var loc = new URL("http://example.com:3000/pathname/?search=test#hash")

然后你可以像这样解析它:

loc.protocol; // => "http:"
loc.host;     // => "example.com:3000"
loc.hostname; // => "example.com"
loc.port;     // => "3000"
loc.pathname; // => "/pathname/"
loc.hash;     // => "#hash"
loc.search;   // => "?search=test"

66

freddiefujiwara的答案非常好,但我还需要在Internet Explorer中支持相对URL。 我想出了以下解决方案:

function getLocation(href) {
    var location = document.createElement("a");
    location.href = href;
    // IE doesn't populate all link properties when setting .href with a relative URL,
    // however .href will return an absolute URL which then can be used on itself
    // to populate these additional fields.
    if (location.host == "") {
      location.href = location.href;
    }
    return location;
};

现在使用它来获取所需的属性:
var a = getLocation('http://example.com/aa/bb/');
document.write(a.hostname);
document.write(a.pathname);

例子:

function getLocation(href) {
  var location = document.createElement("a");
  location.href = href;
  // IE doesn't populate all link properties when setting .href with a relative URL,
  // however .href will return an absolute URL which then can be used on itself
  // to populate these additional fields.
  if (location.host == "") {
    location.href = location.href;
  }
  return location;
};
var urlToParse = 'http://example.com/aa/bb/',
  a = getLocation(urlToParse);
document.write('Absolute URL: ' + urlToParse);
document.write('<br />');
document.write('Hostname: ' + a.hostname);
document.write('<br />');
document.write('Pathname: ' + a.pathname);


4
这应该是被接受的答案。非常聪明地使用了相对到绝对URL处理。+1 - L0j1k
显然,这不是第一次JSFiddle链接失效了:https://dev59.com/5YLba4cB1Zd3GeqPcEJl - Claus
3
这个方法很棒,但我有一个更新,希望能帮助其他人。我正在使用这个方法来检查postMessage请求的来源,在端口是默认端口(80或443)时,它不会被附加到路径中。我在创建URL时进行了条件检查: var locationHost = (location.port !== '80' && location.port !== '443') ? location.host : location.hostname; var locationOrigin = location.protocol + '//' + locationHost; - Bobby Oster
3
我之前在这个解决方案的另一个更受欢迎的版本中发表了评论,但由于这是我最喜欢的解决方案,所以我想在这里重复一下。在IE11中,如果href中有用户名,所有这些属性读取都会引发安全错误。例如:"http://www.example.com"可以正常工作。但是"http://username@www.example.com"或"http://username:password@www.example.com"将导致任何尝试引用锚元素的其他属性(例如:哈希)失败并抛出一个恼人的错误。 - Clippy
绝妙的解决方案! - Christian Haller

18

js-uri(可以在Google Code上找到)接受一个字符串URL,并从中解析出一个URI对象:

var some_uri = new URI("http://www.example.com/foo/bar");

alert(some_uri.authority); // www.example.com
alert(some_uri);           // http://www.example.com/foo/bar

var blah      = new URI("blah");
var blah_full = blah.resolve(some_uri);
alert(blah_full);         // http://www.example.com/foo/blah

谢谢!但是我想要:uri = new Location("http://example.com/aa/bb") typeof(window.location) == typeof(uri) - freddiefujiwara
由于window.location是一个字符串,我真的不明白那可能或有何帮助。为什么类型需要匹配,当你可以轻松地从一个转换到另一个呢? - Rex M
1
设置window.location会改变浏览器,因此不会发生。 - epascarello
1
嗯,没错。window.location不是一个字符串,但可以从一个字符串中赋值。我不确定是否可以模仿这个行为,我尝试将location的原型分配给一个新的uri对象,但没有成功。 - Rex M
请注意,这段代码存在漏洞:不支持IPv6,并且当主机名后面出现“@”符号时会出错。 - Sam
显示剩余2条评论

14

今天我遇到了这个问题,然后我发现:URL - MDN Web API

var url = new URL("http://test.example.com/dir/subdir/file.html#hash");

这将返回:

{ hash:"#hash", host:"test.example.com", hostname:"test.example.com", href:"http://test.example.com/dir/subdir/file.html#hash", origin:"http://test.example.com", password:"", pathname:"/dir/subdir/file.html", port:"", protocol:"http:", search: "", username: "" }
希望我的第一次贡献能对你有所帮助!

重复的答案。 - Martin van Driel
9
是的,但是顶部的那个人在2017年更新了他的答案,而我是在2016年发布的。 - A. Moynet
啊,我的错,抱歉。 - Martin van Driel

13

简单正则表达式怎么样?

url = "http://www.example.com/path/to/somwhere";
urlParts = /^(?:\w+\:\/\/)?([^\/]+)(.*)$/.exec(url);
hostname = urlParts[1]; // www.example.com
path = urlParts[2]; // /path/to/somwhere

尝试解析一些有效的内容,例如//user:password@example.com/path/x?y=z,你会发现为什么简单的正则表达式并不能胜任。现在将一些无效的内容传递给它,它也应该以可预测的方式退出。 - Mikko Rantalainen
简单的正则表达式适用于简单的问题 :) 但是我觉得像这样的URL并不是无法通过正则表达式解析,只需要进行一些微调即可。但是如果我需要更复杂和牢固的东西,我可能会选择一些库。 - svestka
1
我同意@svestka上面的评论,但如果你想要一个简单的正则表达式解决方案,并且有一个可信赖的来源(就像我一样),并且没有访问DOM或URL()的权限(因为我正在使用tabris.js),那么正则表达式可能是一个好方法。这里有一个可以处理查询字符串的正则表达式:^(?:\w+\:\/\/)?([^\/]+)([^\?]*)\??(.*)$ - Luke Cousins

12
function parseUrl(url) {
    var m = (url || sp.targetUrl()).match(/^(([^:\/?#]+:)?(?:\/\/((?:([^\/?#:]*)(?::([^\/?#:]*))?@)?([^\/?#:]*)(?::([^\/?#:]*))?)))?([^?#]*)(\?[^#]*)?(#.*)?$/),
        r = {
            hash: m[10] || "",                   // #asd
            host: m[3] || "",                    // localhost:257
            hostname: m[6] || "",                // localhost
            href: m[0] || "",                    // http://username:password@localhost:257/deploy/?asd=asd#asd
            origin: m[1] || "",                  // http://username:password@localhost:257
            pathname: m[8] || (m[1] ? "/" : ""), // /deploy/
            port: m[7] || "",                    // 257
            protocol: m[2] || "",                // http:
            search: m[9] || "",                  // ?asd=asd
            username: m[4] || "",                // username
            password: m[5] || ""                 // password
        };
    if (r.protocol.length == 2) {
        r.protocol = "file:///" + r.protocol.toUpperCase();
        r.origin = r.protocol + "//" + r.host;
    }
    r.href = r.origin + r.pathname + r.search + r.hash;
    return r;
};
parseUrl("http://username:password@localhost:257/deploy/?asd=asd#asd");

它可以处理绝对路径和相对路径的URL。
要将相对路径处理为绝对URL而不改变模式,请将正则表达式更改为/^((?:([^:\/?#]+:)(?:\/\/))?((?:([^\/?#:]*)(?::([^\/?#:]*))?@)?([^\/?#:]*)(?::([^\/?#:]*))?))?([^?#]*)(\?[^#]*)?(#.*)?$/

不错的正则表达式!我想要的是没有参数的路径名,最好还有任何文件分隔符。我已经用额外的正则表达式解决了这个问题,所以没有紧急需要,但这只是一个建议。 - David
文件名和文件后缀仍然可以分开。 - David
@Nikolay b.xxx.xx.xx.xxx.com:5388无法工作,无法获取主机名。 - Michael Mao
1
@MichaelMao 这是因为绝对URL应该始终定义方案。无论如何,我已经更新了正则表达式以处理这种伪绝对URL,所以现在这不是问题了。我没有足够的时间使用广泛的URL测试这个正则表达式,但我相信所有的都应该正常工作。 - Nikolay
1
@Brett 我已经更新了代码,现在应该可以正常工作了。 - Nikolay
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接