SELENIUM QUICK START
привет другалек! наконец у меня дошли руки написать статью по селениуму. как большинство просило - показать использование селены на реальном примере! бери чипсики чаек плюшки печеньки там шо кушаешь и лфг читать!
оглавление
что такое селениум и для чего его юзать
если обьяснять коротко - селениум это инструмент для того чтобы ты мог абузить какую то хуйню или перефразируя автоматизировать дефолт рутинные действия в браузере на кучу акков в любых количествах
с помощью селениума можно абузить формы, преминты, в общем бля все что можно делать в браузере дефолт юзеру можно делать с селениумом (ну или почти все)
подготавливаем среду обитания нашего скрипта
ты уже должен быть знаком с тем что такое редактор, жиэс и так далее. если ты не знаком прошу любить и жаловать — сюда
убедись что у тебя установлен nodejs и npm (с nodejs автоматически поставляется и npm)
и если у тебя все еще нет пакет менеджера yarn то установи его
как установить yarn
npm i -g yarn
открывай любимый редактор, открывай терминал и директорию с проектом, затем вводи в терминал:
yarn init -y yarn add selenium-webdriver webdriver-manager
и вписываем туда базовую базу:
const { Builder } = require('selenium-webdriver') const driver = new Builder().forBrowser('chrome').build() ;(async () => { await driver.get('https://google.com/ncr') })()
запускаем скриптик node index.js
и у нас открывается браузер с гуглом — ахуенно
базовые базы селениума
щас не будет сходу абуз какого то проекта, давай поиграемся с гуглом
например ты хочешь чтобы бот вводил за тебя в гугле "заряд от ахмеда расулова" и нажимал поиск
давай попробуем это сделать и разберемся с тем как работать с селеной
чтобы ввести какой то текст, надо найти элемент в DOM
что такое DOM?
DOM (Document Object Model) — дерево элементов. любая страница в интернете состоит из элементов (если ты знаком с хтмл то ты понимаешь о чем я). соответственно во главе дерева стоит элемент html
, внутри есть head
и body
. в body
находится вся начинка сайта (то есть все отображающиеся на странице элементы). к чему это я? если ты хочешь ввести что то в инпут селеной, то надо найти этот элемент в DOM
примерно это будет выглядеть так:
html > body > div > div > input
^ инпут который нам нужен вложен в несколько других элементов
как найти элемент в DOM?
все просто. жмешь правой кнопкой мыши по желаемому элементу и нажимаешь "Просмотреть код" (это на маке, на винде может быть типа просмотреть код элемента и тд)
после того как ты нажал на нужный пункт открывается следующая ебанина
имей ввиду что имбовее всего искать элементы юзая консоль хром браузера (не оперы или не дай бог амиго)
консоль дает тебе понять какие элементы вложены в какие и как до них достучаться. если ты наведешься на другие элементы они должны подсвечиваться что дает тебе понять опять же какие элементы где находятся на странице
для того чтобы ты мог ввести какой то текст в элемент тебе нужно искать именно элемент input и никакой другой (ну разве что textarea, или кастомизированные div в которые можно вводить текст, такие есть у твиттера, но пока не вдавайся в подробности о них)
я нашел инпут, как заставить бездушную машину ввести текст в него?
окей это круто ты нашел инпут, теперь нам нужно получить указатель на этот элемент в дереве DOM. сделать это можно кликнув правой кнопкой по элементу в консоли и выбрав Copy -> Copy selector
сохрани эту ебанину и погнали сделаем так чтобы мы ввели заветный текст "заряд от ахмеда расулова" в поиск
const { Builder, By } = require('selenium-webdriver') const driver = new Builder().forBrowser('chrome').build() ;(async () => { await driver.get('https://google.com/ncr') const searchInput = await driver.findElement( By.css( 'body > div.L3eUgb > div.o3j99.ikrT4e.om7nvf > form > div:nth-child(1) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input' ) ) await searchInput.sendKeys('заряд от ахмеда расулова') })()
для того чтобы найти какой либо элемент в селениуме мы используем метод findElement
у driver
. в качестве аргумента мы должны указать путь до элемента. в данном случае мы ищем элемент по селектору (Copy selector). также можно найти элемент по XPath, classname, id, названию тега, тексту ссылки и тд
чтобы указать путь до элемента используя селектор мы вызываем метод css
у класса By
который помогает нам строить пути до элементов
метод By.css
помогает найти элемент используя селектор, в качестве аргумента передаем непосредственно сам селектор
вот мы нашли элемент, сохранили его в переменную, теперь давай сделаем так чтобы бот написал туда текст, а здесь мы будем использовать метод sendKeys
у найденного элемента, в качестве аргумента передаем текст который мы хотим ввести
запускаем скрипт, тестируем и вуаля
ну, тут осталось только нажать поиск!
в гугле для того чтобы начать поиск необходимо нажать ентер, но в других сайтах возможно тебе придется кликать на кнопку для поиска (соответственно искать кнопку, и кликать на нее, алгоритм такой же)
чтобы нажать ентер после того как мы ввели текст в инпуте мы просто добавляем
await searchInput.sendKeys('заряд от ахмеда расулова', Key.RETURN)
Key.RETURN
вторым аргументом и все готово! не забудь только добавить класс Key в импорте селениума
const { Builder, By, Key } = require('selenium-webdriver')
запустив скрипт ты увидишь что поиск пошел!
ну и закончим раздел базовых баз тем что мы кликнем на первую попавшуюся ссылку. тут мы будем задействовать клик следственно алгоритм такой:
нам нужна первая ссылка, соответственно нажимаем правой кнопкой по первой ссылке и идем искать нужный элемент
в дереве дом мы видим элементом выше тег a
который отвечает за ссылку в html. в целом нам нужна ссылка, но мы можем кликнуть также на дочерние элементы (элементы которые входят в тег a
, то есть ссылки на которую мы хотим кликнуть)
;(async () => { ...code const firstLink = await driver.findElement( By.css( '#rso > div:nth-child(1) > div > div > div.Z26q7c.UK95Uc.jGGQ5e > div > a > h3' ) ) await firstLink.click() })()
находим элемент таким же образом как и выше, а для того чтобы кликнуть на элемент мы используем метод click
у элемента. таким образом бот кликнет на элемент — все просто
маскируем нашу селену
ddos guard выше скорее всего оттого что ты юзаешь прокси из датацентра (не резидент) или проще говоря хуевое прокси. соответственно нам нужно подключить не палевное прокси
для того чтобы нам замаскировать каждый вход/сессию в селене нам нужно периодически менять user agent, proxy, выключить опции которые определяют наш браузер как автоматизированное по, сменить платформу (win/mac/linux), отключить детект WebRTC и тд
добавляем прокси в селену
на жиэсе селена почему то не очень кормится проксиями через addProxy
или addArguments
с опцией --proxy-server
в хром. поэтому я решил добавить прокси как кастомное расширение в хроме
создаем файл background.js
и manifest.json
для нашего кастомного расширения
const PROXY = { host: 'zaryadotahmedarassulova.com', // proxy host port: 1337, // proxy port username: 'ahmedrassulov', // proxy username (если прокси приват) password: 'zaryad228' // proxy password } const getProxyConfig = (host, port) => { mode: 'fixed_servers', rules: { singleProxy: { scheme: 'http', host, port }, bypassList: ['localhost'] // не стучим с прокси в локалхост } } chrome.proxy.settings.set({ value: getProxyConfig(PROXY.host, PROXY.port), scope: 'regular' }, () => {}) chrome.webRequest.onAuthRequired.addListener( () => ({ authCredentials: { username: PROXY.username, password: PROXY.password } }), { urls: ['<all_urls>'] }, ['blocking'] )
{ "version": "1.0.0", "manifest_version": 2, "name": "Chrome Proxy", "permissions": [ "proxy", "tabs", "unlimitedStorage", "storage", "<all_urls>", "webRequest", "webRequestBlocking" ], "background": { "scripts": ["background.js"] }, "minimum_chrome_version": "22.0.0" }
manifest_version
не новая но это не сильно критично тк наше расширение не паблик. для 3 манифест версии я не нашел пермишена webRequestBlocking
так что если кто знает инфу буду рад если поделитесь
после чего сжимаем в zip файл эти два файла и переименовываем в proxy.zip
добавляем расширение для антидетекта webRTC
теперь давай скачаем готовое расширение для антидетекта webRTC
я скачал расширение в zip через chrome web store используя расширение CRX Extractor/Downloader
если тебе лень проделывать все эти шаги и ты хочешь просто скачать расширение — ссылка на file.io
маскируем user agent
для этого скачай модуль fake-useragent
yarn add fake-useragent
пишем код для маскировки селены (нинзя пон тип 🥷)
const { Builder, Capabilities } = require('selenium-webdriver') const { Options } = require('selenium-webdriver/chrome') const fakeUseragent = require('fake-useragent') // сетапаем наши новые опции const options = new Options() // маскируемся под человека отключая режим автоматизации в хроме .excludeSwitches('enable-automation') .addArguments( 'disable-popup-blocking', // отключаем блок попапов 'disable-notifications', // отключаем уведомления 'no-sandbox', // отключаем "песочницу" `user-agent=${fakeUseragent()}`, // меняем user agent, 'start-maximized' // запускаем браузер в фулл экране ) // добавляем наши расширения .addExtensions(['proxy.zip', 'webrtc-control.zip']) const capabilities = Capabilities().chrome() // отключаем возможность устанавливать automation расширения // типа CaptureScreenshot и тд .set('useAutomationExtension', false) const driver = await new Builder() .forBrowser('chrome') .setChromeOptions(options) // < добавляем .withCapabilities(capabilities) // < добавляем .build() ;(async () => { await driver.get('https://whatismyip.com') // тестим ip })()
вот так ты замаскировал селену!
пробуем запускать весь код теперь:
const { Builder, Capabilities, By, Key } = require('selenium-webdriver') const { Options } = require('selenium-webdriver/chrome') const fakeUseragent = require('fake-useragent') // сетапаем наши новые опции const options = new Options() // маскируемся под человека отключая режим автоматизации в хроме .excludeSwitches('enable-automation') .addArguments( 'disable-popup-blocking', // отключаем блок попапов 'disable-notifications', // отключаем уведомления 'no-sandbox', // отключаем "песочницу" `user-agent=${fakeUseragent()}`, // меняем user agent, 'start-maximized' // запускаем браузер в фулл экране ) // добавляем наши расширения .addExtensions(['proxy.zip', 'webrtc-control.zip']) const capabilities = Capabilities().chrome() // отключаем возможность устанавливать automation расширения // типа CaptureScreenshot и тд .set('useAutomationExtension', false) const driver = new Builder() .forBrowser('chrome') .setChromeOptions(options) // < добавляем .withCapabilities(capabilities) // < добавляем .build() ;(async () => { const searchInput = await driver.findElement( By.css( 'body > div.L3eUgb > div.o3j99.ikrT4e.om7nvf > form > div:nth-child(1) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input' ) ) await searchInput.sendKeys('заряд от ахмеда расулова', Key.RETURN) const firstLink = await driver.findElement( By.css( '#rso > div:nth-child(1) > div > div > div.Z26q7c.UK95Uc.jGGQ5e > div > a > h3' ) ) await firstLink.click() })()
кастомный язык в браузере
можно указать кастомные языки в селене используя setUserPreferences
const options = new Options() .setUserPreferences({ 'intl.accept_languages': 'es,es-ES' // испанский })
абузим хуйню?
наша цель — вайтлист в obligate
ты можешь быстренько пройтись по флоу join waitlist и ты увидишь что там форма где надо что то вводить, что то тыкать, и там где вводишь — жать ентер после ввода (мы уже с тобой это изучали). в конце есть кнопка сабмит которую просто тыкаем и мы заджойнились в вайтлист. чем не лфг? флоу выглядит просто
давай автоматизируем действия для одного акка — Ahmed Rassulov
для упрощения я не буду переписывать весь код который был до этого
для начала тебе надо перейти на сайт и нажать на кнопку Join waitlist, ты уже знаешь как найти ее, соответственно пишем:
await driver.get('https://obligate.com') const joinWaitlistButton = await driver.findElement( By.css( 'body > div.hero.wf-section > div.html-embed.w-embed.w-iframe.w-script > a' ) ) await joinWaitlistButton.click()
после этого начинается загрузка формы
прикол в том что нам нужно ждать. кто знает сколько ждать? в целом есть два пути — императивный и декларативный. можно сказать грубый и элегантный. но элегантный путь не всегда работает. в селенимуе можно ждать до тех пор пока элемент появится в зоне видимости (учитывая то что он уже есть в DOM):
await driver.wait(until.elementIsVisible(el))
код выше будет ждать пока элемент не появится в поле видимости "юзера"
императивный путь гласит так: мы ждем определенное количество секунд пока прогрузится форма. я так подсчитал что 15 секунд хватает для того чтобы форма прогрузилась (если конечно же у тебя не зафейлится прокси). так как заставить браузер ничего не делать 15 секунд?
await driver.sleep(15000) // ждет 15000 мс = 15 сек
казалось бы все просто? ща ищем инпут, и просто юзаем sendKeys
.. казалось бы
дело в том что так сделать не получится потому что прежде тебе нужно переключиться на нужный iframe
что такое iframe
?
iframe
это один из десятков тегов хтмл. но это не просто тег. iframe
позволяет встраивать страницы в страницы. то есть грубо говоря HTML файлы в HTML файлы. если ты посмотришь на iframe
в консоли то видно как у него внутри есть head
и body
прям как будто это отдельный хтмл документ
так вот для того чтобы нам работать с инпутом и элементами в целом внутри этой маленькой странички нам нужно сфокусировать селену на нем
как определить что элемент находится внутри iframe
?
листаем выше пока не находим такую замечательную конструкцию
как сфокусировать селену на iframe
?
нужно определить на каком iframe
ты хочешь сфокусить селену. можно указать либо индекс iframe
(то есть его порядковый номер в DOM, он начинается с 0) либо сам iframe
найденный через findElement
await driver.switchTo().frame(0) // в нашем случае индекс iframe = 0
и начиная отсюда ты можешь искать элементы и юзать их как хочешь
заполним мыло, нажмем кнопку ентер и подождем секунду:
const emailInput = await driver.findElement( By.css( '#block-dbda6309-71fd-49f4-83b0-a8486f031018 > div > div > div.Root-sc-__sc-1esu8nk-2.beLakw > div > div > div > div > div.SpacerWrapper-sc-__sc-4rs8xl-0.kOpjPh > div.InputWrapper-sc-__sc-26uh88-1.iLBPjJ > input' ) ) await emailInput.sendKeys('[email protected]', Key.RETURN) await driver.sleep(1000) // 1000 мс = 1 секунда
зачем ждать секунду?
если ты заметил то после каждого ввода происходит небольшой переход между шагами который длится около половины секунды. не забудь учитывать такие моменты прежде чем ты попытаешься найти очередной элемент
затем нажмем кнопочку о том что мы простолюдин, а не компания:
const individualButton = await driver.findElement( By.xpath( '//*[@id="block-3ab48cdb-19d6-4eb7-a953-2eb72c9864be"]/div/div/div[2]/div/div/div/fieldset/div[2]/div/div[1]/div/div/div[1]/div/div/div[2]/div' ) ) await individualButton.click() await driver.sleep(1000)
если ты заметил здесь я ищу элемент по XPath. это просто демонстрация того что ты можешь получать элемент по XPath (выбирая Copy XPath вместо Copy selector в меню элемента в консоли)
в целом дальше флоу абсолютно понятен и идентичен. в конце ты просто жмешь кнопку Submit и вуаля, Ахмед Рассулов заполнил форму
я прибрался в коде, полную имплементацию можно скачать здесь
не забудь запустить yarn install
до запуска кода чтоб подтянуть нужные зависимости
заключение
спасибо за то что прочитал мою статью мужичок/девчонка! схватил приподнял обнял до хруста отпустил 💛
я думаю у этой статьи, как и в случае статьи с соланой будет продолжение. в продолжении я научу тебя загонять кучу мыл, создавать логи по успешным абузам/зафейленным абузам, чтобы ты мог наблюдать и анализировать сколько акков загнал и скок не загнал и тд. в общем сделаю по красоте для тебя любимый читатель!
я буду еще писать о концептах селены более подробно и емко, так что подписывайся на рубури!
можешь не беспокоиться если чето не запомнилось, это нормально. не отступай и иди к цели. когда я учился я хотел бросить люто масштабно два раза... но не останавливался пон!!!!! я не остановился и ща работаю дворничком (шутка конечно мужики я во вкусно и точка)
благодарности
спасибо тебе за прочтение, спасибо тем кто ставит реакции, подпищекам, рыжему, сс ресерч, моей девочке, френдли тагу 52 нгг и всем остальным кто пон мотивирует ебашить люто жоска
и удачи тебе в пути кодинга еба!
отзыв
если в каком то примере ошибка, или что то не получается вы всегда можете обратиться за вопросом в телегу к рубурику @rubyuroboros
мой канал — https://t.me/ruburi