January 31, 2023

SELENIUM QUICK START

опачки а вот и селена

привет другалек! наконец у меня дошли руки написать статью по селениуму. как большинство просило - показать использование селены на реальном примере! бери чипсики чаек плюшки печеньки там шо кушаешь и лфг читать!

приступим!


оглавление

  1. что такое селениум и для чего его юзать
  2. подготавливаем среду обитания нашего скрипта
  3. базовые базы селениума
  4. маскируем нашу селеночку
  5. абузим хуйню?
  6. заключение
  7. благодарности + отзыв

что такое селениум и для чего его юзать

если обьяснять коротко - селениум это инструмент для того чтобы ты мог абузить какую то хуйню или перефразируя автоматизировать дефолт рутинные действия в браузере на кучу акков в любых количествах

с помощью селениума можно абузить формы, преминты, в общем бля все что можно делать в браузере дефолт юзеру можно делать с селениумом (ну или почти все)


подготавливаем среду обитания нашего скрипта

ты уже должен быть знаком с тем что такое редактор, жиэс и так далее. если ты не знаком прошу любить и жаловать — сюда

убедись что у тебя установлен nodejs и npm (с nodejs автоматически поставляется и npm)

и если у тебя все еще нет пакет менеджера yarn то установи его

как установить yarn

в терминале вводим:

npm i -g yarn

открывай любимый редактор, открывай терминал и директорию с проектом, затем вводи в терминал:

yarn init -y
yarn add selenium-webdriver webdriver-manager

и создаем файлик index.js

и вписываем туда базовую базу:

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')

запустив скрипт ты увидишь что поиск пошел!

с кайфом садоводы

ну и закончим раздел базовых баз тем что мы кликнем на первую попавшуюся ссылку. тут мы будем задействовать клик следственно алгоритм такой:

  1. ищем элемент
  2. кликаем на элемент

лфг!

нам нужна первая ссылка, соответственно нажимаем правой кнопкой по первой ссылке и идем искать нужный элемент

опа элемент

в дереве дом мы видим элементом выше тег 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 для нашего кастомного расширения

background.js

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']
)

manifest.json

{
  "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

перемещаем zip в директорию с нашим ботом и лфг дальше

маскируем 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 > #document с html, head и body

как сфокусировать селену на 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