手動スクレイピング?
ブラウザ開発者ツールのコンソールに直接jsコードを書いて、ウェブページからデータを抽出する作業を個人的にそう呼んでいます。データは欲しいけど自動化するまでもないときに手軽に実行できて便利です。
おおまかに以下の流れで欲しいデータを集めます。
- 要素を指定する
- 要素をArrayにする
- 要素のデータを取得する
ブラウザはChromeを前提としています。
他のモダンブラウザでも同様のことができると思いますが検証はしていません。
1. 要素を指定する
document.querySelectorAll
を使用して欲しいデータを持つHTMLエレメントを指定します。
- 要素名やclass,idを指定して絞り込む
document.querySelectorAll('span');
document.querySelectorAll('.target-class');
document.querySelectorAll('#target-id');
- 属性で絞り込む
document.querySelectorAll('span[itemprop="propValue"]');
- data属性
document.querySelectorAll('span[data-name="target"]')
document.querySelectorAll('span[data-name]')
メソッドチェーンで絞り込むこともできます。
document.querySelectorAll('#container').querySelectorAll('.item')
2. 要素をArrayにする
document.querySelectorAll
が返すNodeListはそのままでは扱いづらいので、スプレッド構文を使ってHTMLElementのArrayにしてしまいます。
[...document.querySelectorAll('.target')]
これでmapやfilter, sliceなどいつものArrayメソッドを使って要素の巡回、データの抽出をすることができます。
3. 要素のデータを取得する
いくつか例を挙げます。
基本、コンソールのオートコンプリートや先行評価を見ながら方法を探っていく作業になります。
ArrayやStringのメソッドを駆使してデータの絞り込み、整形を行っていきましょう。
テキストだけを取得したいとき
<div>
ほしいテキスト
<p>ほしいテキスト2</p>
</div>
[...document.querySelectorAll('div')][0].innerText
// ほしいテキスト\n\nほしいテキスト2
html属性を取得したい
<div
id="book-id"
class="book-class"
data-isbn="9784873118864"
data-publish="2019-09-19"
itemprop="title">
</div>
[...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"}]
子要素を除くテキストだけ取得したい
<div id="sample">
Hello
<span>いらないテキスト</span>
World
<span>いらないテキスト</span>
!!
</div>
[...document.querySelectorAll('div')[0].childNodes]
.filter(c => c.wholeText)
.map(t => t.nodeValue.trim())
.join(' ');
// Hello World !!
結果を取得する
以下で直前の出力をクリップボードにコピーできます。あとはエディタに貼り付けるなどして煮るなり焼くなりしましょう。
copy($_);
実践編
オライリー・ジャパン発行書籍一覧を取得する
isbnコード, 書籍タイトル, 価格, 発行日の一覧を取得してみます。
下記のコードをコンソールに貼り付けて実行したあと、
[...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, '-')
}
));
結果をクリップボードにコピーします。
copy($_)
結果、執筆時点での全441冊のデータを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を使うと作ったコードが流用できます。
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();
})();