Java 基于元素实例派生CSS选择器 背景
许多问题都会询问如何在给定CSS选择器的情况下获取特定的CSS。这个问题是关于相反的方向的。使用解析文档,但可以轻松地转换为以下任意格式:Java 基于元素实例派生CSS选择器 背景,java,html,css,dom,jsoup,Java,Html,Css,Dom,Jsoup,许多问题都会询问如何在给定CSS选择器的情况下获取特定的CSS。这个问题是关于相反的方向的。使用解析文档,但可以轻松地转换为以下任意格式: 用例 对于一个特定的问题领域(例如,化合物),成千上万的网页以类似的方式列出化学品,但不同网站的标记不同。例如: <div id="chemical-list"> <div class="compound"> <span class="compound-name">water</span>
<div id="chemical-list">
<div class="compound">
<span class="compound-name">water</span>
<span class="compound-periodic">H2O</span>
</div>
<div class="compound">
<span class="compound-name">sodium hypochlorite</span>
<span class="compound-periodic">NaClO</span>
</div>
</div>
这将允许跨数千页进行高精度分析。(该页面可以讨论水和次氯酸钠的应用,但只分析列表。)了解CSS将大大简化分析并提高其准确性
另一种方法是处理整个页面,查找“组”化学品,然后尝试提取列表。这两个问题都很难解决,但使用CSS选择器跳转到页面中的确切位置效率更高,而且可能更准确。这两个问题都需要一些手工制作,但我希望尽可能地自动化
问题
前面提到的API似乎没有生成给定CSS选择器的方法(越独特越好)。可以迭代父元素并手动生成选择器。这已经用了好几年了。也有一些答案可以用来生成一个,并且可能使用
具体来说,您将如何执行以下操作:
String selector = element.getCSSPath();
Elements elements = document.select( selector );
这将:
function getSelector(element) {
var selector = element.id;
// if we have an ID, that's all we need. IDs are unique. The end.
if(selector.id) { return "#" + selector; }
selector = [];
var cl;
while(element.parentNode) {
cl = element.getAttribute("class");
cl = cl ? "." + cl.trim().replace(/ +/g,'.') : '';
selector.push(element.localName + cl);
element = element.parentNode;
}
return selector.reverse().join(' ');
}
让我们来验证一下
<div class="main">
<ul class=" list of things">
<li><a href="moo" class="link">lol</a></li>
</ul>
</div>
--结果:
html body div.main ul.list.of.things li a.link
。。。黄金。据我所知,没有API提供这种功能。以下几点似乎有效:
/**
* Returns the shortest CSS path identify a given element. Note that this
* will not return a unique element, but can be used to obtain all elements
* that match the selector returned.
*
* @param cssElement The element that must be identified by its CSS selector.
* @return The CSS selector for the given element, or the empty string if
* no selector is found.
*/
private String cssPath( Element cssElement ) {
StringBuilder result = new StringBuilder( 256 );
String id = cssElement.id();
// If the element has an ID, then return it as the shortest path (IDs are
// supposed to be unique).
if( id.length() > 0 ) {
// This will break the chain of recursion.
result.append( '#' ).append( id );
}
else {
Element parent = cssElement.parent();
// If there is a parent node, then recurse to determine its CSS path.
// Otherwise, the chain of recursion ends here.
if( parent != null ) {
result.append( cssPath( parent ) );
}
// Generate a CSS path using the element's tag name and classes.
if( cssElement.className().length() > 0 ) {
result.append( " > " ).append( cssElement.tagName() );
Set<String> cssClasses = cssElement.classNames();
cssClasses.forEach( c -> result.append( '.' ).append( c ) );
result.append( ' ' );
}
}
// Return the (possibly incomplete) CSS selector through recursion.
return result.toString();
}
/**
*返回标识给定元素的最短CSS路径。请注意
*不会返回唯一的元素,但可用于获取所有元素
*匹配选择器返回的。
*
*@param cssElement必须由其CSS选择器标识的元素。
*@返回给定元素的CSS选择器,如果需要,返回空字符串
*找不到选择器。
*/
私有字符串cssPath(元素CSSELECT){
StringBuilder结果=新的StringBuilder(256);
字符串id=cssElement.id();
//如果元素具有ID,则将其作为最短路径返回(ID为
//应该是独一无二的)。
如果(id.length()>0){
//这将打破递归链。
result.append(“#”).append(id);
}
否则{
元素父元素=cssElement.parent();
//如果存在父节点,则递归以确定其CSS路径。
//否则,递归链到此结束。
如果(父项!=null){
结果:追加(cssPath(父项));
}
//使用元素的标记名和类生成CSS路径。
if(cssElement.className().length()>0){
result.append(“>”).append(cssElement.tagName());
设置cssClasses=cssElement.classNames();
cssClasses.forEach(c->result.append('.').append(c));
结果:追加(“”);
}
}
//通过递归返回CSS选择器(可能不完整)。
返回result.toString();
}
我使用了Mike的答案,并做了以下更改,以使返回的css选择器更短
更新:还使用name属性来缩短css选择器,并检查到目前为止选择器是否返回页面上的单个元素
更新:正如@10basetom在注释中指出的,在元素没有唯一id、唯一类名或唯一类名+名称属性的情况下,该方法可能会生成非唯一css路径,但在其他情况下,它会生成最短的css选择器。因此,我建议使用document.querySelectorAll(result.length==1
检查css路径结果,并使用其他方法回退
截至2014-09-28/1.8.1,JSoup已通过该方法实现了该功能(多亏了a)
css选择器
公共字符串cssSelector()
-获取将
唯一选择此元素。如果元素具有ID,则返回#ID
;
否则返回父级(如果有)CSS选择器,后跟“>”,
后跟元素的唯一选择器
(tag.class.class:n子级(n))
返回:可用于检索列表中元素的CSS路径
选择器
这将返回选择器,这些选择器通过使用元素ID(如果存在)返回唯一的元素,否则将创建一个形式为tag.class.class:n子元素(n)的选择器
例如:
“html>body>h2.section:nth child(3)”
Jsoup不提供此选项,但如果提供了,最独特的选择器将是使用
和:eq()
模拟XPath表达式的选择器。现在还不清楚它的用途是什么——它将精确地选择该元素,仅此而已,因此您的示例代码将毫无用处。这种API的实际使用案例是什么?可能是重复的,谢谢,迈克。我用它创建了一个(递归的)。在很多情况下,它不会返回唯一的选择器。例如,请参见此处的getSelector2()
console输出:我不明白为什么只要映射是稳定的,就会出现问题。这在许多情况下都不起作用。例如,请参见此处的控制台输出:
function getSelector(element) {
var selector = element.id;
// if we have an ID, that's all we need. IDs are unique. The end.
if(selector.id) { return "#" + selector; }
selector = [];
var cl;
while(element.parentNode) {
cl = element.getAttribute("class");
cl = cl ? "." + cl.trim().replace(/ +/g,'.') : '';
selector.push(element.localName + cl);
element = element.parentNode;
}
return selector.reverse().join(' ');
}
<div class="main">
<ul class=" list of things">
<li><a href="moo" class="link">lol</a></li>
</ul>
</div>
var a = document.querySelector("a");
console.log(getSelector(a));
/**
* Returns the shortest CSS path identify a given element. Note that this
* will not return a unique element, but can be used to obtain all elements
* that match the selector returned.
*
* @param cssElement The element that must be identified by its CSS selector.
* @return The CSS selector for the given element, or the empty string if
* no selector is found.
*/
private String cssPath( Element cssElement ) {
StringBuilder result = new StringBuilder( 256 );
String id = cssElement.id();
// If the element has an ID, then return it as the shortest path (IDs are
// supposed to be unique).
if( id.length() > 0 ) {
// This will break the chain of recursion.
result.append( '#' ).append( id );
}
else {
Element parent = cssElement.parent();
// If there is a parent node, then recurse to determine its CSS path.
// Otherwise, the chain of recursion ends here.
if( parent != null ) {
result.append( cssPath( parent ) );
}
// Generate a CSS path using the element's tag name and classes.
if( cssElement.className().length() > 0 ) {
result.append( " > " ).append( cssElement.tagName() );
Set<String> cssClasses = cssElement.classNames();
cssClasses.forEach( c -> result.append( '.' ).append( c ) );
result.append( ' ' );
}
}
// Return the (possibly incomplete) CSS selector through recursion.
return result.toString();
}
function getShortestSelector(element) {
var selector = element.id;
// if we have an ID, that's all we need. IDs are unique. The end.
if(selector.id) {
return "#" + selector;
}
selector = [];
var cl, name;
while(element.parentNode && (selector.length === 0 || document.querySelectorAll(selector.join(' ')).length !== 1)) {
// if exist, add the first found id and finish building the selector
var id = element.getAttribute("id");
if (id) {
selector.unshift("#" + id);
break;
}
cl = element.getAttribute("class");
cl = cl ? "." + cl.trim().replace(/ +/g,'.') : '';
name = element.getAttribute("name");
name = name ? ("[name=" + name.trim() + "]") : '';
selector.unshift(element.localName + cl + name);
element = element.parentNode;
}
var result = selector[0];
if (selector.length > 1) {
result += " " + selector.slice(1).join(" ").replace(/\[name=[^\]]*]/g, '');
}
return result;
}