5

I am using the Bokeh package to generate maps to show the results of a simulation. The output is individual maps in html format with interactivity. The interactivity is needed for individual maps.

See this link for an example:

http://docs.bokeh.org/en/0.10.0/docs/gallery/texas.html

The simulation can automatically be set to run a number of times and will produce a map for each run. This could be 100's of maps. I would like to be able to stitch together the maps to create a movie - the interactivity is not required for this. Bokeh has functionality to create PNG files via the browser so it is possible to manually save each map as a file and use ffmpeg to create a movie. However this is not really an option if you need to do it for 100's of files. Currently there is no way to automatically generate PNG files via Bokeh but I believe it will be added at some point.

So I need a workaround. My thought is to open each html file from the location they are stored on the local drive, take a screen shot, crop the image to keep the required section and save. But I have not yet found a solution that works.

Cropping an image is easy:

from PIL import Image

img = Image.open(file_name)
box = (1, 1, 1000, 1000)
area = img.crop(box)
area.save('saved_image', 'jpeg')

My problem is opening the html file and taking the screen shot in the first place to feed to the above code.

For this I have tried the following but both require a URL rather than an html file. Also both use Firefox which doesn't work for me but I have installed chrome and altered the code appropriately.

How to take partial screenshot with Selenium WebDriver in python?

http://www.idiotinside.com/2015/10/20/take-screenshot-selenium-python/

My code for this is:

from selenium import webdriver

driver = webdriver.Chrome()
driver.get('file_name')
driver.save_screenshot('image.png')
driver.quit()

Which returns:

{"code":-32603,"message":"Cannot navigate to invalid URL"}

Clearly a filename is not a url so that is clear. It works fine if you pass it a website. Any help in getting a html loaded and a picture taken would be greatly appreciated! It does not have to involve Selenium.

bigreddot
  • 33,642
  • 5
  • 69
  • 122
Pete
  • 767
  • 3
  • 9
  • 16
  • What operating system are you running? – zglin Jul 25 '16 at 13:17
  • Ubuntu 14.04 and Windows. – Pete Jul 25 '16 at 13:20
  • I would hope a bokeh person has a method for you; but I would recommend using qt's qwebview to render your html, and grab the window image http://stackoverflow.com/questions/24413385/pyqt4-qwidget-save-as-image – mdurant Jul 25 '16 at 13:21
  • 2
    Did you try setting the path to use file URI scheme? Check this answer http://stackoverflow.com/a/17973306/636363 – hgazibara Jul 25 '16 at 13:21
  • @hgazibara. Your idea worked. Coercing the filename to the right format enabled the file to be loaded and all worked as intended once done. – Pete Jul 26 '16 at 08:47

4 Answers4

7

As of Bokeh 0.12.6, it is now easier to take those screenshots directly from Python without needing to open a browser.

Exporting PNGs looks like this

export_png(plot, filename="plot.png")

And exporting SVGs looks like this

plot.output_backend = "svg"
export_svgs(plot, filename="plot.svg")

There are some optional dependencies that need to be installed. You can find more information in the Exporting Plots section of the User Guide.

bigreddot
  • 33,642
  • 5
  • 69
  • 122
5

hgazibara comment proved to be the simplest fix. Some simplified code is below to provide the answer. It would be nice if the web page didn't actually have to show itself for the screen shot to be taken. I will see if I can add this later.

import glob
from PIL import Image
from selenium import webdriver 

# get a list of all the files to open
glob_folder = os.path.join(file_location, '*.html')

html_file_list = glob.glob(glob_folder)
index = 1

for html_file in html_file_list:

    # get the name into the right format
    temp_name = "file://" + html_file

    # open in webpage
    driver = webdriver.Chrome()
    # The above line could be substituted for these 3 lines, 
    # which would prevent the webpage from opening first
    ###########
    options = webdriver.ChromeOptions()
    options.add_argument('headless')
    driver = webdriver.Chrome(executable_path="C:\\ChromeDriver\\chromedriver.exe", chrome_options=options)
    # Your path to your chromedriver could be different than mine
    ###########

    driver.get(temp_name)
    save_name = '00' + str(index) + '.png'       
    driver.save_screenshot(save_path, save_name))
    driver.quit()
    index += 1

    # crop as required
    img = Image.open(save_path, save_name))
    box = (1, 1, 1000, 1000)
    area = img.crop(box)
    area.save('cropped_image' + str(index), 'png')
NL23codes
  • 1,181
  • 1
  • 14
  • 31
Pete
  • 767
  • 3
  • 9
  • 16
2

You can use SimpleHTTPServer to create a basic webserver and expose your local file.

You should run this in the path where the html is python -m SimpleHTTPServer 8000

Then just change driver.get('file_name') to driver.get('localhost:8000/file_name.html')

I recommend you to use "wait until" to be sure everything is loaded before take the screenshot:

driver.wait.until(EC.visibility_of_element_located((By.XPATH,'//*[@id="someID"]')))

rcedillo
  • 336
  • 1
  • 4
0

If you're running Linux, you may have to do the following steps.

First open the html file via command line in the default app. You'll probably have to do something like

import os

os.system('command to open html file')

Use the solution listed to take your screenshot.

Then do the processing in PIL as suggested.

If you're doing this in windows,

You may want to setup the AutoIT drivers so that you can use your python script to manipulate GUI windows etc.

Community
  • 1
  • 1
zglin
  • 2,891
  • 2
  • 15
  • 26