[PYTHON] Patch for taking full size screenshots in Chrome

This is the article on the 5th day of Selenium / Appium Advent Calendar 2014. The day before was hiroshitoda-prospire's "Environmental configuration management for running Selenium".

Hello. My name is myhr and I am a test engineer. Today I would like to introduce a patch for taking full size screenshots in Chrome. I think that it has already been introduced in other articles, but this time I would like to explain in detail for beginners.

background

For the background, see last year's article by hiroshitoda-prospire "Tips for taking screenshots with Selenium WebDriver I was allowed to refer to.

I also took screenshots in Firefox and Chrome.

--For vertically long pages

スクリーンショット 2014-12-04 16.14.35.png

--For horizontally long pages

スクリーンショット 2014-12-04 16.16.14.png

Firefox is able to capture the end of the page, but Chrome only shows what you can see on the browser screen. Therefore, unlike Firefox, you can see that every time you take a screenshot, you bother to bring the browser you are operating to the front.

RemoteWebDriver Modes The remote webdriver comes in two flavours:

  • Client mode: where the language bindings connect to the remote instance. This is the way that the FirefoxDriver, OperaDriver and the RemoteWebDriver client normally work.
  • Server mode: where the language bindings are responsible for setting up the server, which the driver running in the browser can connect to. The ChromeDriver works in this way.

I think that the reason why the behavior is different only for Chrome Driver is probably related to this area, but I would appreciate it if you could tell me more about it.

Operating environment

env version
Python 3.4.2
Pillow ※ 2.4.0
selenium 2.44.0
ChromeDriver 2.9.248307
Chrome 39.0.2171.71 (64-bit)

manner

The method is simple. I will do it in a forcible and muddy way.

Variable setting

var description var description
view_width ※ Horizontal size of browser stitched_image Image that becomes a screenshot
view_height ※ Vertical size of the browser tmp_image Image that will be part of the screenshot
total_width ※ Horizontal size of page row_count Number of times to scroll vertically
total_height ※ Vertical size of page col_count Number of scrolls horizontally
scroll_width Scrolled horizontal size meter new_width The remaining horizontal size of the page
scroll_height Scrolled vertical size total new_height The remaining vertical size of the page

※ Fixed value

  1. Assume that the size of the entire page is large both vertically and horizontally as shown below.
    Screenshot 2014-12-05 6.01.47.png
    At this time, initialize to scroll_width, scroll_height, row_count, col_count = 0.

  2. Take a screenshot and scroll by the width of the screen until it hits the right edge of the page.
    Screenshot 2014-12-05 15.48.55.png
    Increase col_count each time you scroll horizontally.
    Although it is not required, if the file name of tmp_image is tmp_ {row_count} _ {col_count} .png, it will be easier to understand when you look at the file later.
    Paste the tmp_image into the stitched_image at the scroll_width and scroll_height positions.

  3. When the scroll size exceeds the remaining size of the page, cut out the remaining amount, save it in tmp_image, and paste it in stitched_image.
    Screenshot 2014-12-05 16.08.23.png
    Then scroll vertically by the height of the screen, and scroll back to the left.
    scroll_height + = view_height; row_count ++; scroll_width, col_count = 0

  4. Repeat the above process until you reach the bottom right of the page. (Cut and paste in the same way as horizontal scrolling when vertical scrolling MAX)
    Finally, save stitched_image to the specified filename (passed as an argument) and return True.

code

https://gist.github.com/yui3880/a63ced61806d8517c4a3

from PIL import Image
import time

def save_screenshot(self, filename, fullsize=False):
	filepath = '/'.join(filename.split('/')[:-1])
	if fullsize:
		#Scroll to the top left of the page
		self.execute_script("window.scrollTo(0, 0);")

		#Get page size
		total_height = self.execute_script("return document.body.scrollHeight")
		total_width = self.execute_script("return document.body.scrollWidth")

		#Get screen size
		view_width = self.execute_script("return window.innerWidth")
		view_height = self.execute_script("return window.innerHeight")
		
		#For image processing
		stitched_image = Image.new("RGB", (total_width, total_height))

		#For scroll operation
		scroll_width = 0
		scroll_height = 0

		row_count = 0
		#Vertical scrolling §
		while scroll_height < total_height:
			#Side-scroll initialization
			col_count = 0
			scroll_width = 0
			self.execute_script("window.scrollTo(%d, %d)" % (scroll_width, scroll_height)) 
			#Side-scrolling process
			while scroll_width < total_width:
				if col_count > 0:
					#Side-scrolling for screen size
					self.execute_script("window.scrollBy("+str(view_width)+",0)") 
					
				tmpname = filepath + '/tmp_%d_%d.png' % (row_count, col_count)
				self.get_screenshot_as_file(tmpname)
				time.sleep(3)

				#When you reach the right or bottom edge, cut the image and stitched_Paste to image
				if scroll_width + view_width >= total_width or scroll_height + view_height >= total_height:
					new_width = view_width
					new_height= view_height
					if scroll_width + view_width >= total_width:
						new_width = total_width - scroll_width
					if scroll_height + view_height >= total_height:
						new_height = total_height - scroll_height
					tmp_image = Image.open(tmpname)
					tmp_image.crop((view_width - new_width, view_height - new_height, view_width, view_height)).save(tmpname)
					stitched_image.paste(Image.open(tmpname), (scroll_width, scroll_height))
					scroll_width += new_width

				#Paste normally
				else:
					stitched_image.paste(Image.open(tmpname), (scroll_width, scroll_height))
					scroll_width += view_width
					col_count += 1

			scroll_height += view_height
			time.sleep(3)

		
		#Stitched to the specified filename_image storage
		stitched_image.save(filename)
		return True

	# fullsize=If False, take a normal screenshot
	else:
		self.get_screenshot_as_file(filename)

Where do you hit

If you're not using a wrapper

Let's hit the language binding directly.

--For python: {$ PYTHONPATH} /site-packages/webdriver/chrome/webdriver.py

  ...
  from selenium.common.exceptions import WebDriverException
  from .service import Service
  from .options import Options
+ from PIL import Image
+ import time

  class WebDriver(RemoteWebDriver):
  ...
+     def save_screenshot(self, filename, fullsize=False):
+         #Get page size
+         total_width = self.execute_script("return document.body.scrollWidth")
+         total_height = self.execute_script("return document.body.scrollHeight")
  ...

――Advantage: If you put it in, all Chrome screenshots will be taken in this way, and the calling method is the same as before --Disadvantage: It is necessary to apply a patch every time the version of selenium is changed. It may not work depending on the environment.

If you are using a wrapper or an in-house library

Change self to webdriver or something in the method in your screenshot.

Button touch

Next is nkns165. Thank you.

Recommended Posts

Patch for taking full size screenshots in Chrome
Screenshots of Megalodon in selenium and Chrome.
Selenium-Screenshot is useful for screenshots of web pages in Python3, Selenium and Google Chrome