jsdom是一个很棒的网页抓取工具。 然而, textContent
是一种非常不方便的方式来获取 html2text 转换的可读文本。
有一篇精彩的文章介绍innerText
在许多情况下可以忽略的
http://perfectkills.com/the-poor-misunderstood-innerText/
作者建议将getSelection().toString()
作为一种非常缓慢的解决方法,但是getSelection
尚未在jsdom 中实现。
你能考虑在jsdom 中实现innerText
吗? 作者对此进行了很大的探索,他甚至在最后添加了一个简单的规范。
什么可惜,四肢瘦长Selection
和innerText
库与jsdom不兼容: https://github.com/timdown/rangy/issues/348
因此,innerText 不是标准的,并且没有在至少一个主要引擎(Firefox)中实现。 没有标准,我认为我们不应该实施它。
从规范看来,如果没有基本的布局支持,我们似乎无法正确实现innerText
。
是的,无论如何,如果没有大量的基础设施工作,这在 jsdom 中实际上是无法实现的……没有人抱有希望 :(。
至于布局支持要求: https :
由于采用了 WHATWG,是否有计划实施它?
是的......虽然规范需要很多jsdom没有的东西,但围绕CSS框:(。不知道该怎么做。
是否有任何库可以与 jsdom 一起插入?
@domenic关心放弃一些关于为什么这是这样的基础设施大修的知识? 我们认为房间里 800 磅的大猩猩会离开 lo-key。 但看起来它不会去任何地方。 如你所知,我一直在思考 jsdom 的内部结构。 在 repo 中哪里是开始审查 jsdom newb 代码的好地方?
在此先感谢🙏 /cc @vsemozhetbyt
主要问题是innerText
依靠布局引擎来指导这一事实,而 jsdom 没有布局引擎。 请参阅https://html.spec.whatwg.org/multipage/dom.html#the -innertext-idl-attribute 和
http://perfectkills.com/the-poor-misunderstood-innerText/ 。 从第二个链接:
请注意 innerText 如何几乎精确地表示文本在页面上的显示方式。 另一方面,textContent 做了一些奇怪的事情——它忽略了由
以及围绕样式块元素(在这种情况下)。
仍然超出范围并且没有解决方法?
显然,规范说:
如果这个元素没有被渲染,或者如果用户代理是一个非 CSS 用户代理, [emphasis added] 那么返回与这个元素的 textContent IDL 属性相同的值。
我认为一种解决方法是简单地返回textContent
。
我们实现了足够多的 CSS,我认为这并不适用。 我们只是不实现布局部分......
大家好,这个有消息吗?
只需使用无头镀铬:)
@domenic来自@coreh提到的规范:
https://html.spec.whatwg.org/multipage/dom.html#the -innertext-idl-attribute
如果这个元素没有被渲染,或者如果用户代理是一个非 CSS 用户代理,那么返回与这个元素上的
textContent
IDL 属性相同的值。
https://html.spec.whatwg.org/multipage/rendering.html#being -rendered
如果元素具有任何关联的 CSS 布局框、SVG 布局框或其他样式语言中的等效项,则该元素正在被渲染。
如果jsdom
没有实现布局部分,那是不是意味着“不被渲染”适用?
此消息适用于到达此 github 线程的任何人,他们只想通过一种方法在不更改其函数实现的情况下让他们的测试通过。
copypasta 用于测试文件的顶部:
// Expose JSDOM Element constructor
global.Element = (new JSDOM()).window.Element;
// 'Implement' innerText in JSDOM: https://github.com/jsdom/jsdom/issues/1245
Object.defineProperty(global.Element.prototype, 'innerText', {
get() {
return this.textContent;
},
});
当然,上述讨论中的警告也适用。
如果其他人遇到这个问题,我会更进一步,并使用sanitize-html
包来基本上了解浏览器在做什么(注意我没有导入 JSDOM 设置,因为我发现它不需要将其放入我的 Jest 设置文件时,但如果您不使用 Jest,那么您将需要使用@bennypowers推荐的global.Element = (new JSDOM()).window.Element
设置):
Object.defineProperty(global.Element.prototype, 'innerText', {
get() {
return sanitizeHtml(this.textContent, {
allowedTags: [], // remove all tags and return text content only
allowedAttributes: {}, // remove all tags and return text content only
});
},
configurable: true, // make it so that it doesn't blow chunks on re-running tests with things like --watch
});
我有类似的需求,但想比仅仅使用textContent
更进一步——同样,这不能准确表示浏览器的实际操作,尤其是对于 css 隐藏的元素,但这很好足以满足我的用例:
function innerText(el)
el = el.cloneNode(true) // can skip if mutability isn't a concern
el.querySelectorAll('script,style').forEach(s => s.remove())
return el.textContent
}
真可惜!
显然,规范说:
如果这个元素没有被渲染,或者如果用户代理是一个非 CSS 用户代理,[emphasis added] 那么返回与这个元素的 textContent IDL 属性相同的值。
我认为一种解决方法是简单地返回 textContent。
我们实现了足够多的 CSS,我认为这并不适用。 我们只是不实现布局部分......
@domenic请考虑对规范进行更自由的解释
当 CSS 规则的应用成本太高时,明确允许textContent
作为后备
此外,innerText 被指定为getter 和 setter
鉴于我是规范编辑,我可以肯定地说“当 CSS 规则的应用成本太高时”并不是规范所说的。
..这是我对“如果用户代理是非 CSS 用户代理”的解释
“CSS 用户代理”和“非 CSS 用户代理”有什么区别?
关于什么:
CSS 用户代理可以“应用 CSS 规则”并输出结果(图形或文本)
非 CSS 用户代理太笨了,无法“应用 CSS 规则”
我们实现了足够多的 CSS,我认为这并不适用。
你是什么意思? window.getComputedStyle?
回退到 textContent 仍然比不实现标准接口好
也许我们可以只使用textContent
值替换的结果innerText
同时运行与测试jsdom
。 例如:
describe('mytest', () => {
beforeAll(() => {
Object.defineProperty(HTMLElement.prototype, 'innerText', {
get() {
return this.textContent;
}
});
});
it('should ok', () => {
// test assertions
});
});
最有用的评论
如果其他人遇到这个问题,我会更进一步,并使用
sanitize-html
包来基本上了解浏览器在做什么(注意我没有导入 JSDOM 设置,因为我发现它不需要将其放入我的 Jest 设置文件时,但如果您不使用 Jest,那么您将需要使用@bennypowers推荐的global.Element = (new JSDOM()).window.Element
设置):