それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

Selenium IDEが期待と違うから、クリックしたボタンとリンクをログとる拡張機能書いた。

クリックしたボタンのXpathを記録したい。

Xpathで記録したいんだけど、ルートからのXpathって表記が長くって、可読性低い。つまるところメンテナンス性能が最悪。

xpath を検索条件で保存したいですね・

つぎのようなXpathで保存されるとナニが何かわからない。

/html/body/div[4]/div[2]/div/div/div/div[2]/a

だから、次のようなxpathで保存したい

//a[contains(./text(), ' Download ')][@href='#']

ぱぱっと書いてみた。

content_script として突っ込んで、Extensionに、クリックしたリンクやボタンを始め、クリックした要素のXpathを送ることにした。

  document.body.addEventListener( "click" , function(e){
    console.clear()


      get_xpath_expression = function(e) {
        if (e.nodeType == 9) { return "";  }

         ////////////////////////////////////////////////////
         // scraping 用なので id より input[]/a[] 表記を優先する。
         ////////////////////////////////////////////////////
         //リンクの場合の処理。
        if( /a$/.test(e.tagName.toLowerCase()) ){
           str = `//${e.tagName.toLowerCase()}`
           if (e.text.trim() != "" ) {
            str += `[contains(./text(), '${e.textContent}')]`
           }
           "id href name".split(/\s+/).forEach( function( attr_name ){
             if ( e.getAttribute(attr_name)) {
                 str += `[@${attr_name}='${e.getAttribute(attr_name)}']`
             }
           })
           return str;
        }
         //input の処理
        if( /input$/.test(e.tagName.toLowerCase()) ){
            str = `//${e.tagName.toLowerCase()}`
            "name type value id".split(/\s+/).forEach( function( attr_name ){
              if ( e.getAttribute(attr_name)) {
                  str += `[@${attr_name}='${e.getAttribute(attr_name)}']`
              }
            })
           return str;
        }
        if( /form$/.test(e.tagName.toLowerCase()) ){
            str = `//${e.tagName.toLowerCase()}`
            "name action id".split(/\s+/).forEach( function( attr_name ){
              if ( e.getAttribute(attr_name)) {
                  str += `[@${attr_name}='${e.getAttribute(attr_name)}']`
              }
            })
          return str;
        }
        if( /button$/.test(e.tagName.toLowerCase()) ){
            str = `//${e.tagName.toLowerCase()}`
            if (e.text.trim() != "" ) {
              str += `[contains(./text(), '${e.textContent}')]`
            }
            "id name value".split(/\s+/).forEach( function( attr_name ){
              if ( e.getAttribute(attr_name)) {
                  str += `[@${attr_name}='${e.getAttribute(attr_name)}']`
              }
            })
          return str;
        }


        //
        if (e.hasAttribute("id")) {
          return 'id("' + e.getAttribute("id") + '")'
        }

        var p = e.parentNode;
        var t = get_xpath_expression(p) + "/" + e.tagName.toLowerCase();
        var c = p.childNodes;
        var g = 0;
        var s;
        for (var i = 0, n = c.length; i < n; ++i) {
           if (c[i].nodeName == e.nodeName && c[i].nodeType == e.nodeType) {
               ++g;
               if (c[i] == e) {
                   s = g
               }
           }
        }
         if (g == 1) {
             return t
        }
        t += "[" + (s) + "]";
        return t
      }

    var xpath_str = get_xpath_expression(e.target)
    console.log(xpath_str)
    var message_sample = function(){

      var id = "icjaalhedjpokjepgfojceaclllpdman";
      data = {
        xpath: xpath_str,
        url : window.location.toString(),
        title : window.document.title,
        origin : window.location.origin
      }
      console.log(data)

      chrome.runtime.sendMessage(id, data, null , function(x){
        console.log("sent");
        console.log(x);
      } )
    }
    message_sample()

  }, true);

拡張機能にして保存してみた。

f:id:takuya_1st:20160403042702p:plain

クリックを追いかけられるのは便利かもしれない。

ついでに、rubySeleniumソースを自動生成するようにしたので、スクレーパーの雛形をぱぱっと作ることが出来て嬉しい。

プロファイルを工夫すれば、請求書の取得をさらに自動化して遊べそう。

フォーム入力値どうするか。

テキスト入力は日本語があるから、シンドいんですよね。

onchage イベントでも監視しよう。

こっちはそのうちやる。