Node.js
WebDriver
Selenium

Selenium WebDriverでの要素解析はPromise.allで

More than 1 year has passed since last update.

WebDriverでの要素解析って、Promiseで返るので、たくさん解析する要素あるといちいちthen then繋げるの面倒だなあと思ってたんですけど、よく考えたら、Promise.allで並列化した上でthenしてまとめて受け取ってやれば無問題どころか直列でのインタフェースより快適でした。

サンプル
<div id="header">ヘッダ</div>
<div id="list">
  <div class="item"><span class="title">名前</span><span class="desc">内容</span></div>
  <div class="item"><span class="title">なまえ</span><span class="desc"></span></div>
  <div class="item"><span class="title">Name</span><span class="desc">ないよう</span></div>
</div>
<div id="footer">フッタ</div>

みたいなhtmlなら、サンプルとして壮絶ダサいですけどそれはおいておいて、

Promise.allサンプル1
Promise.all([
    driver.findElements(By.xpath('//div[@id="header"]')),
    driver.findElements(By.xpath('//div[@class="item"]')),
    driver.findElements(By.xpath('//div[@id="footer"]'))
])
.then(function(args){
    var headerPromise = args[0];
    var itemsPromise  = args[1];
    var footerPromise = args[2];
});

みたいな感じで、直列にしなくてもPromise.allでまとめてしまえばガサッと取れる。

なんだったらもっと処理してしまってもいい。

Promise.allサンプル2
Promise.all([
    driver.findElements(By.xpath('//div[@id="header"]'))
        .then(function(headers){
            return headers.length ? headers[0].getText() : "";
        }),
    driver.findElements(By.xpath('//div[@class="item"]'))
        .then(function(items){
            return items.map(function(item){
                return Promise.all([
                    item.findElement(By.xpath('./span[@class="title"]'))
                        .getText().catch(function(err){ return ""; }),
                    item.findElement(By.xpath('./span[@class="desc"]'))
                        .getText().catch(function(err){ return ""; })
                ]);                
            });
        }),
    driver.findElement(By.xpath('//div[@id="footer"]'))
        .getText()
        .catch(function(err){
            return "";
        })
])
.then(function(args){
    var headerText = args[0]; //"ヘッダ"
    var items      = args[1]; //[["名前","内容"],["なまえ","は"],["Name","ないよう"]]
    var footerText = args[2]; //"フッタ"
});

まあ考えたら当たり前なんですけども、最初は思いついてなかったので、記事にしてみました。