Webで歌詞を自動に取得

今日、[”https://mp3.zing.vn/zing-chart-tuan/bai-hat-US-UK/IWZ9Z0BW.html”](https://mp3.zing.vn/zing-chart-tuan/bai-hat-US-UK/IWZ9Z0BW.html)で歌詞を取得する自動的なツールを書きます

#技術
|技術 　| バージョン    |
|:-----|------------:|
| Nodejs|10.14.1 | 
| Puppeteer|1.12.2 |


#準備
1. 新たなフォルダを作る（名前はなんでも良い）
2. 「１」で作ったフォルダにアクセスし、ターミナルで```npm init``` -> ```npm i puppeteer```を書く
3. index.jsを作る
4. 歌詞を保存するフォルダを作る
5. Google Chromeを使用する

<img width="324" alt="Screen Shot 2019-02-18 at 8.19.47 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/332720/f54ff5ed-ff08-fdf7-8bb6-6a692804aa6d.png">

#PuppeteerのAPI
page.evaluateだけを使用するので、他のAPIは[ここ](https://github.com/GoogleChrome/puppeteer)で参照して下さい

#開発
1. page.evaluateはWebでスクリプトを実行して、結果を帰ります。なので、HTML構築の分析が必要です。
<img width="1440" alt="Screen Shot 2019-02-18 at 8.52.00 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/332720/bf4ce312-2744-03fa-4fd5-f302014e41e5.png">

それぞれの歌は＜div＞の「e-item」の中に有ります：
- txt-primary _trackLinkのclassがある＜a＞：歌の名前
- href：歌詳細へのリンク
2. Google ChromeのConsoleでスクリプトを試します

```
let songs = document.getElementsByClassName('e-item');
songs = [...songs];
let array = songs.map(song => ({
    title: song.getElementsByClassName('txt-primary _trackLink')[0].innerHTML.trim(),
    url: song.getElementsByClassName('txt-primary _trackLink')[0].href
}));
```
結果
<img width="1440" alt="Screen Shot 2019-02-18 at 8.58.43 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/332720/66d933da-615a-f669-bb6d-bb5ff7b0a28b.png">
イメージのような結果が出てくると、成功です。
3. 次は、Webで歌詞の取得スクリプトを試します
<img width="1118" alt="Screen Shot 2019-02-18 at 9.20.32 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/332720/6063ab5d-c337-dd2f-aa4e-27242b63d6e7.png">
歌詞はfn-wlyrics fn-contentのclassがある＜p＞です
また、＜br /＞が有りますので、replaceを使って、それを省く
スクリプト: `document.getElementsByClassName('fn-wlyrics fn-content')[0].innerHTML.replace(/\<br\>/g,"")`
結果
<img width="1440" alt="Screen Shot 2019-02-18 at 9.25.33 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/332720/d62dd726-b30e-2d18-a1ef-e51cc3e8163a.png">
4. 今、index.jsを書きましょう。歌詞はファイルに保存します。

```index.js
const puppeteer = require('puppeteer');
const fs = require('fs');

(async()=>{
  const browser=await puppeteer.launch({headless:true});
  const page=await browser.newPage();

  await page.goto('https://mp3.zing.vn/zing-chart-tuan/bai-hat-US-UK/IWZ9Z0BW.html');
  const songs = await page.evaluate(() => {
    let songs = document.getElementsByClassName('e-item');
    songs = [...songs];
    let array = songs.map(song => ({
        title: song.getElementsByClassName('txt-primary _trackLink')[0].innerHTML.trim(),
        url: song.getElementsByClassName('txt-primary _trackLink')[0].href
    }));
    return array;
  });

  for(let song of songs){
    await page.goto(song.url);
    let lyric = await page.evaluate(()=>{
      const evaluatedLyric = document.getElementsByClassName('fn-wlyrics fn-content')[0];
      // 歌詞はいつもあるわけではないので、条件をチェック
      if (evaluatedLyric !== undefined) {
        return evaluatedLyric.innerHTML.replace(/\<br\>/g,"");
      } else return "歌詞無し";
    });
    await fs.writeFile(`songs/${song.title}.txt`,lyric,function(err){
      if(err) throw err;
      console.log("取得: "+song.title);
    });
  }
  await browser.close();
})();
```
最終的に、以下の結果が出る
<img width="519" alt="Screen Shot 2019-02-18 at 9.39.20 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/332720/8fe12eef-d2f8-528f-80d6-57625437fc89.png">
<img width="329" alt="Screen Shot 2019-02-18 at 9.40.51 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/332720/0e6b2b8e-cd52-3445-25ed-c954437fedd5.png">
<img width="1552" alt="Screen Shot 2019-02-18 at 9.42.29 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/332720/1419b11d-fcd4-5488-586b-45716c315433.png">

#まとめ
この方法はかなり簡単ですが、HTML構築の分析が必要なので、時々嫌を感じるかもしれません。それで、使用の前に、注意しべきです。