8
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

PuppeteerでスクレイピングしてMacBook Pro整備済製品をカートに入れる!もう逃さない!

はじめに

MacBook Proを安く購入したい!

その場合、Apple製品を特別価格で購入できる認定の整備済製品という選択肢があります。(1年間の特別保証付き)
ただ、ラインナップに並んだとしても手に入れるのはなかなか難しく入荷時期は予測不可能で数量は非常に少なく、先着順となります。
なので、購入しようと思うと定期的にサイトにアクセスしてラインナップを確認する必要があります。

うーん、めんどい・・・

そこで、定期的にPuppeteerでスクレイピングして希望する商品があればカートに入れて通知が来るようにしたいと思います。

Puppeteerとは

PuppeteerはDevToolsプロトコルで ChromeまたはChromiumを制御するための高水準APIを提供するNodeライブラリです。
ブラウザで手動で行うことができるほとんどのことができます!

環境

$ node -v
v8.11.2

$ yarn -v
1.7.0

プロジェクトの作成

$ mkdir get-macbook
$ cd get-macbook

package.jsonを作成

$ yarn init -y

パッケージのインストール

$ yarn add node-notifier puppeteer -S

ファイルを作成&編集

今回は、「2017年6月発売モデル」の「15.4インチMacBook Pro 3.1GHzクアッドコアIntel Core i7 Retinaディスプレイモデル - スペースグレイ」をターゲットに30秒間隔でスクレイピングします。

$ vi index.js
index.js
const puppeteer = require('puppeteer')
const notifier = require('node-notifier')

const getMacbook = setInterval(() => {
  !(async() => {
    try {
      const browser = await puppeteer.launch()
      const page = await browser.newPage()

      // ページアクセス
      await page.goto('https://www.apple.com/jp/shop/browse/home/specialdeals/mac/macbook_pro/15')

      /**
       * findItem
       *
       * @return {object}
       */
      const findItem = await page.evaluate(() => {
        let result = {}
        const nodeList = document.querySelectorAll('.specs')

        for (let i = 0; nodeList.length > i; i++) {
          const target = nodeList[i].innerText

          const check_1 = '15.4インチMacBook Pro 3.1GHzクアッドコアIntel Core i7 Retinaディスプレイモデル - スペースグレイ'
          const check_2 = '2017年6月発売モデル'

          if (target.indexOf(check_1) !== -1 && target.indexOf(check_2) !== -1) {
            result.isHit = true
            result.itemNumber = i + 1

            break
          }
        }

        return result
      })

      if (findItem.isHit) {
        // ログイン部分のDOM読み込み(isLogin)
        await page.click('#ac-gn-bag > a')
        await page.waitFor(500)

        /**
         * isLogin
         *
         * @return {boolean}
         */
        const isLogin = await page.evaluate(() => {
          const findItem = document.querySelectorAll('.ac-gn-bagview-nav-item-signIn')

          return findItem.length ? false : true
        })

        if (!isLogin) {
          // ページ移動
          await page.click('.ac-gn-bagview-nav-link-signIn')
          await page.waitForNavigation()

          // ログイン情報設定
          await page.type('input[name="login-appleId"]', 'ログインメールアドレス');
          await page.type('input[name="login-password"]', 'ログインパスワード');

          // ログイン
          await page.click('#sign-in')
          await page.waitForNavigation()

          // ログイン部分のDOM読み込み(isCartEmpty)
          await page.click('#ac-gn-bag > a')
          await page.waitFor(500)

          /**
           * isCartEmpty
           *
           * @return {boolean}
           */
          const isCartEmpty = await page.evaluate(() => {
            const findItem = document.querySelectorAll('.ac-gn-bagview-message-empty')

            return findItem.length ? true : false
          })

          if (isCartEmpty) {
            // ページ移動
            await page.click(`.box-content > table:nth-child(${findItem.itemNumber}) .product > .purchase-info > a`)
            await page.waitFor('.form-submit-btn', {
              timeout: 120000
            })

            // カートに追加
            await page.click('.form-submit-btn button')

            // デスクトップ通知
            notifier.notify({
              'title': 'アイテムヒット!',
              'message': 'カートに追加したよ!'
            })

            console.log('カートに追加したよ!')
          } else {
            console.log('もうカートにあるよ!')
            clearInterval(getMacbook)
          }
        }
      } else {
        console.log('みつからないなー。。。')
      }

      browser.close()
    } catch (e) {
      console.error(e)
    }
  })()
}, 30000)

実行(スクレイピング開始)

$ node index.js
  • ヒット ~ カートに追加

カートに追加したよ!
  • ヒット ~ カートに追加済み(定期処理を解除します)
もうカートにあるよ!
  • ヒットなし
みつからないなー。。。

まとめ

以上、「PuppeteerでスクレイピングしてMacBook Pro整備済製品をカートに入れる!」の紹介でした。

今回は簡易的に作成しましたが、Cloud Functionsを使ったり、ターゲットを複数にしたり、通知を Slack や LINE等にするのもいいかもですね!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
8
Help us understand the problem. What are the problem?