Last active
September 21, 2024 16:08
-
-
Save br4instormer/ab47af07c680380165b3e6a777aebc08 to your computer and use it in GitHub Desktop.
Wildberries out and about script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==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