• Курсы Академии Кодебай, стартующие в мае - июне, от команды The Codeby

    1. Цифровая криминалистика и реагирование на инциденты
    2. ОС Linux (DFIR) Старт: 16 мая
    3. Анализ фишинговых атак Старт: 16 мая Устройства для тестирования на проникновение Старт: 16 мая

    Скидки до 10%

    Полный список ближайших курсов ...

Статья Скриншот полной страницы сайта с помощью Selenium и Python

Столкнулся я с небольшой задачей – сделать полный скриншот сайта. То есть, не только видимую область, а всю страницу целиком. Конечно, здесь могут помочь специализированные сервисы. Но, так как я реализовал данный функционал в своем скрипте, создание скриншотов с помощью сервисов автоматически отпадало. Впрочем, возможно у некоторых сервисов есть для этих целей специализированный API, однако, по моему опыту использование таких API, как правило, стоит денег. Поэтому я стал искать бесплатное и, что немаловажно, локальное решение. В данной статье хочу поделиться тем, что мне удалось найти.

000.jpg


Для начала рассмотрим создание скриншота с помощью сервиса от Google - PageSpeed Insights API. Как я и писал, данный сервис в своей бесплатной реализации имеет ряд ограничений. В частности, при бесплатном использовании 400 запросов в течение 100 секунд, 25000 запросов в день. И самое печальное ограничение ждет нас в части создания скриншотов. Здесь размер изображения ограничен 320px. Конечно, какое-то общее представление о содержимом страницы это дать может, вот только разглядеть детали уже не получиться. Тем не менее, давайте попробуем данный сервис в деле.


Что потребуется?

Для выполнения запросов к сервису потребуется установить библиотеку requests. Поэтому, пишем в терминале команду:

pip install requests

Следующим шагом будет получение ключа для работы с API сервиса. Для этого у вас должен быть аккаунт Google. Переходим по адресу: . На данной странице расположена кнопка «Получить ключ». Жмем ее и во всплывающем окне указываем имя приложения для работы с API. В принципе, можно выбрать название проекта по умолчанию, то, что предлагает нам сервис, после чего нажать на кнопку «Next».

screenshot1.png

После этого попадаем в окно, в котором расположена еще одна кнопка. Жмем ее и видим ключ для работы. Его нам нужно скопировать для последующей работы с API сервиса.

screenshot2.png

После того, как данная библиотека будет установлена, можно импортировать в скрипт все, что нужно для его работы. Поэтому, в блоке импорта пишем следующее:

Python:
import base64
from pathlib import Path
from urllib import parse
from urllib.parse import urlparse

import requests


Получаем скриншот сайта от сервиса

Создадим функцию get_screenshot(url), которая в качестве параметра получает ссылку на сайт, скриншот которого нужно получить. Следующим шагом кодируем не ASCII символы в полученной ссылке. То есть, для примера адрес вида: Форум информационной безопасности - Codeby.net будет выглядеть как https%3A%2F%2Fcodeby.net. Затем формируем имя скриншота, которое будет, в данном случае, состоять из имени сайта с расширением .png.

В переменной key указываем ключ, который мы получили от сервиса ранее. Далее, указываем режим, в котором будет выполняться запрос. Подробнее об этом можно почитать на странице документации: .

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

Выполняем запрос и получаем ответ в формате json. В ответ прилетит довольно длинная портянка, однако нас интересует вполне конкретная информация, а именно ключ «data», который содержит скриншот в кодировке base64. Удаляем лишние символы из содержимого и декодируем изображение. После чего сохраняем его в файл в байтовом режиме.

Python:
def get_screenshot(url):
    url_q = parse.quote_plus(url)
    image_path = str(urlparse(url).hostname).replace(Path(str(urlparse(url).hostname)).suffix, ".png")

    key = "AIzaSyBxH7Mf-4iqFrjs-5KL8P95hua5RDSOgapp"  # здесь ваш ключ
    strategy = "desktop"  # "mobile"
    u = f"https://www.googleapis.com/pagespeedonline/v5/runPagespeed?key={key}&strategy={strategy}&url={url_q}"

    try:
        j = requests.get(u).json()
        ss_encoded = j['lighthouseResult']['audits']['final-screenshot']['details']['data'].replace(
            "data:image/jpeg;base64,", "")
        ss_decoded = base64.b64decode(ss_encoded)
        with open(image_path, 'wb+') as f:
            f.write(ss_decoded)
    except Exception as ex:
        print(ex)
        exit(0)

Python:
"""
Get API Key (https://developers.google.com/speed/docs/insights/v5/get-started)
Docs (https://developers.google.com/speed/docs/insights/v5/reference/pagespeedapi/runpagespeed)

Limits:
Queries per day = 25,000
Queries per 100 seconds = 400
The maximum width is 320px
"""

import base64
from pathlib import Path
from urllib import parse
from urllib.parse import urlparse

import requests


def get_screenshot(url):
    url_q = parse.quote_plus(url)
    image_path = str(urlparse(url).hostname).replace(Path(str(urlparse(url).hostname)).suffix, ".png")

    key = "AIzaSyBxH7Mf-4iqFrjs-5KL8P95hua5RDSOgapp"  # здесь ваш ключ
    strategy = "desktop"  # "mobile"
    u = f"https://www.googleapis.com/pagespeedonline/v5/runPagespeed?key={key}&strategy={strategy}&url={url_q}"

    try:
        j = requests.get(u).json()
        ss_encoded = j['lighthouseResult']['audits']['final-screenshot']['details']['data'].replace(
            "data:image/jpeg;base64,", "")
        ss_decoded = base64.b64decode(ss_encoded)
        with open(image_path, 'wb+') as f:
            f.write(ss_decoded)
    except Exception as ex:
        print(ex)
        exit(0)


get_screenshot('https://codeby.net')

Как итог, вот, что у нас получилось:

codeby.net_google.jpg


Как мы видим, скриншот здесь далеко не полный, а только видимая часть, которая открывается при загрузке. Но, в данном случае нашей задачей было показать, как работает данный сервис.
Получается, что его бесплатный API весьма ограничен в использовании. Более того, использование API завязано на получение ключа, для чего нужно зарегистрировать аккаунт Google.
Что ж, двигаемся дальше и рассмотрим, наверное всем известный способ получения скриншота с помощью selenium.


Скриншот сайта с помощью Selenium

Так как в данном способе используется библиотека Selenium, ее нужно для начала установить. Поэтому пишем в терминале команду:

pip install selenium

Следующим шагом будет скачивание драйвера для браузера и сохранение его в папку для последующего использования в скрипте. Директория может быть, в принципе, любой. Главное, в скрипте указать полный путь к драйверу. Тем не менее, я создал в директории со скриптом папку driver, куда его и сохранил. Скачать драйвер для Chrome можно по следующему адресу: . В данном скрипте будет использоваться именно он.

Также вам потребуется установить браузер Google Chrome. После чего, посмотреть версию браузера и скачать драйвер именно для этой версии. Главное, посмотреть первый цифры версии, для примера 112. Последующие номера решающего значения не имеют.

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

Python:
import os
from pathlib import Path
from platform import system
from urllib.parse import urlparse

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service

Затем укажем расширенные опции, которые позволят запускать браузер в «безголовом» режиме, а также позволят скрыть от сайтов, что используется автоматизированное ПО.

Python:
options = Options()
options.add_argument("--headless")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
options.add_argument("--disable-blink-features=AutomationControlled")

Укажем путь к драйверу браузер, и создадим объект веб-драйвера, в который передадим путь к драйверу, указанные нами опции и определим сохранение логов в null.
Далее, чтобы продолжить маскировку браузера, необходимо выполнить скрипт, который удалит все функции, которые используются сайтами для детекта.

Python:
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
    'source': '''
        delete window.cdc_adoQpoasnfa76pfcZLmcfl_Array;
        delete window.cdc_adoQpoasnfa76pfcZLmcfl_Promise;
        delete window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol;
  '''
})

Создадим функцию screen_sait(url), в которую будем передавать ссылку на страницу сайта, скриншот которой необходимо сделать. Формируем имя скриншота. Переходим на страницу по переданной в функцию ссылке. И, собственно, сохраняем скриншот. После этого закрываем страницу и браузер.

Python:
def screen_sait(url) -> None:
    path = str(urlparse(url).hostname).replace(Path(str(urlparse(url).hostname)).suffix, ".png")
    driver.get(url)
    driver.save_screenshot(path)
    driver.close()
    driver.quit()

Теперь запустим созданный скрипт и посмотрим на результат. Иногда создание скриншота может занимать некоторое время. Связано это с тем, что браузер достаточно долго подгружает страницу. И, пока она не будет полностью загружена, скриншот сделан не будет. К сожалению, это один из минусов использования этого способа. Ниже показан скриншот, который у нас получился в итоге:

codeby_selenium.png

Как видим, в таком упрощенном варианте, так же, как и в варианте с сервисом, был сделан скриншот только видимой части страницы.

Python:
# pip install selenium

import os
from pathlib import Path
from platform import system
from urllib.parse import urlparse

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service

options = Options()
options.add_argument("--headless")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
options.add_argument("--disable-blink-features=AutomationControlled")

exec_path = os.path.join(os.getcwd(), 'driver', 'chromedriver.exe') if system() == "Windows" else \
    os.path.join(os.getcwd(), 'driver', 'chromedriver')

driver = webdriver.Chrome(options=options, service=Service(log_path=os.devnull, executable_path=exec_path))

driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
    'source': '''
        delete window.cdc_adoQpoasnfa76pfcZLmcfl_Array;
        delete window.cdc_adoQpoasnfa76pfcZLmcfl_Promise;
        delete window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol;
  '''
})


def screen_sait(url) -> None:
    path = str(urlparse(url).hostname).replace(Path(str(urlparse(url).hostname)).suffix, ".png")
    driver.get(url)
    driver.save_screenshot(path)
    driver.close()
    driver.quit()


screen_sait('https://codeby.net/')

Но, что если нам необходимо сделать скриншот полностью? Конечно, нижеприведенный способ не совсем оптимален, но, тем не менее, это все же лучше, чем прокручивать страницу по экранам, а после делать скриншот, не забывая при этом удалять заголовок. А после этого еще и склеивать то, что сделано в единое целое.

Итак, давайте приступим к написанию кода. Для того, чтобы сделать скриншот, слегка видоизменим вышеприведенный скрипт. То есть, для начала импортируем библиотеки. Указываем опции, путь к драйверу, создаем объект драйвера, выполняем скрипт для удаления функций.

Python:
import os
from pathlib import Path
from platform import system
from urllib.parse import urlparse

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By

options = Options()
options.add_argument("--headless")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
options.add_argument("--disable-blink-features=AutomationControlled")

exec_path = os.path.join(os.getcwd(), 'driver', 'chromedriver.exe') if system() == "Windows" else \
    os.path.join(os.getcwd(), 'driver', 'chromedriver')

driver = webdriver.Chrome(options=options, service=Service(log_path=os.devnull, executable_path=exec_path))

driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
    'source': '''
        delete window.cdc_adoQpoasnfa76pfcZLmcfl_Array;
        delete window.cdc_adoQpoasnfa76pfcZLmcfl_Promise;
        delete window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol;
  '''
})

После, создаем функцию screen_sait(url), которая, как и в предыдущем варианте получает ссылку на страницу сайта. Формируем название скриншота. Переходим на страницу, после чего выполняем java-скрипт, с помощью которого прокручиваем страницу до самого конца, для того, чтобы определить ширину и высоту тела страницы, которое заключено в тег «body». После чего передаем полученные значения в размера окна, элемент по тегу «body» и делаем скриншот. Закрываем страницу и браузер.

Python:
def screen_sait(url) -> None:
    path = str(urlparse(url).hostname).replace(Path(str(urlparse(url).hostname)).suffix, ".png")
    driver.get(url)
    s = lambda x: driver.execute_script('return document.body.parentNode.scroll' + x)
    driver.set_window_size(s('Width'), s('Height'))
    driver.find_element(By.TAG_NAME, 'body').screenshot(path)
    driver.close()
    driver.quit()

Как и в предыдущем случае, создание скриншота займет какое-то время. Но, результат уже будет другой. Ниже представлен скриншот, который получен с помощью данного скрипта.

codeby_selenium_full.png

Python:
# pip install selenium

import os
from pathlib import Path
from platform import system
from urllib.parse import urlparse

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By

options = Options()
options.add_argument("--headless")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
options.add_argument("--disable-blink-features=AutomationControlled")

exec_path = os.path.join(os.getcwd(), 'driver', 'chromedriver.exe') if system() == "Windows" else \
    os.path.join(os.getcwd(), 'driver', 'chromedriver')

driver = webdriver.Chrome(options=options, service=Service(log_path=os.devnull, executable_path=exec_path))

driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
    'source': '''
        delete window.cdc_adoQpoasnfa76pfcZLmcfl_Array;
        delete window.cdc_adoQpoasnfa76pfcZLmcfl_Promise;
        delete window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol;
  '''
})


def screen_sait(url) -> None:
    path = str(urlparse(url).hostname).replace(Path(str(urlparse(url).hostname)).suffix, ".png")
    driver.get(url)
    s = lambda x: driver.execute_script('return document.body.parentNode.scroll' + x)
    driver.set_window_size(s('Width'), s('Height'))
    driver.find_element(By.TAG_NAME, 'body').screenshot(path)
    driver.close()
    driver.quit()


screen_sait('https://codeby.net/')

Конечно, это далеко не универсальный способ. Но, так или иначе, множество библиотек для получения скриншота используют именно Selenium. В той или иной его разновидности. Поэтому, вышеприведенный способ имеет право на существование.

А на этом, пожалуй, все.

Спасибо за внимание. Надеюсь, данная информация будет вам полезна
 

Вложения

  • screenshot.zip
    2,4 КБ · Просмотры: 104
Последнее редактирование модератором:

Brendan Castaneda

One Level
18.02.2020
5
2
BIT
12
Может я чего-то не понимаю, но для того, что-бы сделать "полноразмерный скриншот" в Google Chrome можно сделать следующее:
Нажимаем F12 > Жмём на три вертикальные точки > Выполнить команду > "в строке поиска вбиваем: скрин" находим строку: "Сделать полноразмерный скриншот" нажимаем на неё и всё.
Или так:
Нажимаем F12 > Ctrl+Shift+P > "в строке поиска вбиваем: скрин" находим строку: "Сделать полноразмерный скриншот" нажимаем на неё и всё.
Profit 💁🏼‍♂️
 

Brendan Castaneda

One Level
18.02.2020
5
2
BIT
12
А для удобства запуска вашего скрипта "full_screenshot.py" я дополнительно создал бы .bat файл положив его в тот-же каталог.
Пример:
Код:
@echo off
color a
set /p url=Paste your link:
python full_screenshot.py %url%

В файле скрипта нужно дописать импорт модуля sys и изменить строку screen_sait:
Код:
import sys
screen_sait(sys.argv[1])

Теперь при запуске бат файла можно копипостить нужный линк и жать enter, без необходимости каждый раз изменять скрипт и открывать его для запуска.
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!