如果 <base href...> 设置为双斜杠,会发生什么?

6
我想了解如何使用<base href="" />在我的网络爬虫中进行设置,因此我尝试使用不同浏览器的几种组合,最终发现有双斜杠的东西我不太明白。

如果你不想阅读全部内容,请跳转到DE的测试结果。所有测试演示如下:
http://gutt.it/basehref.php

下面是我调用http://example.com/images.html的测试结果:

A - 多个base href

<html>
<head>
<base target="_blank" />
<base href="http://example.com/images/" />
<base href="http://example.com/" />
</head>
<body>
<img src="/images/image.jpg">
<img src="image.jpg">
<img src="./image.jpg">
<img src="images/image.jpg"> not found
<img src="/image.jpg"> not found
<img src="../image.jpg"> not found
</body>
</html>

结论

  • 只有第一个带有href属性的<base>标签有效
  • /开头的资源指向根目录
  • ../表示上一级目录

B - 不带尾部斜杠

<html>
<head>
<base href="http://example.com/images" />
</head>
<body>
<img src="/images/image.jpg">
<img src="image.jpg"> not found
<img src="./image.jpg"> not found
<img src="images/image.jpg">
<img src="/image.jpg"> not found
<img src="../image.jpg"> not found
</body>
</html>

结论

  • <base href>会忽略最后一个斜杠后面的所有内容,因此http://example.com/images会变成http://example.com/

C - 应该这样

<html>
<head>
<base href="http://example.com/" />
</head>
<body>
<img src="/images/image.jpg">
<img src="image.jpg"> not found
<img src="./image.jpg"> not found
<img src="images/image.jpg">
<img src="/image.jpg"> not found
<img src="../image.jpg"> not found
</body>
</html>

结论

  • 测试B的结果相同,符合预期

D - 双斜线

<html>
<head>
<base href="http://example.com/images//" />
</head>
<body>
<img src="/images/image.jpg">
<img src="image.jpg">
<img src="./image.jpg">
<img src="images/image.jpg"> not found
<img src="/image.jpg"> not found
<img src="../image.jpg">
</body>
</html>

E - 添加空格的双斜线

<html>
<head>
<base href="http://example.com/images/ /" />
</head>
<body>
<img src="/images/image.jpg">
<img src="image.jpg"> not found
<img src="./image.jpg"> not found
<img src="images/image.jpg"> not found
<img src="/image.jpg"> not found
<img src="../image.jpg">
</body>
</html>

这两个URL都不是“有效”的,但却是我的网络爬虫的真实结果。请解释一下DE中发生了什么事情,以便可以找到../image.jpg,以及为什么会因为空格而导致差异?

仅供您参考:

  • <base href="http://example.com//" />测试C相同
  • <base href="http://example.com/ /" />完全不同。只能找到../image.jpg
  • <base href="a/" />只能找到 /images/image.jpg

我的意思是你可以简单地尝试一下。为什么你没有呢? - pavel
4
当然了。你没有仔细阅读说明。请撤销你的踩和关闭请求。 - mgutt
根据定义,您应该只使用单斜杠...双斜杠将根据服务器设置而有所不同。请参见https://dev59.com/Amkw5IYBdhLWcg3wEmab#10161264。 - pschueller
@pschueller 我知道我的例子不是“有效的”,但这是通过我的爬虫程序得到的真实例子,所以我想知道如何在这种特殊情况下使用base href。最终,我不会影响HTML源代码。 - mgutt
现在我也在描述中添加了一个演示页面。 - mgutt
显示剩余3条评论
1个回答

6

base元素的行为在HTML规范中有解释:

base元素允许作者指定文档基础URL,以便解析相对URL

如您所示的测试A,如果有多个带有href属性的base,则文档基础URL将是第一个。

解析相对URL的方法如下:

应用URL解析器url,以base作为基础URL,使用encoding编码。 URL解析算法在URL规范中定义。
这里不方便详细解释它的复杂性。但基本上,以下是发生的情况:
- 以 / 开头的相对 URL 是相对于基础 URL 的主机计算的。 - 否则,相对 URL 是相对于基础 URL 的最后一个目录计算的。 - 注意,如果基础路径没有以 / 结尾,则最后一部分将是文件,而不是目录。 - ./ 是当前目录 - ../ 进入上一级目录 (可能,“目录”和“文件”不是 URL 中的正确术语) 以下是一些例子:
  • http://example.com/images/a/./http://example.com/images/a/
  • http://example.com/images/a/../http://example.com/images/
  • http://example.com/images//./http://example.com/images//
  • http://example.com/images//../http://example.com/images/
  • http://example.com/images/./http://example.com/images/
  • http://example.com/images/../http://example.com/

请注意,在大多数情况下,// 将像 / 一样。正如 @poncha 所说的那样。

除非您使用某种URL重写(在这种情况下,重写规则可能会受到斜杠数量的影响),否则URI映射到磁盘上的路径,但在大多数现代操作系统(Linux / Unix,Windows)中,连续的多个路径分隔符没有任何特殊含义,因此/path/to/foo和/path//to////foo最终将映射到相同的文件。
然而,通常/ /不会变成//。
您可以使用以下代码片段将相对URL的列表解析为绝对URL。

var bases = [
  "http://example.com/images/",
  "http://example.com/images",
  "http://example.com/",
  "http://example.com/images//",
  "http://example.com/images/ /"
];
var urls = [
  "/images/image.jpg",
  "image.jpg",
  "./image.jpg",
  "images/image.jpg",
  "/image.jpg",
  "../image.jpg"
];
function newEl(type, contents) {
  var el = document.createElement(type);
  if(!contents) return el;
  if(!(contents instanceof Array))
    contents = [contents];
  for(var i=0; i<contents.length; ++i)
    if(typeof contents[i] == 'string')
      el.appendChild(document.createTextNode(contents[i]))
    else if(typeof contents[i] == 'object') // contents[i] instanceof Node
      el.appendChild(contents[i])
  return el;
}
function emoticon(str) {
  return {
    'http://example.com/images/image.jpg': 'good',
    'http://example.com/images//image.jpg': 'neutral'
  }[str] || 'bad';
}
var base = document.createElement('base'),
    a = document.createElement('a'),
    output = document.createElement('ul'),
    head = document.getElementsByTagName('head')[0];
head.insertBefore(base, head.firstChild);
for(var i=0; i<bases.length; ++i) {
  base.href = bases[i];
  var test = newEl('li', [
    'Test ' + (i+1) + ': ',
    newEl('span', bases[i])
  ]);
  test.className = 'test';
  var testItems = newEl('ul');
  testItems.className = 'test-items';
  for(var j=0; j<urls.length; ++j) {
    a.href = urls[j];
    var absURL = a.cloneNode(false).href;
      /* Stupid old IE requires cloning
         https://dev59.com/lnRB5IYBdhLWcg3w6bUD#24437713 */
    var testItem = newEl('li', [
      newEl('span', urls[j]),
      ' → ',
      newEl('span', absURL)
    ]);
    testItem.className = 'test-item ' + emoticon(absURL);
    testItems.appendChild(testItem);
  }
  test.appendChild(testItems);
  output.appendChild(test);
}
document.body.appendChild(output);
span {
  background: #eef;
}
.test-items {
  display: table;
  border-spacing: .13em;
  padding-left: 1.1em;
  margin-bottom: .3em;
}
.test-item {
  display: table-row;
  position: relative;
  list-style: none;
}
.test-item > span {
  display: table-cell;
}
.test-item:before {
  display: inline-block;
  width: 1.1em;
  height: 1.1em;
  line-height: 1em;
  text-align: center;
  border-radius: 50%;
  margin-right: .4em;
  position: absolute;
  left: -1.1em;
  top: 0;
}
.good:before {
  content: ':)';
  background: #0f0;
}
.neutral:before {
  content: ':|';
  background: #ff0;
}
.bad:before {
  content: ':(';
  background: #f00;
}

您也可以使用此片段进行操作:

var resolveURL = (function() {
  var base = document.createElement('base'),
      a = document.createElement('a'),
      head = document.getElementsByTagName('head')[0];
  return function(url, baseurl) {
    if(base) {
      base.href = baseurl;
      head.insertBefore(base, head.firstChild);
    }
    a.href = url;
    var abs = a.cloneNode(false).href;
    /* Stupid old IE requires cloning
       https://dev59.com/lnRB5IYBdhLWcg3w6bUD#24437713 */
    if(base)
      head.removeChild(base);
    return abs;
  };
})();
var base = document.getElementById('base'),
    url = document.getElementById('url'),
    abs = document.getElementById('absolute');
base.onpropertychange = url.onpropertychange = function() {
  if (event.propertyName == "value")
    update()
};
(base.oninput = url.oninput = update)();
function update() {
  abs.value = resolveURL(url.value, base.value);
}
label {
  display: block;
  margin: 1em 0;
}
input {
  width: 100%;
}
<label>
  Base url:
  <input id="base" value="http://example.com/images//foo////bar/baz"
         placeholder="Enter your base url here" />
</label>
<label>
  URL to be resolved:
  <input id="url" value="./a/b/../c"
         placeholder="Enter your URL here">
</label>
<label>
  Resulting url:
  <input id="absolute" readonly>
</label>


1
谢谢!现在我明白发生了什么。在测试D中,//images/深一级,因此../指向images/。但是,如果目标包含//,就像./image.jpg一样,第二个将被忽略。为了澄清事情,我会稍微编辑你的答案。请随意使用它。 - mgutt

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