Печать PDF с помощью selenium на Heroku (Django)

Я действительно борюсь с проблемой. Я потратил вечер, пытаясь решить ее, провел множество исследований, но не могу найти подсказку для ее решения.

Мой проект: печать определенной веб-страницы в формате PDF, используя selenium и chrome webdriver, на приложении heroku через django. Идея заключается в том, что пользователь (я) получает pdf файл сразу после заполнения формы на сайте с выбранной ссылкой. Мой скрипт печати отлично работает локально (pdf загружается с правильными параметрами), мое приложение heroku работает хорошо (мне удалось получить ссылку из моей формы Django).

Вот мой метод index, связанный с формой, которую я использую в своем шаблоне. После заполнения формы вызывается функция (MyPdfSaver()), которая должна распечатать pdf.

def index(request):
submitbutton = request.POST.get("submit")

study_url = ''
test_value = ''
form = UserForm(request.POST or None)
if form.is_valid():
    study_url = form.cleaned_data.get("study_url")
    test_value = MyPdfSaver(study_url) #calling the function that prints as PDF. 

context = {
    'form': form,
    'study_url': study_url, #the link I want to print a PDF from
    'submitbutton': submitbutton,
    'test_value' : str(test_value) #just a test value 
}

return render(request, 'index.html', context)

Вот моя функция MyPdfSaver (идея заключается в том, чтобы загрузить pdf непосредственно через эту функцию и вернуть простую ссылку на pdf) :

def MyPdfSaver(study_url):

# set webdriver and set chrome_options/settings
chrome_options = webdriver.ChromeOptions()
settings = {
    "recentDestinations": [{
        "id": "Save as PDF",
        "origin": "local",
        "account": "",
    }],
    "selectedDestinationId":
    "Save as PDF",
    "version":
    2,
    "isCssBackgroundEnabled":
    True
}
prefs = {
    'printing.print_preview_sticky_settings.appState':
    json.dumps(settings)
}
chrome_options.add_experimental_option('prefs', prefs)
chrome_options.add_argument('--kiosk-printing')
chrome_options.add_argument('--headless')
chrome_options.add_argument("--disable-dev-shm-usage")
chrome_options.add_argument("--no-sandbox")

# set chrome bin location & driver (change paths to run locally)
chrome_options.binary_location = os.environ.get("GOOGLE_CHROME_BIN")
driver = webdriver.Chrome(
    executable_path=os.environ.get("CHROMEDRIVER_PATH"),
    chrome_options=chrome_options)
driver.get(study_url)

# necessary for webpage to fully load
time.sleep(3)

html_source = driver.page_source
driver.execute_script('return window.print();') #is supposed to run the pdf printing

return str(type(html_source)) #just a try to see if it returns my driver well.

Итак! Все работает почти хорошо: ссылка отправляется в мою функцию MyPdfSaver(), MyPdfSaver() запускает webdriver без ошибки и возвращает содержимое драйвера (например, html-код, как в моем коде). Но вот эта строка кода, похоже, не работает :

driver.execute_script('return window.print();') #I also tried without the "return"

Хотя предполагается загрузить pdf "в" моем приложении Heroku, я не могу найти файл нигде... Я попытался получить доступ к bash, используя heroku run bash и запустить ls -R, чтобы увидеть все в моем приложении, но я не могу найти никакого PDF...

Итак, главный вопрос: должен ли мой driver.execute_script('return window.print();') работать? И если это так, то куда девается скачанный файл ?

Не стесняйтесь, если вам нужна дополнительная информация!

Тааанк вам :-)

PS : Я видел, что Selenium 4 предоставляет функцию driver.print(), но я не нашел никакой документации по Python, чтобы использовать ее правильно, и я думаю, что проблема будет такой же: я не знаю, куда попадают мои файлы в моем приложении heroku, когда selenium загружает его.

Создайте новую функцию представления,

from django.http import FileResponse, Http404
    
def get_pdf(request, filename):
      try:
        return FileResponse(open('path_to_pdf/%s.pdf' % (filename), 'rb'), content_type='application/pdf')
      except FileNotFoundError:
        raise Http404()

И определите путь URL,

path('pdf/<str:filename>', get_pdf)

Вернуть ссылку на файл пользователю.

Пример: app.com/pdf/file.pdf

Совсем забыл опубликовать ответ! Вот он :

import base64
import json
import os
import time
import base64

from django.http import FileResponse, HttpResponse
from django.shortcuts import render
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from django.http import FileResponse

from .forms import UserForm



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)
    return response.get('value')


def get_pdf_from_html(driver,
                      url,
                      print_options={},
                      output_file_path="example.pdf"):
    driver.get(url)
    time.sleep(5)
    calculated_print_options = {
        'landscape': False,
        'displayHeaderFooter': True,
        'printBackground': True,
        'preferCSSPageSize': True,
    }
    calculated_print_options.update(print_options)
    result = send_devtools(driver, "Page.printToPDF", calculated_print_options)
    data = base64.b64decode(result['data'])
    with open(output_file_path, "wb") as f:
        f.write(data)

def YanportSaver(study_url):
    webdriver_options = Options()
    webdriver_options.add_argument("--no-sandbox")
    webdriver_options.add_argument('--headless')
    webdriver_options.add_argument('--disable-gpu')

    driver = webdriver.Chrome(
        executable_path=os.environ.get("CHROMEDRIVER_PATH"),
        options=webdriver_options)
    get_pdf_from_html(driver, study_url)
    driver.quit()




def index(request):
    # makes the form dynamic + returns index when form is not completed, and
    # file once it's done
    submitbutton = request.POST.get("submit")

    study_url = ''
    test_value = ''
    form = UserForm(request.POST or None)
    if form.is_valid():
        study_url = form.cleaned_data.get("study_url") #retrieve study_url
        YanportSaver(study_url) #call the func that creates and dl pdf
        response = FileResponse(open('../app/example.pdf', 'rb'))
        return response

    context = {
        'form': form,
        'study_url': study_url,
        'submitbutton': submitbutton,
        'test_value' : str(test_value)
    }

    return render(request, 'index.html', context)
Вернуться на верх