Amazon price tracker with Zenaton & Puppeteer

Dec 31, 2019 (4 min read)

Amazon use dynamic pricing and prices of the products we are interested in can vary depending on many things.

We will create a tool to set price alerts on any Amazon product. With no more than 50 lines of codes.

TL;DR: project code is available here: https://github.com/MartinRdw/amazon-price-tracking, feel free to clone/deploy to Heroku it

For this we will use:

  • Puppeteer: a Node library which provides a high-level API to control Chrome
  • Zenaton: a SaaS service allowing to build tasks and workflows quickly

Puppeteer will allow us to scrape the Amazon product page to get the current price and Zenaton will allow us to run the code every X hours and send alerts.

Configuration

To start using Zenaton, you have to complete 4 steps:

curl https://install.zenaton.com | sh
  • Configure it with your Zenaton credentials.

We will use a .env file, the simplest is to clone the example repo here: https://github.com/zenaton/examples-node

git clone https://github.com/zenaton/examples-node
cp -n .env.example .env

If you are not interested by the examples you can just keep boot.js, client.js, .env and package.json files.

Update .env with your credentials from https://app.zenaton.com/api and run npm i .

  • Launch the Zenaton Agent

Start the agent and make it listen to your configuration:

zenaton start && zenaton listen — env=.env — boot=boot.js

Workflow creation

The workflow will take 4 variables as parameters:

  • The name of the product
  • The product url
  • The price of the alert
  • The number of hours between each check
touch Workflows/TrackAmazonPrice.js

The code is quite simple:

'use strict'
const { workflow, duration } = require('zenaton')

module.exports = workflow('TrackAmazonPrice', function * (product, productUrl, alertPrice, hoursBetweenEachCheck) {
  const slack = this.connector('slack', 'your_slack_connector_id')

  while (true) {
    // get product price
    const price = yield this.run.task('GetAmazonProductPrice', productUrl)

    // send alert if needed
    if (parseFloat(price) < alertPrice) {
      slack.post('chat.postMessage', {
        body: {
          text: `🚨 ${product} goes below ${alertPrice}€ \n 💰 Current price: ${price} \n ➡️ ${productUrl}`,
          as_user: true,
          channel: 'amazon'
        }
      })

      // terminate workflows
      this.terminate()
    }

    // wait 4 hours before next check
    yield this.wait.for(duration.hours(hoursBetweenEachCheck))
  }
})

TrackAmazonPrice.js

Some explanations:

We use the wait function of Zenaton to wait 4 hours between each price analysis https://zenaton.com/documentation/node/workflow-waiting/#duration

To send the alert, I chose to use one of the new Zenaton features to receive a message in Slack: connectors.

To configure a new connector you just have to go here: https://app.zenaton.com/connectors

Search for Slack and click on “Add”.

Replace your_slack_connector_id in the code with the id you have here:

1 FUY 7 YLL on LJ m5v Yz Fj Qk FA 5d40c5fe4b

More info on connectors here: https://zenaton.com/documentation/node/api-connectors

Task creation

We still have to create the Task which will allow us to get the price on Amazon. TheGetAmazonProductPriceof our workflow code.

touch Tasks/GetAmazonProductPrice.js

This is where we will use Puppeteer:

npm -i puppeteer

Goal of the task:

  • Open a Chromium browser
  • Go to amazon product URL
  • Scrape the webpage to find the #attach-base-productselector (the price is in this input)
  • Return the price

Code :

const { task } = require('zenaton')
const puppeteer = require('puppeteer')

const SELECTORS = {
  productTitle: '#productTitle',
  productPrice: '#attach-base-product-price'
}

module.exports = task('GetAmazonProductPrice', async function (productUrl) {
  const browser = await puppeteer.launch(
    {
      headless: true,
      defaultViewport: {
        width: 1100,
        height: 840
      }
    }
  )
  const page = await browser.newPage()
  await page.goto(productUrl)

  // wait page load
  await page.waitForSelector(SELECTORS.productTitle, { visible: true })

  // find price
  const priceInput = await page.$(SELECTORS.productPrice)
  const price = await page.evaluate(element => element.value, priceInput)

  await browser.close()

  return price
})

GetAmazonProductPrice.js

The few Puppeteer functions used are well documented here: https://github.com/puppeteer/puppeteer/blob/master/docs/api.md

Launch the workflow

It remains for us to launch our workflow.

touch launch_amazon_workflow.js

This is an example to get an alert when the iPhone 11 Pro will drop below $1000:

const run = require('./client').run

const product = 'Iphone 11 Pro - 64go'
const productUrl = 'https://www.amazon.fr/Apple-iPhone-11-Pro-64-Go/dp/B07XRRNYWK'
const alertPrice = 1000
const hoursBetweenEachCheck = 4

// run workflow
run.withTag(`${product} (${alertPrice}€)`).workflow('TrackAmazonPrice', product, productUrl, alertPrice, hoursBetweenEachCheck)

launch_amazon_workflow.js

Just run node launch_amazon_workflow.js to launch your workflow.

withTag() allows us to give a name to each of our workflows. Thanks to that, by going back to the Zenaton dashboard we can see the products currently tracked:

1 XEI l Io Txjb M7d Em AP wh Tw 2a7414a16a

Products currently tracked

The code is available here: https://github.com/MartinRdw/amazon-price-tracking/tree/no-web-app

Bonus: Frontend + deploy to Heroku

You can create an interface to more easily interact with your Zenaton workflows:

1 m 5 Z4w UF z XFX 8 Qss Y1b k Q 6d0f885437

Create workflows from a frontend application

It’s composed of:

The code (with frontend app + deploy to Heroku) is available here: https://github.com/MartinRdw/amazon-price-tracking