Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/349.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/html/74.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 基于元素实例派生CSS选择器 背景_Java_Html_Css_Dom_Jsoup - Fatal编程技术网

Java 基于元素实例派生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>

许多问题都会询问如何在给定CSS选择器的情况下获取特定的CSS。这个问题是关于相反的方向的。使用解析文档,但可以轻松地转换为以下任意格式:

用例 对于一个特定的问题领域(例如,化合物),成千上万的网页以类似的方式列出化学品,但不同网站的标记不同。例如:

<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 );
这将:

  • 返回给定元素的CSS选择器
  • 在文档中搜索给定的CSS选择器
  • 返回与选择器匹配的元素列表
  • 第二行不是问题;第一行是有问题的

    问题: 什么API可以从DOM元素生成CSS选择器(尽可能唯一)

    如果没有现有的API,那么很高兴知道。

    只需使用Java并运行一些普通JavaScript

    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;
    }