javascript 如何将HTML解析成一个DOM树,并选择一个源位置?

myss37ts  于 2023-04-04  发布在  Java
关注(0)|答案(2)|浏览(137)

bounty将在12小时后过期。回答此问题可获得+150声望奖励。user3840170希望引起更多关注此问题。

我正在编写一个在https://example.net上运行的用户脚本,它对来自https://example.com的HTML文档发出fetch请求,我希望将这些HTML文档解析为HTML DOM树。
fetch API只给我原始的HTML源代码。我可以自己使用DOMParser解析它,但是我遇到了一个相对链接的问题。假设来自https://example.com的文档包含如下内容:

<!DOCTYPE html>
<html>
  <head>
  <body>
    <p> <a href="/foo">hello!</a>

如果我获取body > p > a元素的DOM节点,并读取其href属性,我获得的值将是https://example.net/foo。这是因为DOMParser将环境文档的源位置分配给解析结果。我希望将文档的实际源分配给它,以便正确解析相对链接。
目前我能想到的解决方法只有:

  • <base>元素注入DOM树,这可能会干扰实际HTML源代码中的<base>标记
  • 使用document.implementation.createHTMLDocument(),然后使用.write(),这会给我一个带有空白源位置的文档,其中相对链接至少不会被错误解析(但根本不会被解析)。除了这在用户脚本中不起作用:它抛出一个SecurityError
  • 使用Proxy拦截对href属性的访问,该属性似乎太重,无法轻松地放入用户脚本中
  • 包括一个用户级HTML解析器和DOM实现,这看起来又太麻烦了

我也意识到,从.text()获得的Unicode文本解析HTML将绕过HTML编码检测算法。我自己可以接受,因为我感兴趣的网站只使用UTF-8正确地表示在标题中,但这也是一个应该注意的缺陷。理想情况下,HTML文档应该直接从Blob甚至ReadableStream解析。
有没有更好的方法来实现我想要的?

cqoc49vn

cqoc49vn1#

使用XMLHttpRequest而不是fetch,它具有将HTML解析为Document的内置功能。
在调用open()之后、调用send()之前,必须通过将字符串"document"分配给XMLHttpRequest对象的responseType属性来显式请求文档。

const xhr = new XMLHttpRequest();
xhr.onload = () => {
  console.log(
    Array.from(xhr.responseXML.links).map(({ href }) => href)
  );
}
xhr.open("GET", "https://example.com");
xhr.responseType = "document";
xhr.send();

在我的测试中,相对URL被转换为基于源文档的绝对URL。

axr492tv

axr492tv2#

如果您可以将基本元素注入到DOM树中,这将是最简单的方法。
但是,如果考虑另一种可能的方法,您可以使用URL对象基于文档的基本URL构造一个新的绝对URL。例如-

const base = new URL('https://example.com');
const html = '<!DOCTYPE html><html><body><p><a href="/foo">hello!</a></p></body></html>';
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const link = doc.querySelector('a');
const href = link.href;
const absUrl = new URL(href, base).href;
console.log(absUrl); // output: "https://example.com/foo"

通过这种方式,您可以确保正确地设置相对链接,而不必将基本元素插入DOM树或使用用户态HTML解析器和DOM实现。

相关问题