11

Is there any way to invoke chromedriver's Page.printToPDF() method from python + selenium?

PhantomJS has a similar render() method that can save straight to pdf, which is only available from the privileged client-side REPL of phantomjs. This SO answer shows how to patch a running selenium driver to invoke it, using a custom phantomjs webdriver command (/session/$sessionId/phantom/execute) to call this.render().

Is there something similar that can be done for chromedriver? Either something like phantomjs's execute command that allows calling into the devtools methods; or a way to invoke printToPDF directly via a custom driver command?

(Note: I'm trying to render html that's the result of a POST, so alternate solutions like wkhtmltopdf won't work. I can fall back to using selenium's screenshot -> png, but that's burdensome for storage purposes).

Eli Collins
  • 8,375
  • 2
  • 34
  • 38

2 Answers2

11

It's possible by calling Page.printToPDF from the DevTool API. However this command is experimental and not implemented on all the platforms:

from selenium import webdriver
import json, base64

def send_devtools(driver, cmd, params={}):
  resource = "/session/%s/chromium/send_command_and_get_result" % driver.session_id
  url = driver.command_executor._url + resource
  body = json.dumps({'cmd': cmd, 'params': params})
  response = driver.command_executor._request('POST', url, body)
  if response['status']:
    raise Exception(response.get('value'))
  return response.get('value')

def save_as_pdf(driver, path, options={}):    
  # https://timvdlippe.github.io/devtools-protocol/tot/Page#method-printToPDF
  result = send_devtools(driver, "Page.printToPDF", options)
  with open(path, 'wb') as file:
    file.write(base64.b64decode(result['data']))


options = webdriver.ChromeOptions()
options.add_argument("--headless")
options.add_argument("--disable-gpu")

driver = webdriver.Chrome(chrome_options=options)
driver.get("https://www.google.co.uk/")

save_as_pdf(driver, r'page.pdf', { 'landscape': False })
Florent B.
  • 41,537
  • 7
  • 86
  • 101
  • Thanks, exactly what I was looking for! Though I'm getting a `PrintToPDF is not implemented` error back. Is there something I have to do to enable experimental features? (Selenium reports it's connecting to Chrome 62.0.3202.62, ChromeDriver 2.32, Platform=Linux). From what I can tell, that version *should(?)* have printToPDF support. – Eli Collins Oct 30 '17 at 21:32
  • Try with the canary version or the latest from [woolyss](https://chromium.woolyss.com/) (64.0.3249.0) – Florent B. Oct 30 '17 at 21:36
  • 3
    Ah, tracked it down. Per [puppeteer's printToPDF docs](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagepdfoptions), it's currently only available in headless mode. Everything works perfect soon as I turn that on. – Eli Collins Oct 30 '17 at 21:39
4

OK, only for reference, this is how I made it work on 2019 without generating an exception:

def send_devtools(driver, cmd, params={}):
    resource = "/session/%s/chromium/send_command_and_get_result" % driver.session_id
    url = driver.command_executor._url + resource
    body = json.dumps({'cmd': cmd, 'params': params})
    response = driver.command_executor._request('POST', url, body)
    #print (response)
    if (response.get('value') is not None):
        return response.get('value')
    else:
        return None

def save_as_pdf(driver, path, options={}):
    # https://timvdlippe.github.io/devtools-protocol/tot/Page#method-printToPDF
    result = send_devtools(driver, "Page.printToPDF", options)
    if (result is not None):
        with open(path, 'wb') as file:
            file.write(base64.b64decode(result['data']))
        return True
    else:
        return False
  • Welcome to SO! Please edit your answer and explain a bit the context, and why it is possible working. For more guidance, see https://stackoverflow.com/help/how-to-answer – B--rian Aug 15 '19 at 09:11