Building My First Sneaker Bot: A Beginner's Guide to Puppeteer and Automation
Example code given!
Welcome to my exciting coding journey! In this newsletter, I'll be diving into the captivating world of sneaker bots, Puppeteer, and real-life application of coding skills. As an aspiring developer, I've come to realize that my ability to retain knowledge significantly improves when I apply it to tangible projects. That's why I embarked on this coding adventure—to create my very own sneaker bot using Puppeteer.
Disclaimer: I did not manage to create a completely working sneaker bot (and I would really like to know why and how in the comments) but I still want to share what I’ve learnt to help me better retain this knowledge too. I also acknowledge that there’s still much more to learn and what I say may not be fully correct. Besides, content written in this blog is last updated in June. After further studies and experimentation I am aware that some of them are not fully accurate but it still serves its purpose well!
Throughout this article, I'll delve into the essential HTML and Puppeteer codes that play a pivotal role in the creation of this sneaker bot. From crafting dynamic web elements to automating repetitive tasks, we'll cover the key codes that breathe life into this project. By dissecting these crucial snippets, you'll gain insights into the inner workings of Puppeteer and how it enables seamless automation.
Starting with a screen recording of my progress:
Without further ado, let's dive in!
Here’s What I’ve learned
As a quick introduction, all texts, images and logos are introduced to your webpage by HTML code. To better visualise this, open Developer Tools as shown in the video below.
Under the ‘elements’ page, the code that we see below are HTML codes. Some of the important HTML code that we need to know for this project are:
1) img:
The <img>
tag is used to embed an image in an HTML page.
Images are not technically inserted into a web page; images are linked to web pages. The <img>
tag creates a holding space for the referenced image.
The <img>
tag has two required attributes:
- src - Specifies the path to the image. As seen in Figure 2, src is taking the user to a https link. If you manually click into the link, it will bring u to a page with only that photo.
- alt - Specifies an alternate text for the image, if the image for some reason cannot be displayed
2) div:
The <div>
tag defines a division or a section in an HTML document.
The <div>
tag is used as a container for HTML elements - which is then styled with CSS or manipulated with JavaScript.
The <div>
tag is easily styled by using the class or id attribute.
Any sort of content can be put inside the <div>
tag!
3) class:
In HTML, the class attribute is used to assign one or more CSS classes to an element. The class attribute allows you to specify a class name or multiple class names separated by spaces. CSS classes are used to define styles and apply them to elements in a web page.
Due to this function of class, many elements on your screen that is of the same category (for this case is each of the different shoe pictures), has the same class name. This is somewhat like if you want to make your Instagram page aesthetic, you want all your post to use the same filter. Therefore, you name all your pictures as a single class name and apply the filter to just that single class to standardise the filter of every picture.
As seen below, the different shoes has the same class name. Knowing which class name interacts with different elements on the web page allows us to use JavaScript to effectively interact with the web page.
4) <a> and href:
The <a>
tag defines a hyperlink. The href
attribute specifies the URL of the page the link goes to:
In figure 2, the link from href bring us to the page where we can choose our shoe size, that’s why when we click on the image, we get directed to that page.
5) id:
The id attribute is used to mark each unique element. No 2 id are the same. Therefore, it can come in very handy when you know what is the id of the button you want to click on.
Later, their positions, the size, the colour and fonts of the elements are then determined by the CSS code (that’s usually not seen). This will make the webpage look more dynamic instead of being 2D like the one below that’s built fully through HTML code.
Puppeteer
Puppeteer is a Node.js library which provides a high-level API to control Chrome/Chromium over the DevTools Protocol. For Puppeteer to work, we must first have installed Node.js . Then, install puppeteer with the help of this guide. (essentially it’s just typing the code below in an empty folder.
npm install puppeteer
Common function used in building this bot are:
const puppeteer = require('puppeteer'); // to import puppeteer
async function functionName(){…}
Below is a template is a template to create and async function
in JavaScript’s case:
newPage()
To “activate” puppeteer, we will use the launch
function. Subsequently a new web page will be created using the newPage()
function.
async function givePage(){
const browser = await puppeteer.launch({
headless: false});
// or if header true --> await puppeteer.launch();
const page = await browser.newPage();
return page;
}
Await is usually used to unwrap promises by passing a Promise as the expression . Using await pauses the execution of its surrounding async function until the promise is settled (that is, fulfilled or rejected). Almost every line of command in a async function has to be followed by await to ensure sequential running of the commands.
NOTE: await cannot be used outside an async function
async function addToCart(page){
await page.goto(url);
await page.waitForSelector("li[class= 'size va-sm-m d-sm-ib va-sm-t ta-sm-c ']");
const sizes = await page.$$("li[class= 'size va-sm-m d-sm-ib va-sm-t ta-sm-c ']");
for (let i = 0; i < sizes.length; i++) {
const lizz = sizes[i];
const value = await lizz.evaluate((el) => el.textContent);
if (value === 'US 9.5') {
await lizz.click("button[class='size-grid-dropdown size-grid-button'][data-qa='size-dropdown']");
break;
}
}
goto(url);
“Inserts” a url link at our web page created during newPage() where url is predefined through —> const url = ‘…’;
waitForSelector()
await page.waitForSelector("li[class= 'size va-sm-m d-sm-ib va-sm-t ta-sm-c ']");
This function waits for the below html code to be loaded on the Developer Tools before continuing with the rest of the code.
<li class="size va-sm-m d-sm-ib va-sm-t ta-sm-c " data-qa="size-available"><button type="button" class="size-grid-dropdown size-grid-button" data-qa="size-dropdown">US 9.5</button></li>
The waitForSelector function can come in 3 main forms:
Waiting for an element with a specific attribute and value to appear:
await page.waitForSelector('input[type="submit"]');
Waiting for an element with a specific class to appear:
await page.waitForSelector('.myElementClass');
Waiting for an element with a specific ID to appear:
await page.waitForSelector('#myElementId');
await page.$$(“li[class = ‘…’]”)
The $$ sign suggests that there are multiple elements with the class name as indicated below. by calling this line of code, we are generating a list of DOM. In Figure 4 below, the code will create a list of 4 objects. Which can then be assessed through for loops.
However, if you only wish to select the very first DOM with the class = ‘…’, you can change $$ to $
const sizes = await page.$$("li[class= 'size va-sm-m d-sm-ib va-sm-t ta-sm-c ']"); // returns a list of DOM
const sizes = await page.$("li[class= 'size va-sm-m d-sm-ib va-sm-t ta-sm-c ']"); // returns the first DOM where the value is "US 9"
await lizz.evaluate((el) => el.textContent);
In the provided code snippet, lizz.evaluate((el) => el.textContent)
is used to retrieve the text content of the lizz
element. The evaluate()
method takes a function as an argument, and in this case, the function (el) => el.textContent
is passed.
The (el) => el.textContent
function is an arrow function that receives the el
parameter, representing the element being evaluated. Within the function, el.textContent
is accessed to retrieve the text content of the element.
So, lizz.evaluate((el) => el.textContent)
will evaluate the provided function in the context of the lizz
element and return its text content.
For this case, there is no ‘value’ attribute for each of the list items therefore we have to use an alternative code:
const value = await lizz.evaluate((el) => el.textContent);
NOTE: This code is used only when the unique identifier is a text.
If unique identifier is value=’US 9’, use this code:
await (await lizz.getProperty('value')).jsonValue(); // gets the value of 'value' and converts it into text
input.getProperty('value')).jsonValue();
is first getting the JSHandle
to the value
property of the input
element, then calling jsonValue()
on it to extract the actual string value of the value
attribute of the element, and finally assigning that value to the value
constant.
click()
Puppeteer can click on various types of elements in a web page. Here are some examples:
Buttons:
<button>
elements can be clicked using Puppeteer.click(“button[ class= ‘…’]”)
Links:
<a>
elements representing hyperlinks can be clicked.Checkboxes:
<input>
elements withtype="checkbox"
can be checked or unchecked by clicking on them.Radio buttons:
<input>
elements withtype="radio"
can be selected by clicking on them.click(“input[ type= ‘radio’]”)
Dropdown menus:
<select>
elements can be interacted with by clicking on them and selecting an option from the dropdown.Custom elements: Puppeteer can also click on custom elements or elements created using JavaScript frameworks like React, Vue.js, or Angular.
clicking on unique id:
click(#myElementId);
It's important to note that Puppeteer interacts with elements based on their CSS selector or XPath, so any element that can be targeted using these methods can be clicked using Puppeteer.
type(placeToType, WhatToType)
similar to the video in figure 1, you can hover around text boxes and find the unique ID of each input boxes. As most of them are input boxes, the first parameter will be in this format: “input[id=’city’]”. The second parameter on the other hand is what you want to fill in.
await page.type("input[id='city']", 'Singapore');
Lessons learned
There are many different programming language out there, all of them having different functions. However, what’s important is not how many functions you know, but how many you can apply. Though the functions stated in this article are little, it is actually sufficient to get you started on building your bot — something that can be profitable in the long run. After this project, I’m sold that projects are the fastest way to learn how to code and I hope that after reading this, you will feel motivated to start one of your own!
NOTE: if you are planning to make a snkr bot like me, you should be aware that copy and pasting codes might not work that well. This is because the code for different shoes differ and if I’m not wrong, Nike might be changing their code from time to time even for the same shoes.
That’s all from me today! Have a nice one!