1

Asking this again as the previous has been closed without any useful solution.

I am trying to produce few charts in a loop and save them automatically.

I am working on Windows, using Chrome.

I have been following the documentation on how to save a chart in svg.

I downloaded the appropriate ChromeDriver for my Chrome version. gdf is my geodataframe and everything works smoothly and I can generate and manually save charts.

Also, this example taken from the chromium documentation works:

import time
from selenium import webdriver

driver = webdriver.Chrome('pathtochromedriver/chromedriver_win32/chromedriver.exe')
driver.get('http://www.google.com/');
time.sleep(5) # Let the user actually see something!
search_box = driver.find_element_by_name('q')
search_box.send_keys('ChromeDriver')
search_box.submit()
time.sleep(5) # Let the user actually see something!
driver.quit()

Below is the piece of code I am trying to run:

#Chromedriver for headless run and chart automatic save
from selenium import webdriver
from selenium.webdriver.chrome.options import Options 

chrome_options = Options()  
chrome_options.add_argument("--headless") 
####### None of the following work! 
#driver = webdriver.Chrome(chrome_options=chrome_options, executable_path=r'pathtochromedriver/chromedriver_win32/chromedriver.exe')
#driver = webdriver.Chrome(executable_path=r'pathtochromedriver/chromedriver_win32/chromedriver.exe')
driver = webdriver.Chrome('pathtochromedriver/chromedriver_win32/chromedriver.exe')
#
#loop
for group in gdf['Group Name'].unique():
    map_1km_json = json.loads(gdf[gdf['Group Name']==group].to_json())
    map_1km_data = alt.Data(values=map_1km_json['features'])
    data_1km_geojson = alt.InlineData(values=map_1km, format=alt.DataFormat(property='features',type='json'))
    domain=[1,2]
    range_=['#b0d247','#007bd1']
    choro1_5 = alt.Chart(map_1km_data).mark_geoshape().encode(
        alt.Color('properties.Cat2:O', scale=alt.Scale(domain=domain, range=range_),title = "Havvandsstigning"),
        #alt.Color('properties.Cat2', type='quantitative', scale=alt.Scale(scheme='plasma'),title = "Havvandsstigning")
        tooltip = ['properties.dd_km1:N','properties.Cat2:N']
    ).properties(
    width=1100,
    height=800
    )
    (background + choro1_5).save("{}.svg".format(bank))
driver.quit()

Running the code opens an empty new chrome pane (in the background) that looks like this, which hints to the fact that the chromedriver is working smoothly: chromepane

But the code returns the following error:

---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
~\Anaconda3\lib\site-packages\selenium\webdriver\common\service.py in start(self)
     75                                             stderr=self.log_file,
---> 76                                             stdin=PIPE)
     77         except TypeError:

~\Anaconda3\lib\subprocess.py in __init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, encoding, errors, text)
    774                                 errread, errwrite,
--> 775                                 restore_signals, start_new_session)
    776         except:

~\Anaconda3\lib\subprocess.py in _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, unused_restore_signals, unused_start_new_session)
   1177                                          os.fspath(cwd) if cwd is not None else None,
-> 1178                                          startupinfo)
   1179             finally:

FileNotFoundError: [WinError 2] The system cannot find the file specified

During handling of the above exception, another exception occurred:

WebDriverException                        Traceback (most recent call last)
<ipython-input-27-6067387e4b2f> in <module>
     19     height=800
     20     )
---> 21     (background + choro1_5).save("{}.svg".format(bank))
     22 driver.quit()

~\Anaconda3\lib\site-packages\altair\vegalite\v3\api.py in save(self, fp, format, override_data_transformer, scale_factor, vegalite_version, vega_version, vegaembed_version, **kwargs)
    478         if override_data_transformer:
    479             with data_transformers.disable_max_rows():
--> 480                 result = save(**kwds)
    481         else:
    482             result = save(**kwds)

~\Anaconda3\lib\site-packages\altair\utils\save.py in save(chart, fp, vega_version, vegaembed_version, format, mode, vegalite_version, embed_options, json_kwds, webdriver, scale_factor, **kwargs)
    102                                         vegaembed_version=vegaembed_version,
    103                                         webdriver=webdriver,
--> 104                                         scale_factor=scale_factor, **kwargs)
    105         if format == 'png':
    106             write_file_or_filename(fp, mimebundle['image/png'], mode='wb')

~\Anaconda3\lib\site-packages\altair\utils\mimebundle.py in spec_to_mimebundle(spec, format, mode, vega_version, vegaembed_version, vegalite_version, **kwargs)
     54                               vega_version=vega_version,
     55                               vegaembed_version=vegaembed_version,
---> 56                               vegalite_version=vegalite_version, **kwargs)
     57         if format == 'png':
     58             render = base64.b64decode(render.split(',', 1)[1].encode())

~\Anaconda3\lib\site-packages\altair\utils\headless.py in compile_spec(spec, format, mode, vega_version, vegaembed_version, vegalite_version, scale_factor, driver_timeout, webdriver)
    152             webdriver_options.add_argument('--no-sandbox')
    153 
--> 154     driver = webdriver_class(options=webdriver_options)
    155 
    156     try:

~\Anaconda3\lib\site-packages\selenium\webdriver\chrome\webdriver.py in __init__(self, executable_path, port, options, service_args, desired_capabilities, service_log_path, chrome_options, keep_alive)
     71             service_args=service_args,
     72             log_path=service_log_path)
---> 73         self.service.start()
     74 
     75         try:

~\Anaconda3\lib\site-packages\selenium\webdriver\common\service.py in start(self)
     81                 raise WebDriverException(
     82                     "'%s' executable needs to be in PATH. %s" % (
---> 83                         os.path.basename(self.path), self.start_error_message)
     84                 )
     85             elif err.errno == errno.EACCES:

WebDriverException: Message: 'chromedriver' executable needs to be in PATH. Please see https://sites.google.com/a/chromium.org/chromedriver/home
CAPSLOCK
  • 6,243
  • 3
  • 33
  • 56
  • 1
    seems like a real odd way to produce an svg, but I don't think you have to initialize the driver at all. Altair's code will do that... and it seems like it's trying to do that but chromedriver is not in your system's PATH variable. (Why they are using Selenium here is beyond me... if I were you I'd just output to HTML so you don't need to rely on Selenium at all.) – pcalkins Jan 24 '20 at 23:43
  • 1
    btw, you won't see the browser... I think they're using a headless browser for this and then taking a screenshot using Selenium. If I'm right, there's no point in outputting a svg that is just a screenshot of a bitmap (they use canvas for the chart) – pcalkins Jan 25 '20 at 00:07

3 Answers3

5

The reason your code does not work is because you are not passing your driver instance to Altair, and so Altair is creating a default driver instance and looking for chromedriver in the default location.

Altair currently does not support passing a custom driver instance to the save() method, so saving a figure in the way you are attempting above is not possible, unless chromedriver can be made available at the default location.

We are in the process of improving this, however, with the altair_saver package. With it, you can pass a custom driver instance to the save method:

from altair_saver import save

# ...

chart = background + choro1_5
filename = f"{bank}.svg"
save(chart, filename, method='selenium', webdriver=driver)

In a future Altair release, this more flexible saver extension will replace Altair's current built-in chart.save() method.

jakevdp
  • 77,104
  • 11
  • 125
  • 160
  • using the save method from altair_saver with custom arguments (method='selenium', webdriver=driver) helped in my case. thanks @jakevdp – sudheer Mar 18 '22 at 05:23
2

Have a look at Altair Saver project. It uses Chrome Driver and Selenium. Install via pip or conda. It can save to svg:

from altair_saver import save

save((background + choro1_5), "{}.svg".format(bank))
cast42
  • 1,999
  • 1
  • 16
  • 10
0

@jakevdp solution works perfectly. Here are the complete steps on how to enable .png and .svg using selenium and chromedriver on Windows Machine

Install altair_saver

pip install altair_saver

Check the chrome version installed in your windows system (Chrome browser > Help > Version). For example if the version is 99.0.4844.xx, install the matching chromedriver as follows

pip install chromedriver-binary=="99.0.4844.74"

Download the chromedriver from chromium.org and add it to Environment variable PATH or lets say the location of driver is PATH_TO_CHROME_DRIVER_EXE

Your Python code should be as follows to save as png

from altair_saver import save
import selenium.webdriver
driver = selenium.webdriver.Chrome('PATH_TO_CHROME_DRIVER_EXE')
save(chart, "chart.png", method='selenium', webdriver=driver) 

Note: altair_saver documents some of these details in installation section at top level for various operating systems for your reference.

sudheer
  • 94
  • 9