JavaScriptとChromeブラウザで手動スクレイピングする

# 手動スクレイピング？

ブラウザ開発者ツールのコンソールに直接jsコードを書いて、ウェブページからデータを抽出する作業を個人的にそう呼んでいます。データは欲しいけど自動化するまでもないときに手軽に実行できて便利です。

おおまかに以下の流れで欲しいデータを集めます。

1. 要素を指定する
2. 要素をArrayにする
3. 要素のデータを取得する

ブラウザはChromeを前提としています。
他のモダンブラウザでも同様のことができると思いますが検証はしていません。

# 1. 要素を指定する

`document.querySelectorAll`を使用して欲しいデータを持つHTMLエレメントを指定します。

https://developer.mozilla.org/ja/docs/Web/API/Document/querySelectorAll

- 要素名やclass,idを指定して絞り込む

```js
document.querySelectorAll('span');
document.querySelectorAll('.target-class');
document.querySelectorAll('#target-id');
```

- 属性で絞り込む

```js
document.querySelectorAll('span[itemprop="propValue"]');
```

- data属性

```js
document.querySelectorAll('span[data-name="target"]')
document.querySelectorAll('span[data-name]')
```

メソッドチェーンで絞り込むこともできます。

```js
document.querySelectorAll('#container').querySelectorAll('.item')
```

# 2. 要素をArrayにする
`document.querySelectorAll`が返す[NodeList](https://developer.mozilla.org/ja/docs/Web/API/NodeList)はそのままでは扱いづらいので、[スプレッド構文](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax)を使って[HTMLElement](https://developer.mozilla.org/ja/docs/Web/API/HTMLElement)のArrayにしてしまいます。

```js
[...document.querySelectorAll('.target')]
```

これでmapやfilter, sliceなどいつものArrayメソッドを使って要素の巡回、データの抽出をすることができます。

# 3. 要素のデータを取得する
いくつか例を挙げます。
基本、コンソールのオートコンプリートや先行評価を見ながら方法を探っていく作業になります。
ArrayやStringのメソッドを駆使してデータの絞り込み、整形を行っていきましょう。

## テキストだけを取得したいとき

```html
<div>
  ほしいテキスト
  <p>ほしいテキスト２</p>
</div>
```

```js
[...document.querySelectorAll('div')][0].innerText
// ほしいテキスト\n\nほしいテキスト２
```

## html属性を取得したい

```html
<div
  id="book-id"
  class="book-class"
  data-isbn="9784873118864"
  data-publish="2019-09-19"
  itemprop="title">
</div>
```

```js
[...document.querySelectorAll('#book-id')].map(e => ({
	id: e.id,
	class: e.className,
	data: e.dataset,
	itemprop: e.getAttribute('itemprop')
}));

// [{"id":"book-id","class":"book-class","data":{"isbn":"9784873118864","publish":"2019-09-19"},"itemprop":"title"}]
```

## 子要素を除くテキストだけ取得したい

```html
<div id="sample">
  Hello
  <span>いらないテキスト</span>
  World
  <span>いらないテキスト</span>
  !!
</div>
```

```js
[...document.querySelectorAll('div')[0].childNodes]
    .filter(c => c.wholeText)
    .map(t => t.nodeValue.trim())
    .join(' ');
// Hello World !!
```

# 結果を取得する
以下で直前の出力をクリップボードにコピーできます。あとはエディタに貼り付けるなどして煮るなり焼くなりしましょう。

```js
copy($_);
```

# 実践編
## [オライリー・ジャパン発行書籍一覧](https://www.oreilly.co.jp/catalog/)を取得する
isbnコード, 書籍タイトル, 価格, 発行日の一覧を取得してみます。

下記のコードをコンソールに貼り付けて実行したあと、

```js
[...document.querySelector('#bookTable').querySelectorAll('tr')]
    .slice(1)  // テーブルのヘッダを除く
    .map(e => (
        {
            isbn: e.children[0].innerText.replace(/-/g, ''),
            title: e.children[1].innerText,
            price: e.children[2].innerText.replace(',', ''),
            publishDate: e.children[3].innerText.replace(/\//g, '-')
        }
    ));
```

結果をクリップボードにコピーします。

```js
copy($_)
```

結果、執筆時点での全441冊のデータをjson形式で取得できました。

```json
[
    {
        "isbn": "4873110637",
        "title": "C++プログラミング入門 新版",
        "price": "3024",
        "publishDate": "2001-11-01"
    },
    {
        "isbn": "4873110653",
        "title": "サーバ負荷分散技術",
        "price": "3240",
        "publishDate": "2001-12-01"
    },
    ...
]
```

# コードを保存して繰り返し使いたい
Chromeブラウザのsnippets機能を使いましょう
https://developers.google.com/web/tools/chrome-devtools/javascript/snippets

# 自動化したい
puppeteerを使うと作ったコードが流用できます。

```js
const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto("https://example.com");

  const result = await page.evaluate(() => {
    return [...document.querySelectorAll(".target")]
      .map(e => e.innerText);
  });

  await browser.close();
})();
```