Capturing a full-page screenshot using Selenium's Marionette with Python

Following the recent update of Firefox to version 47, we found it necessary to add the Marionette extension in order to continue using Selenium Webdriver. In my case, I also had to upgrade from Selenium 2.52 to 2.53.

I rely on the Python version of Selenium Webdriver to capture high-resolution images of maps created with HTML and JavaScript. Previously, this process worked seamlessly in Firefox, allowing me to take screenshots of entire pages that were much larger than the size of my screen. However, with the recent changes, screenshots are now limited to the visible area on the screen only. The code snippet I use is as follows:

import time
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

caps = DesiredCapabilities.FIREFOX
caps["marionette"] = True

browser = webdriver.Firefox(capabilities=caps)
browser.get(html_file)
time.sleep(15)

browser.save_screenshot(image_name)
browser.quit()

I have explored various options such as downgrading, stitching together multiple screenshots, or transitioning to Qgis. Nevertheless, I am searching for a more refined solution that allows me to continue utilizing the latest Firefox version and a similar workflow. Does anyone have insight into a potential solution? Perhaps by manipulating Selenium to perceive a larger viewport or by adopting another Linux-compatible browser that supports full-page screenshot capabilities?

Answer №1

Here is the solution I use for capturing a full page screenshot:

#!/usr/bin/python
from selenium import webdriver
from PIL import Image
from cStringIO import StringIO

verbose = 1

browser = webdriver.Firefox()
browser.get('http://stackoverflow.com/questions/37906704/taking-a-whole-page-screenshot-with-selenium-marionette-in-python')

# JavaScript code to get the height of the entire document
js = 'return Math.max( document.body.scrollHeight, document.body.offsetHeight,  document.documentElement.clientHeight,  document.documentElement.scrollHeight,  document.documentElement.offsetHeight);'

scrollheight = browser.execute_script(js)

if verbose > 0: 
    print scrollheight

slices = []
offset = 0
while offset < scrollheight:
    if verbose > 0: 
        print offset

    browser.execute_script("window.scrollTo(0, %s);" % offset)
    img = Image.open(StringIO(browser.get_screenshot_as_png()))
    offset += img.size[1]
    slices.append(img)

    if verbose > 0:
        browser.get_screenshot_as_file('%s/screen_%s.png' % ('/tmp', offset))
        print scrollheight


screenshot = Image.new('RGB', (slices[0].size[0], scrollheight))
offset = 0
for img in slices:
    screenshot.paste(img, (0, offset))
    offset += img.size[1]

screenshot.save('/tmp/test.png')

You can find the code snippet here on GitHub.

An issue with scrolling and stitching is that HTML nodes set to "display: fixed" will appear repeatedly in each screenshot.

Answer №2

My experience with this approach has been quite positive. Although it operates in headless mode, the results achieved are similar to those obtained in normal mode.

from selenium import webdriver

firefox_options = webdriver.FirefoxOptions()
firefox_options.set_headless() 

firefox_driver = webdriver.Firefox(executable_path=<path_to_gecko_driver>, firefox_options=firefox_options)
firefox_driver.get(<some_url>)

firefox_elem = firefox_driver.find_element_by_tag_name('html')
firefox_elem.screenshot(<png_screenshot_file_path>)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using a custom filename for image downloads with Scrapy

In my current project with scrapy, I am using the ImagesPipeline to handle downloaded images. The Images are stored in the file system using a SHA1 hash of their URL as the file names. Is there a way to customize the file names for storage? If I want to ...

Locating a table in Java using Selenium without an ID

I am facing an issue with locating a table element in my code. The HTML structure looks like this: <table class="table"> <thead> <tr> <th>Role</th> <th>Description</th> <th> ...

Divide the strings using punctuation marks, but leave the tags intact

How can I split a string by removing all punctuation marks and spaces, except for the # character? tweet = "I visited #India to experience the culture. It was amazing!" I want to separate the words in the string above like this: ["I", "visited", "#India ...

Ways to determine if an item is present in my list

Is there a way to determine the number of instances of an object in one list compared to another? Let's start with the Card class. class Card(object): def __init__(self, number): self.number = number def __eq__(self, other): ...

Using pysnmp to fetch SNMP data: a step-by-step guide

My goal is to retrieve SNMP data using the Python pysnmp module. Previously, I would use the command line to fetch SNMP data, but now I am interested in utilizing the pysnmp module for this purpose. In the past, I used the following SNMP command: snmpwal ...

What is the process for invoking a function in a Python script right before it is terminated using the kill command?

I created a python script named flashscore.py. During execution, I find the need to terminate the script abruptly. To do this, I use the command line tool kill. # Locate process ID for the script $ pgrep -f flashscore.py 55033 $ kill 55033 The script ...

Discovering xpath in Chrome Version 58.0.3029.81 (64-bit) can be done effortlessly with the help of xpathfinder. Unfortunately, the shortcut shift+ctrl+x

Having trouble locating the xpath in Google Chrome Version 58.0.3029.81 (64-bit) using xpathfinder. The shortcut shift+ctrl+x is not working for this. This xpath would be used with selenium 3.3.1 for automation testing. Your assistance is greatly appreci ...

The alignment of margins appears as intended in Opera, Chrome, and IE, but unfortunately it does not

I have exhausted all possible options, but I am still struggling to get this page to appear correctly in Firefox. When you click inside the square, a menu with images should display on the right side. It is properly aligned in every browser except for Fire ...

"Flawed spacing in python-mode of emacs causing incorrect indentation

When dealing with objects that have a property named Class, there seems to be an issue in emacs where the indentation of the else: line in if statements is misaligned. The expected indentation versus what emacs generates can be seen below. def func(): ...

How to programmatically upload files to various S3 buckets using Django Storages and Boto3

I am currently utilizing django-storages to upload files into an s3 bucket. However, I am interested in being able to upload files into a different s3 bucket than the one specified in my default settings.py file. Each of my app's web pages corresponds ...

Exploring the method to iterate with Selenium RC directly on XPath search outcomes

As of now, I find myself resorting to the method of first performing a get_xpath_count, and then setting up a loop that increases an index variable. This variable is injected back into the original xpath to select the nth result... all in all, a very cumbe ...

Suggestions for resolving the error message "Meta.fields cannot be a string. Did you intend to type: 'name'?"

After creating a script to allow users to add new Hotels using a form, I encountered the following error exception. The error message states: AddHotelForm.Meta.fields cannot be a string. Did you mean to type: ('name',)? Details of the request i ...

It appears that there is a security concern with your current connection while utilizing Selenium.WebDriver version 3.6.0 with Firefox version 56

While working on my tests using Selenium with C#, I encountered a major issue when testing my website with a secure connection (HTTPS). Unfortunately, all the solutions I found on stackoverflow were either outdated or ineffective. I attempted to implement ...

Utilizing pytest and tox for managing environment variables

Is there a way to efficiently test environment variables using pytest and tox? verify.py ENV_VARIABLE = os.environ['ENV_VARIABLE'] def check_env_variable(val): if val != ENV_VARIABLE: raise Exception test_verify.py class TestCheckEn ...

A guide on mimicking URL input and pressing ENTER in Chrome using Selenium and NodeJS

My current project involves using Selenium and NodeJS to automate download tests specifically on Chrome. Interestingly, I have observed that the download protection feature in Chrome behaves differently depending on whether a link is clicked or if the URL ...

Error related to environment / authentication - BigQuery Admin: {invalid_grant, Invalid JWT Signature}

Recently, I embarked on my first journey to utilize the BigQuery API by following a Python tutorial guide available here. My primary objective is to create datasets, however, I'm encountering challenges with basic public data access. To address this, ...

What is the rationale behind assigning names to variables in Tensorflow?

During my observation in various locations, I noticed a pattern in variable initialization where some were assigned names while others were not. For instance: # Named var = tf.Variable(0, name="counter") # Unnamed one = tf.constant(1) What is the signif ...

Having trouble accessing a website using Selenium-wire after compiling with Pyinstaller

I am trying to access a website using proxies with authentication and a specific user-agent. Here is the code snippet I am using: def start_driver(proxy_data, user_agent): proxy = ( proxy_data.get('login') + ':' + proxy_ ...

Django redirects to an alternative template instead of the default one

After renaming my login.html file to login1.html instead of deleting it, I have been using Django-registration and Django-registration-views from Github. However, despite this change, Django continues to call registration/login1.html. Is there a way for me ...

Truth Values and their Roles in Functions

I'm struggling with creating a function in Python that takes three boolean values and returns true if at least two are true. The function seems to work fine when I call it explicitly in the interpreter after running the program: >>> function ...