How to Build a Price Monitoring Slack Bot 🤑
Tags: python, tutorial, bot, webscraping
Published: 2020-09-03 15:36:16

I just got a new Switch (because what else is there to do these days?!) And I really wanna get the new [Paper Mario](https://www.bestbuy.ca/en-ca/product/paper-mario-the-origami-king-switch/14643866) game. Except, I'm not keen on paying full pop for the title... So. I built a price monitoring Slack Bot to tell me if it's on sale! Here's how you can build one too~

First, install [gazpacho](https://gazpacho.xyz/), [hickory](https://github.com/maxhumber/hickory), and [slackclient](https://github.com/slackapi/python-slackclient):

```sh
pip install gazpacho hickory slackclient
```

Identify the [page](https://www.bestbuy.ca/en-ca/product/paper-mario-the-origami-king-switch/14643866) you want to scrape:

![BestBuy](https://dev-to-uploads.s3.amazonaws.com/i/zb53hk8qjsivhwvlfn8g.png)

Grab the URL, open a Text Editor (I like [Atom](https://atom.io/)), create a python file called `mario.py` and import gazpacho:

```python
from gazpacho import get, Soup
```

Download the html with the `get` function and stuff it in a `Soup` wrapper:

```python
url = "https://www.bestbuy.ca/en-ca/product/paper-mario-the-origami-king-switch/14643866"
html = get(url)
soup = Soup(html)
```

Figure out how the price is being rendered on the page by right clicking on it and opening "Inspect Element:

![Inspect Element](https://dev-to-uploads.s3.amazonaws.com/i/eli23ut8ex4f9fzmp2nn.png)

Note the specific tag and element attributes: 

![HTML](https://dev-to-uploads.s3.amazonaws.com/i/phvj7vjbq0umz0fagx7y.png)

In this case the relevant block of html on the page is:

```html
<div class="price_FHDfG large_3aP7Z " aria-hidden="true">
  $79
  <span class="rightEndPrice_6y_hS">99</span>
</div>
```

Use the `find` method with partial matching (`strict=False`) enabled to extract the price and format it as a float:

```python
price = soup.find("div", {"class": "price"}, strict=False)
lhs = price.text
rhs = price.find("span", {'class': 'rightEndPrice'}).text
price = float(f"{lhs}.{rhs}".replace('$', ""))
```

Might as well grab the title too:

```python
title = soup.find('h1', {'class': 'productName'}).text
```

Print out both to confirm that everything is working:

```python
print(title, price)
# Paper Mario: The Origami King (Switch) 79.99
```

Your entire script should look like this:

```python
# mario.py
from gazpacho import get, Soup

url = "https://www.bestbuy.ca/en-ca/product/paper-mario-the-origami-king-switch/14643866"
html = get(url)
soup = Soup(html)

title = soup.find('h1', {'class': 'productName'}).text
price = soup.find("div", {"class": "price"})
lhs = price.text
rhs = price.find("span", {'class': 'rightEndPrice'}).text
price = float(f"{lhs}.{rhs}".replace('$', ""))

print(title, price)
```

To schedule this scraper and turn it into a Slack Bot go to [https://api.slack.com/apps](https://api.slack.com/apps) and click **Create New App**:

![Slack API](https://dev-to-uploads.s3.amazonaws.com/i/3pa0egq56th04ybh9w24.png)

Find and click on **OAuth & Permissions**:

![OAuth](https://dev-to-uploads.s3.amazonaws.com/i/jwih2m5sihyol16f6yjz.png)

Search for the **chat:public.write** scope and add it:

![Scopes](https://dev-to-uploads.s3.amazonaws.com/i/h085mlh20bpdj6oasxdz.png)

Scroll back up to the top of the page and install the app into your Slack Workspace:

![Install](https://dev-to-uploads.s3.amazonaws.com/i/2md3srbgv3zl6ql6uqtp.png)

Copy the generated token to the `mario.py` file as a string:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/eek9zr1panpra8i0w921.png)

```python
token = "xoxb-00000000-0000000000-00000000000"
```

Import slack and instantiate an instance of `WebClient`:

```python
from slack import WebClient
client = WebClient(token=token)
```

Send a message to a Slack channel with the `chat_postMessage` method:

```python 

client.chat_postMessage(channel="mario", text=f"{price} - {title}")
```

Re-factor everything to make it look like:

```python
# mario.py

from gazpacho import get, Soup
from slack import WebClient

token = "xoxb-00000000-00000000-000000000000000"
client = WebClient(token=token)

url = "https://www.bestbuy.ca/en-ca/product/paper-mario-the-origami-king-switch/14643866"
html = get(url)
soup = Soup(html)

title = soup.find('h1', {'class': 'productName'}).text
price = soup.find("div", {"class": "price"})
lhs = price.text
rhs = price.find("span", {'class': 'rightEndPrice'}).text
price = float(f"{lhs}.{rhs}".replace('$', ""))

client.chat_postMessage(channel="mario", text=f"${price} - {title}")
```

You really shouldn't expose your token as plaintext but, whatever...

![YOLO](https://media.giphy.com/media/84EQtaETcza8w/giphy.gif)

Run the script at the command line to make sure it works:

```sh
python mario.py
```

Scheduling this script to every 30 seconds with hickory is dead simple. Just use `hickory schedule` at the command line:

```sh
hickory schedule mario.py --every=30s
```

To check the status of the schedule run:

```sh
hickory status
# ID      FILE         STATE    RUNS  INTERVAL
# ea6b74  mario.py     waiting  0     30
```

And to kill the schedule:

```sh
hickory kill mario.py
```

Scheduling this Price Monitoring Slack Bot to execute every day at 9:00am is probably more realistic:

```sh
hickory schedule mario.py --every=@9:00am
```

And that's it! 🤑

If you've built something cool with [gazpacho](https://gazpacho.xyz/) and/or [hickory](https://github.com/maxhumber/hickory) get in touch, I'd love to hear about it!