スクレイピングしたくてやっぱスクレイピングといえばPythonかなーと思ったけど、Node.js今自分の中で熱いからライブラリーー探したらあったよってこと🤭
読み方はパペッティア
Chromeの操作を自動化できるライブラリ(正しくはChromiumというChromeの元になったwebブラウザを操作する)
色々な操作をする時の参考になればいいなとおもう。
使ってみた感想はNode.js慣れしている人ならかなり簡単にスクレイピングできるなって感じです。
#####よく操作するクラスの種類#####
・Browser
ブラウザの生成、終了のときくらいしかつかわない
・Page
最も利用するクラス。ページの移動、formへの入力、Elementの取得などができる
・ElementHandle
Pageクラスの「.$()
, .$$()
, .$eval()
, .$$eval()
」などを利用してElementを取得したときの返り値はこのクラス。指定したElementに直接click()やhover()をさせたりできる。
#####【操作するブラウザを生成】#####
const browser = await puppeteer.launch({
headless: false //falseにしておくとBrowserが表示されて動く
slowMo: 50 //ブラウザ操作のスピード調整ができる
})
const page = await browser.newPage()
await page.setViewport({ //ブラウザ幅、高さの設定
width: 1200,
height: 800,
})
#####【指定したURLにログイン】#####
// LOGIN_URL = formのあるページ
// LOGIN_USER_SELECTOR = input[type=text]
// LOGIN_USER = username
// LOGIN_PASS_SELECTOR = input[type=password]
// LOGIN_PASS = password
// LOGIN_SUBMIT_SELECTOR = input[type=submit]
// TARGET_URL = login後開きたいページ
await page.goto(LOGIN_URL, { waitUntil: 'domcontentloaded' })
await page.waitForTimeout(2000)
await page.type(LOGIN_USER_SELECTOR, LOGIN_USER)
await page.type(LOGIN_PASS_SELECTOR, LOGIN_PASS)
await Promise.all([ // ログインボタンクリック
page.waitForNavigation({ waitUntil: 'networkidle0' }),
page.click(LOGIN_SUBMIT_SELECTOR),
])
await page.goto(TARGET_URL)
・{ waitUntil: load
, domcontentloaded
, networkidle0
, networkidle2
} の4つのoptionがあり、ページ遷移が終わったことを決定するタイミングの違いがある。
・page.waitForTimeout(2000)
指定したミリ秒で処理を一時停止する。
→ goto()
の後は数秒待たせないとtype()
によるform入力がうまく行かない確率が高まる。
・click()
とwaitForNavigation()
を同時に行うのは、click後のページ遷移が完了する前に次の処理を行ってエラーになるのを防ぐため。
#####【別ウィンドウ、サブウィンドウ、ポップアップへの対処法】#####
const [popup] = await Promise.all([
new Promise(resolve => page.once('popup', resolve)),
page.click('a[target=_blank]'),
])
・click()
で開かれたサブウィンドウなどは[popup]
に入る。サブウィンドウやポップアップを操作するにはこの[popup]
を操作する。
#####【要素を取得するよく使う4つの方法+1】#####
const element = await page.$("#name")
const elements = await page.$$(".names")
const value = await page.$eval("#name", el => el.value)
const values = await page.$$eval(".names", el => el.value)
const nameArray = document.querySelectorAll('.names')
・$(), $$(), は引数のセレクタでElementHandleクラスを生成する。
内部的に、document.querySelector()
とdocument.querySelectorAll()
が行われている。
・$eval(), $$eval()は第2引数に関数をセットすることで引数に指定した要素を操作した値を取得したりできる。
・ElementHandleクラスは、forEachなどができなくて不便なことがある。そういうときは、
document.querySelectorAll()
で要素を取得すれば普通にforEachなどを適用させられる。
#####【タブ切り替え】#####
await page.bringToFront()
// page.に指定したページを操作できるように切り替える
#####【実際に使うときの流れ】#####
ログイン→ログイン後画面でクリック→出てきたサブウィンドウ操作→元画面に戻る→終了
const puppeteer = require('puppeteer')
require('dotenv').config()
const env = process.env
(async () => {
//ブラウザ生成
const browser = await puppeteer.launch({
headless: false //falseにしておくとBrowserが表示されて動く
slowMo: 50
})
const page = await browser.newPage()
await page.setViewport({
width: 1200,
height: 800,
})
//form入力&submitでログイン
await page.goto(env.LOGIN_URL, { waitUntil: 'domcontentloaded' })
await page.waitForTimeout(2000) //少し待たせないと入力ミスが起きやすい
await page.type(env.LOGIN_USER_SELECTOR, env.LOGIN_USER)
await page.type(env.LOGIN_PASS_SELECTOR, env.LOGIN_PASS)
await Promise.all([ // ログインボタンクリック
page.waitForNavigation({ waitUntil: 'networkidle0' }),
page.click(env.LOGIN_SUBMIT_SELECTOR),
])
//ログイン後ページへ飛ぶ
await page.goto(env.TARGET_URL)
//clickによって表示されたpopup取得
const [popup] = await Promise.all([
new Promise(resolve => page.once('popup', resolve)),
page.click('input[value="~"]'),
])
//もとのページへback
await page.goBack({ waitUntil: 'networkidle0' })
//ブラウザ終了
await browser.close()
})()
・dotenvモジュールで定数管理している
puppeteerを使う上での注意点
ページロードが終わるタイミングと要素取得のタイミングがズレると要素の取得ができなかったり、ページ遷移が終わる前に次の処理に行ってしまっていたりするので、常にタイミングには気をつけないといけない。