Skip to content

Instantly share code, notes, and snippets.

@br4instormer
Last active September 21, 2024 16:08
Show Gist options
  • Select an option

  • Save br4instormer/ab47af07c680380165b3e6a777aebc08 to your computer and use it in GitHub Desktop.

Select an option

Save br4instormer/ab47af07c680380165b3e6a777aebc08 to your computer and use it in GitHub Desktop.
Wildberries out and about script
// ==UserScript==
// @name wildberries-search-and-add-to-cart2
// @namespace http://tampermonkey.net/
// @version 0.1
// @description Depicting a flurry of activity
// @author br4instormer
// @match https://wildberries.ru/*
// @match https://www.wildberries.ru/*
// @icon https://www.wildberries.ru/favicon.ico
// @grant none
// ==/UserScript==
(function () {
"use strict";
const SEARCH_QUERIES = [
"рубашка мужская",
"рубашка мужская белая",
"кружка",
"пенал",
];
const PRODUCT_IDS = ["12732467", "45474707", "60858047", "79931890"];
const SIZES = ["XXXL", "random", "0", "0"];
const BRANDS = ["MONOLITH", "1", "AVZ Fashion", "", "molti", "", "Lorex", ""];
const DO_ORDER = ["1", "0", "1", "0"];
const MAX_PAGES = 15;
const INTERVAL_SPIN_TIMEOUT = 1000;
const TIMEOUT_SLEEP = 2000;
const KEYDOWN_TIMEOUT = 150;
const SCROLL_ITERATION_HEIGHT = 500;
const ENTER_PRODUCT_PAGE_TIMEOUT = 5000;
const MAX_SHOW_CARDS = 100;
const ROOT_CATALOG_SELECTOR = "#catalog";
const PRODUCT_PAGE_SELECTOR = ".product-page";
const MAIN_PAGE_SELECTOR = ".main-page";
const CATALOG_OR_PRODUCT_PAGE_SELECTOR = `${ROOT_CATALOG_SELECTOR}, ${PRODUCT_PAGE_SELECTOR}, ${MAIN_PAGE_SELECTOR}`;
const SEARCH_INPUT_SELECTOR = "#searchInput";
const SEARCH_BUTTON_SELECTOR = "#applySearchBtn";
const NEXT_PAGE_SELECTOR = ".pagination-next";
const PRODUCT_CARD_SELECTOR = ".product-card";
const PRODUCT_IMAGE_SELECTOR = ".product-card__link";
const OPEN_PRODUCT_CONTAINER_SELECTOR =
"#app > div:nth-child(8)[style*='display: block']";
const ORDER_PRODUCT_BUTTON_SELECTOR =
".product-page__aside-container .order .btn-main";
const ORDER_SHOW_DESCRIPTION_SELECTOR = ".j-wba-card-item.j-description-btn";
const ORDER_SIZES_SELECTOR =
".product-page__sizes-wrap > ul.sizes-list .j-size";
const ORDER_ITEM_SIZE = ".sizes-list__size";
const FILTER_OPEN_BUTTON_SELECTOR = ".dropdown-filter__btn";
const FILTER_OPEN_BUTTON_TEXT = "Бренд";
const FILTER_SHOW_ALL_BRANDS_BUTTON_SELECTOR = ".filter__show-all";
const FILTER_INPUT_SELECTOR = ".filter__input-search input";
const FILTER_BRAND_CHECKBOX_SELECTOR =
".filter__item .checkbox-with-text__text";
const PAGINATION_BLOCK_SELECTOR = ".pagination";
const BASKET_LINK_SELECTOR = ".j-item-basket .navbar-pc__link";
const sleep = async (timeout) =>
new Promise((resolve) => setTimeout(resolve, timeout));
const random = (min, max) =>
Math.floor(Math.random() * (max - min + 1)) + min;
async function inputQuery(input, query) {
input.dispatchEvent(new FocusEvent("focus"));
input.value = "";
for (const sign of query) {
input.value += sign;
await sleep(KEYDOWN_TIMEOUT);
}
input.dispatchEvent(new InputEvent("input"));
}
function convertToRules(queries, ids, sizes, brandRules, doOrder) {
const rules = [];
for (let index = 0; index < queries.length; index++) {
rules.push({
query: queries[index],
id: ids[index],
size: sizes[index],
order: doOrder[index] === "1",
brandName: brandRules[index],
searchByBrand: brandRules[index] !== "1",
});
}
return rules;
}
async function waitForSelector(selector, timeout) {
return new Promise((resolve) => {
const intervalId = setInterval(
(selector, resolve) => {
if (!document.querySelectorAll(selector)) {
return;
}
clearInterval(intervalId);
resolve();
},
timeout,
selector,
resolve,
);
});
}
async function waitChildrenLoad(rootSelector) {
return new Promise((resolve) => {
const observer = new MutationObserver((mutationList) => {
const isAdded = mutationList.some(({ type }) => type === "childList");
if (!isAdded) {
return;
}
observer.disconnect();
resolve();
});
observer.observe(document.querySelector(rootSelector), {
childList: true,
subtree: true,
});
});
}
async function search(query) {
const input = document.querySelector(SEARCH_INPUT_SELECTOR);
const searchButton = document.querySelector(SEARCH_BUTTON_SELECTOR);
await inputQuery(input, query);
// @ts-ignore
searchButton.click();
}
async function revealAllProducts(selector) {
const hasNextPage = !!document.querySelector(selector);
let cardsCurent = document.querySelectorAll(selector).length;
let cardsTotal = cardsCurent;
if (!hasNextPage) {
return;
}
do {
cardsCurent = cardsTotal;
window.scrollTo({
left: 0,
top: document.body.scrollHeight,
behavior: "smooth",
});
await sleep(TIMEOUT_SLEEP);
cardsTotal = document.querySelectorAll(selector).length;
if (cardsTotal === MAX_SHOW_CARDS) {
break;
}
} while (cardsTotal > cardsCurent);
}
async function scrollProductPage() {
await waitForSelector(
OPEN_PRODUCT_CONTAINER_SELECTOR,
INTERVAL_SPIN_TIMEOUT,
);
await sleep(ENTER_PRODUCT_PAGE_TIMEOUT);
const showDescriptionButton = document.querySelector(
ORDER_SHOW_DESCRIPTION_SELECTOR,
);
if (showDescriptionButton) {
// @ts-ignore
showDescriptionButton.scrollIntoView({
behavior: "smooth",
block: "center",
});
await sleep(TIMEOUT_SLEEP);
// @ts-ignore
showDescriptionButton.click();
await sleep(TIMEOUT_SLEEP);
}
for (let iteration = 0; iteration < 4; iteration++) {
window.scrollBy({
behavior: "smooth",
top: SCROLL_ITERATION_HEIGHT,
left: 0,
});
await sleep(TIMEOUT_SLEEP);
}
window.scrollTo({ top: 0, behavior: "smooth" });
await sleep(TIMEOUT_SLEEP);
}
function findSizeCandidate(sizes, size) {
return Array.from(sizes).find(
(element) => element.querySelector(ORDER_ITEM_SIZE).innerText === size,
);
}
async function orderProduct(size) {
const isNoSize = size === "0";
const isRandom = size === "random";
if (isNoSize) {
// @ts-ignore
document.querySelector(ORDER_PRODUCT_BUTTON_SELECTOR).click();
return;
}
const sizes = document.querySelectorAll(ORDER_SIZES_SELECTOR);
const min = 0;
const max = sizes.length - 1;
const randomIndex = random(min, max);
const sizeCandidate = isRandom
? sizes[randomIndex]
: findSizeCandidate(sizes, size);
if (!sizeCandidate) {
return;
}
sizeCandidate.click();
// @ts-ignore
document.querySelector(ORDER_PRODUCT_BUTTON_SELECTOR).click();
}
async function searchByQuery(id, size, doOrder, maxPages) {
for (let page = 1; page <= maxPages; page++) {
const nextPageLink = document.querySelector(NEXT_PAGE_SELECTOR);
await revealAllProducts(PRODUCT_CARD_SELECTOR);
await sleep(TIMEOUT_SLEEP);
const candidate = document.querySelector(`[id='c${id}']`);
if (!candidate) {
// @ts-ignore
document
.querySelector(PAGINATION_BLOCK_SELECTOR)
.scrollIntoView({ behavior: "smooth", block: "center" });
await sleep(TIMEOUT_SLEEP);
nextPageLink.click();
await waitChildrenLoad(ROOT_CATALOG_SELECTOR);
await sleep(TIMEOUT_SLEEP);
continue;
}
candidate.scrollIntoView({ behavior: "smooth" });
await sleep(TIMEOUT_SLEEP);
// @ts-ignore
candidate.querySelector(PRODUCT_IMAGE_SELECTOR).click();
await scrollProductPage();
await sleep(TIMEOUT_SLEEP);
if (!doOrder) {
break;
}
await orderProduct(size);
await sleep(TIMEOUT_SLEEP);
break;
}
}
async function filterByBrand(brand) {
const brandsButton = Array.from(
document.querySelectorAll(FILTER_OPEN_BUTTON_SELECTOR),
)
// @ts-ignore
.find((element) => element.innerText === FILTER_OPEN_BUTTON_TEXT);
// @ts-ignore
brandsButton.click();
await sleep(TIMEOUT_SLEEP);
// @ts-ignore
document.querySelector(FILTER_SHOW_ALL_BRANDS_BUTTON_SELECTOR).click();
await sleep(TIMEOUT_SLEEP);
await inputQuery(document.querySelector(FILTER_INPUT_SELECTOR), brand);
await sleep(TIMEOUT_SLEEP);
const brandCheckbox = document.querySelector(
FILTER_BRAND_CHECKBOX_SELECTOR,
);
if (!brandCheckbox) {
return;
}
// @ts-ignore
brandCheckbox.click();
await sleep(TIMEOUT_SLEEP);
// @ts-ignore
brandsButton.click();
}
async function searchBrand(id, size, doOrder) {
await revealAllProducts(PRODUCT_CARD_SELECTOR);
await sleep(TIMEOUT_SLEEP);
const candidate = document.querySelector(`[id='c${id}']`);
if (!candidate) {
return;
}
candidate.scrollIntoView({ behavior: "smooth" });
await sleep(TIMEOUT_SLEEP);
// @ts-ignore
candidate.querySelector(PRODUCT_IMAGE_SELECTOR).click();
await scrollProductPage();
await sleep(TIMEOUT_SLEEP);
if (!doOrder) {
return;
}
await orderProduct(size);
await sleep(TIMEOUT_SLEEP);
}
async function goToCart() {
// @ts-ignore
document.querySelector(BASKET_LINK_SELECTOR).click();
}
async function main(rules, maxPages) {
await waitForSelector(
CATALOG_OR_PRODUCT_PAGE_SELECTOR,
INTERVAL_SPIN_TIMEOUT,
);
await sleep(TIMEOUT_SLEEP);
for (const rule of rules) {
const { query, id, size, order, brandName, searchByBrand } = rule;
await search(query);
await waitForSelector(ROOT_CATALOG_SELECTOR, INTERVAL_SPIN_TIMEOUT);
await sleep(TIMEOUT_SLEEP);
if (searchByBrand) {
await filterByBrand(brandName);
await sleep(TIMEOUT_SLEEP);
await searchBrand(id, size, order);
} else {
await searchByQuery(id, size, order, maxPages);
}
}
await goToCart();
console.warn("end");
}
main(
convertToRules(SEARCH_QUERIES, PRODUCT_IDS, SIZES, BRANDS, DO_ORDER),
MAX_PAGES,
);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment