Ekstrakcja danych w języku Python z zaszyfrowanego pliku PDF

12

Jestem niedawnym absolwentem czystej matematyki, który odbył tylko kilka podstawowych kursów programowania. Robię staż i mam wewnętrzny projekt analizy danych. Muszę przeanalizować wewnętrzne pliki PDF z ostatnich lat. Pliki PDF są „zabezpieczone”. Innymi słowy, są one szyfrowane. Nie mamy haseł PDF, co więcej, nie jesteśmy pewni, czy istnieją hasła. Ale mamy wszystkie te dokumenty i możemy je odczytać ręcznie. Możemy je również wydrukować. Celem jest przeczytanie ich w Pythonie, ponieważ jest to język, który mamy jakiś pomysł.

Najpierw próbowałem odczytać pliki PDF w niektórych bibliotekach Python. Jednak biblioteki Pythona, które znalazłem, nie czytają zaszyfrowanych plików PDF. W tym czasie nie mogłem również wyeksportować informacji za pomocą programu Adobe Reader.

Po drugie, postanowiłem odszyfrować pliki PDF. Udało mi się użyć pykepdf z biblioteki Python. Pykepdf działa bardzo dobrze! Jednak odszyfrowanych plików PDF nie można również odczytać za pomocą bibliotek Pythona z poprzedniego punktu ( PyPDF2 i Tabula ). W tej chwili wprowadziliśmy pewne ulepszenia, ponieważ za pomocą programu Adobe Reader mogę eksportować informacje z odszyfrowanych plików PDF, ale celem jest zrobienie wszystkiego za pomocą Pythona.

Kod, który pokazuję, działa idealnie z niezaszyfrowanymi plikami PDF, ale nie z zaszyfrowanymi plikami PDF. Nie działa również z odszyfrowanymi plikami PDF, które zostały uzyskane przy pomocy pykepdf.

Nie napisałem kodu. Znalazłem to w dokumentacji bibliotek Pythona Pykepdf i Tabula . Rozwiązanie PyPDF2 zostało napisane przez Al Sweigarta w jego książce „ Automate the Boring Stuff with Python ”, którą bardzo polecam. Sprawdziłem również, czy kod działa poprawnie, z ograniczeniami, które wcześniej wyjaśniłem.

Pierwsze pytanie, dlaczego nie mogę odczytać odszyfrowanych plików, jeśli programy działają z plikami, które nigdy nie zostały zaszyfrowane?

Drugie pytanie: czy możemy jakoś odczytać w Pythonie odszyfrowane pliki? Która biblioteka może to zrobić, czy jest to niemożliwe? Czy wszystkie odszyfrowane pliki PDF można wyodrębnić?

Dziękuję za twój czas i pomoc!!!

Znalazłem te wyniki przy użyciu Python 3.7, Windows 10, Jupiter Notebooks i Anaconda 2019.07.

Python

import pikepdf
with pikepdf.open("encrypted.pdf") as pdf:
  num_pages = len(pdf.pages)
  del pdf.pages[-1]
  pdf.save("decrypted.pdf")

import tabula
tabula.read_pdf("decrypted.pdf", stream=True)

import PyPDF2
pdfFileObj=open("decrypted.pdf", "rb")
pdfReader=PyPDF2.PdfFileReader(pdfFileObj)
pdfReader.numPages
pageObj=pdfReader.getPage(0)
pageObj.extractText()

W przypadku Tabuli pojawia się komunikat „plik wyjściowy jest pusty”.

Dzięki PyPDF2 otrzymuję tylko „/ n”

AKTUALIZACJA 10/3/2019 Pdfminer.six (wersja z listopada 2018 r.)

Lepsze wyniki uzyskałem dzięki rozwiązaniu opublikowanemu przez DuckPuncher . Dla odszyfrowanego pliku dostałem etykiety, ale nie dane. To samo dzieje się z zaszyfrowanym plikiem. Do pliku, który nigdy nie był zaszyfrowany, działa idealnie. Ponieważ potrzebuję danych i etykiet zaszyfrowanych lub odszyfrowanych plików, ten kod nie działa dla mnie. Do tej analizy wykorzystałem pdfminer.six , czyli bibliotekę Python wydaną w listopadzie 2018 roku. Pdfminer.six zawiera pycryptodome biblioteki. Według ich dokumentacji „ PyCryptodome jest samodzielnym pakietem Pythona niskopoziomowych prymitywów kryptograficznych ..”

Kod znajduje się w pytaniu dotyczącym wymiany stosów: Wyodrębnianie tekstu z pliku PDF za pomocą programu PDFMiner w pythonie?

Chciałbym, jeśli chcesz powtórzyć mój eksperyment. Oto opis:

1) Uruchom kody wymienione w tym pytaniu z dowolnym plikiem PDF, który nigdy nie został zaszyfrowany.

2) Zrób to samo z „Bezpiecznym” plikiem PDF (jest to termin, którego używa Adobe), nazywam go zaszyfrowanym plikiem PDF. Użyj ogólnego formularza, który można znaleźć za pomocą Google. Po pobraniu musisz wypełnić pola. W przeciwnym razie sprawdzane byłyby etykiety, ale nie pola. Dane są w polach.

3) Odszyfruj zaszyfrowany plik PDF za pomocą Pykepdf. To będzie odszyfrowany plik PDF.

4) Uruchom kody ponownie, używając odszyfrowanego pliku PDF.

AKTUALIZACJA 10/4/2019 Camelot (wersja lipiec 2019)

Znalazłem bibliotekę Python Camelot. Bądź ostrożny, że potrzebujesz camelot-py 0.7.3.

Jest bardzo wydajny i współpracuje z Pythonem 3.7. Jest również bardzo łatwy w użyciu. Najpierw musisz również zainstalować Ghostscript . W przeciwnym razie to nie zadziała. Musisz także zainstalować Pandy . Nie używaj pip install camelot-py . Zamiast tego użyj pip install camelot-py [cv]

Autorem programu jest Vinayak Mehta. Frank Du udostępnia ten kod w filmie na YouTube „Wyodrębnij dane tabelaryczne z pliku PDF za pomocą Camelot za pomocą Pythona”.

Sprawdziłem kod i działa on z nieszyfrowanymi plikami. Jednak nie działa z zaszyfrowanymi i odszyfrowanymi plikami i to jest mój cel .

Camelot jest zorientowany na pobieranie tabel z plików PDF.

Oto kod:

Python

import camelot
import pandas
name_table = camelot.read_pdf("uncrypted.pdf")
type(name_table)

#This is a Pandas dataframe
name_table[0]

first_table = name_table[0]   

#Translate camelot table object to a pandas dataframe
first_table.df

first_table.to_excel("unencrypted.xlsx")
#This creates an excel file.
#Same can be done with csv, json, html, or sqlite.

#To get all the tables of the pdf you need to use this code.
for table in name_table:
   print(table.df)

AKTUALIZACJA 10/7/2019 Znalazłem jedną sztuczkę. Jeśli otworzę zabezpieczony plik PDF w programie Adobe Reader i wydrukuję go przy użyciu programu Microsoft do formatu PDF i zapiszę go jako plik PDF, mogę wyodrębnić dane przy użyciu tej kopii. Mogę również przekonwertować plik PDF na JSON, Excel, SQLite, CSV, HTML i inne formaty. To możliwe rozwiązanie mojego pytania. Jednak wciąż szukam opcji, aby to zrobić bez tej sztuczki, ponieważ celem jest zrobienie tego w 100% z Pythonem. Obawiam się również, że zastosowanie lepszej metody szyfrowania może nie zadziałać. Czasami trzeba użyć programu Adobe Reader kilka razy, aby uzyskać ekstrakt.

AKTUALIZACJA 10/8/2019. Trzecie pytanie. Mam teraz trzecie pytanie. Czy wszystkie zabezpieczone / zaszyfrowane pliki pdf są chronione hasłem? Dlaczego pikepdf nie działa? Domyślam się, że obecna wersja pikepdf może złamać jakiś rodzaj szyfrowania, ale nie wszystkie. @constt wspomniał, że PyPDF2 może złamać jakiś rodzaj ochrony. Odpowiedziałem mu jednak, że znalazłem artykuł, w którym PyPDF2 może złamać szyfrowanie wykonane za pomocą Adobe Acrobat Pro 6.0, ale nie w późniejszych wersjach.

Początkujący
źródło
2
Nie mogłem odtworzyć tych problemów PyPDF2, wszystko działa dobrze. pdftkDo szyfrowania plików korzystałem również z usług online. Czy możesz publikować linki do „kłopotliwych” plików pdf?
constt
1
Ok dzięki! Czy próbowałeś użyć qpdfdo odszyfrowania plików? Jeśli to zrobi, możesz wywołać go ze skryptu za pomocą subprocessmodułu do odszyfrowania plików przed ich analizą.
constt
1
Po pierwsze, PyPDF2 nie może odszyfrować plików Acrobat PDF => 6.0. Po drugie, pikepdf obecnie nie ma narzędzia do ekstrakcji tekstu.
Życie jest skomplikowane
1
@Beginner Spekulowałbym, że ma to związek z podstawowym formatowaniem używanym przez pykepdf do pisania niezaszyfrowanego pliku PDF.
Życie jest skomplikowane
2
„Czy wszystkie zabezpieczone / zaszyfrowane pliki PDF są chronione hasłem?” - nie Istnieją również pliki PDF zaszyfrowane przy użyciu kryptografii klucza prywatnego / publicznego na podstawie certyfikatów X509.
mkl

Odpowiedzi:

8

OSTATNIA AKTUALIZACJA 10-11-2019

Nie jestem pewien, czy całkowicie rozumiem twoje pytanie. Poniższy kod można ulepszyć, ale czyta on zarówno zaszyfrowany, jak i niezaszyfrowany plik PDF i wyodrębnia tekst. Daj mi znać, jeśli źle zrozumiem twoje wymagania.

from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
from io import StringIO

def extract_encrypted_pdf_text(path, encryption_true, decryption_password):

  output = StringIO()

  resource_manager = PDFResourceManager()
  laparams = LAParams()

  device = TextConverter(resource_manager, output, codec='utf-8', laparams=laparams)

  pdf_infile = open(path, 'rb')
  interpreter = PDFPageInterpreter(resource_manager, device)

  page_numbers = set()

  if encryption_true == False:
    for page in PDFPage.get_pages(pdf_infile, page_numbers, maxpages=0, caching=True, check_extractable=True):
      interpreter.process_page(page)

  elif encryption_true == True:
    for page in PDFPage.get_pages(pdf_infile, page_numbers, maxpages=0, password=decryption_password, caching=True, check_extractable=True):
      interpreter.process_page(page)

 text = output.getvalue()
 pdf_infile.close()
 device.close()
 output.close()
return text

results = extract_encrypted_pdf_text('encrypted.pdf', True, 'password')
print (results)

Zauważyłem, że w kodzie pikepdf używanym do otwierania zaszyfrowanego pliku PDF brakowało hasła, które powinno spowodować wyświetlenie tego komunikatu o błędzie:

pikepdf._qpdf.PasswordError: encrypted.pdf: nieprawidłowe hasło

import pikepdf

with pikepdf.open("encrypted.pdf", password='password') as pdf:
num_pages = len(pdf.pages)
del pdf.pages[-1]
pdf.save("decrypted.pdf")

Możesz użyć tika, aby wyodrębnić tekst z pliku odszyfrowanego.pdf utworzonego przez pikepdf .

from tika import parser

parsedPDF = parser.from_file("decrypted.pdf")
pdf = parsedPDF["content"]
pdf = pdf.replace('\n\n', '\n')

Ponadto pikepdf obecnie nie implementuje ekstrakcji tekstu, w tym najnowszej wersji v1.6.4.


Zdecydowałem się przeprowadzić kilka testów przy użyciu różnych zaszyfrowanych plików PDF.

Wszystkie zaszyfrowane pliki nazwałem „encrypted.pdf” i wszystkie używały tego samego hasła szyfrowania i deszyfrowania.

  1. Adobe Acrobat 9.0 i nowsze wersje - 256-bitowy AES na poziomie szyfrowania

    • pikepdf był w stanie odszyfrować ten plik
    • PyPDF2 nie mógł poprawnie wyodrębnić tekstu
    • tika może poprawnie wyodrębnić tekst
  2. Adobe Acrobat 6.0 i nowsze wersje - poziom szyfrowania 128-bitowy RC4

    • pikepdf był w stanie odszyfrować ten plik
    • PyPDF2 nie mógł poprawnie wyodrębnić tekstu
    • tika może poprawnie wyodrębnić tekst
  3. Adobe Acrobat 3.0 i nowsze wersje - poziom szyfrowania 40-bit RC4

    • pikepdf był w stanie odszyfrować ten plik
    • PyPDF2 nie mógł poprawnie wyodrębnić tekstu
    • tika może poprawnie wyodrębnić tekst
  4. Adobe Acrobat 5.0 i nowsze wersje - poziom szyfrowania 128-bitowy RC4

    • utworzony za pomocą Microsoft Word
    • pikepdf był w stanie odszyfrować ten plik
    • PyPDF2 może poprawnie wyodrębnić tekst
    • tika może poprawnie wyodrębnić tekst
  5. Adobe Acrobat 9.0 i nowsze wersje - 256-bitowy AES na poziomie szyfrowania

    • utworzony za pomocą pdfprotectfree
    • pikepdf był w stanie odszyfrować ten plik
    • PyPDF2 może poprawnie wyodrębnić tekst
    • tika może poprawnie wyodrębnić tekst

PyPDF2 był w stanie wyodrębnić tekst z odszyfrowanych plików PDF, które nie zostały utworzone za pomocą Adobe Acrobat.

Zakładam, że niepowodzenia mają coś wspólnego z formatowaniem osadzonym w plikach PDF utworzonych przez Adobe Acrobat. Wymagane są dalsze testy, aby potwierdzić to przypuszczenie dotyczące formatowania.

tika była w stanie wyodrębnić tekst ze wszystkich dokumentów odszyfrowanych za pomocą pikepdf.


 import pikepdf
 with pikepdf.open("encrypted.pdf", password='password') as pdf:
    num_pages = len(pdf.pages)
    del pdf.pages[-1]
    pdf.save("decrypted.pdf")


 from PyPDF2 import PdfFileReader

 def text_extractor(path):
   with open(path, 'rb') as f:
     pdf = PdfFileReader(f)
     page = pdf.getPage(1)
     print('Page type: {}'.format(str(type(page))))
     text = page.extractText()
     print(text)

    text_extractor('decrypted.pdf')

PyPDF2 nie może odszyfrować plików Acrobat PDF => 6.0

Ten problem jest otwarty dla właścicieli modułów od 15 września 2015 r . W komentarzach dotyczących tego problemu nie jest jasne, kiedy ten problem zostanie rozwiązany przez właścicieli projektu. Ostatnim zatwierdzeniem był 25 czerwca 2018 r.

Problemy z deszyfrowaniem PyPDF4

PyPDF4 jest zamiennikiem PyPDF2. Ten moduł ma również problemy z odszyfrowaniem niektórych algorytmów używanych do szyfrowania plików PDF.

plik testowy: Adobe Acrobat 9.0 i nowsze wersje - poziom szyfrowania 256-bitowy AES

Komunikat o błędzie PyPDF2: obsługiwane są tylko kody algorytmu 1 i 2

Komunikat o błędzie PyPDF4: obsługiwane są tylko kody algorytmu 1 i 2. Ten plik PDF używa kodu 5


AKTUALIZACJA SEKCJA 10-11-2019

Ta sekcja jest odpowiedzią na twoje aktualizacje z 10-07-2019 i 10-08-2019.

W aktualizacji stwierdziłeś, że możesz otworzyć „zabezpieczony plik PDF za pomocą programu Adobe Reader” i wydrukować dokument do innego pliku PDF, co usuwa flagę „SECURED”. Po przeprowadzeniu testów wydaje mi się, że zorientowali się, co dzieje się w tym scenariuszu.

Poziom bezpieczeństwa plików Adobe PDF

Pliki Adobe PDF mają wiele rodzajów kontroli bezpieczeństwa, które może włączyć właściciel dokumentu. Kontrole można wymusić za pomocą hasła lub certyfikatu.

  1. Szyfrowanie dokumentu (wymuszone hasłem otwarcia dokumentu)

    • Zaszyfruj całą zawartość dokumentu (najczęściej)
    • Zaszyfruj całą zawartość dokumentu oprócz metadanych => Acrobat 6.0
    • Szyfruj tylko załączniki plików => Acrobat 7.0
  2. Restrykcyjna edycja i drukowanie (wymuszone hasłem uprawnień)

    • Drukowanie dozwolone
    • Zmiany dozwolone

Poniższy obraz pokazuje, że plik Adobe PDF jest szyfrowany za pomocą 256-bitowego szyfrowania AES. Aby otworzyć lub wydrukować ten plik PDF, wymagane jest hasło. Po otwarciu tego dokumentu w programie Adobe Reader za pomocą hasła tytuł będzie miał status ZABEZPIECZONY

hasło_poziomowanie_szyfrowania

Ten dokument wymaga hasła do otwarcia za pomocą modułów Python wymienionych w tej odpowiedzi. Jeśli spróbujesz otworzyć zaszyfrowany plik PDF za pomocą programu Adobe Reader. Powinieneś to zobaczyć:

monit o hasło

Jeśli nie pojawi się to ostrzeżenie, dokument albo nie ma włączonej kontroli bezpieczeństwa, albo ma tylko ograniczoną edycję i drukowanie.

Poniższy obraz pokazuje restrykcyjną edycję z włączonym hasłem w dokumencie PDF. Drukowanie notatek jest włączone . Aby otworzyć lub wydrukować ten plik PDF, hasło nie jest wymagane . Gdy otworzysz ten dokument w programie Adobe Reader bez hasła, tytuł będzie brzmiał ZABEZPIECZONY To jest to samo ostrzeżenie, co zaszyfrowany plik PDF otwarty za pomocą hasła.

Podczas drukowania tego dokumentu do nowego pliku PDF ostrzeżenie BEZPIECZNE jest usuwane, ponieważ została usunięta restrykcyjna edycja.

poziom_elementu_rejestrującego hasło

Wszystkie produkty Adobe egzekwują ograniczenia określone przez hasło uprawnień. Jeśli jednak produkty innych firm nie obsługują tych ustawień, odbiorcy dokumentów mogą ominąć niektóre lub wszystkie ustawione ograniczenia .

Zakładam więc, że dokument, który drukujesz do formatu PDF ma włączoną restrykcyjną edycję i nie ma hasła wymaganego do otwarcia.

Dotyczące złamania szyfrowania PDF

Ani PyPDF2, ani PyPDF4 nie mają na celu złamania funkcji otwierania dokumentu w dokumencie za pomocą hasła. Oba moduły wygenerują następujący błąd, jeśli spróbują otworzyć zaszyfrowany plik PDF chroniony hasłem.

PyPDF2.utils.PdfReadError: plik nie został odszyfrowany

Funkcja otwierania hasła zaszyfrowanego pliku PDF może być ominięta przy użyciu różnych metod, ale jedna technika może nie działać, a niektóre nie będą akceptowane z powodu kilku czynników, w tym złożoności hasła.

Szyfrowanie PDF wewnętrznie działa z kluczami szyfrującymi 40, 128 lub 256 bitów, w zależności od wersji PDF. Binarny klucz szyfrujący pochodzi od hasła podanego przez użytkownika. Hasło podlega ograniczeniom długości i kodowania.

Na przykład PDF 1.7 Adobe Extension Level 3 (Acrobat 9 - AES-256) wprowadził znaki Unicode (65 536 możliwych znaków) i podniósł maksymalną długość do 127 bajtów w reprezentacji hasła UTF-8.


Poniższy kod otworzy plik PDF z włączoną ograniczoną edycją. Zapisuje ten plik w nowym pliku PDF bez dodawania ostrzeżenia SECURED. Tika kod będzie analizować zawartość z nowego pliku.

from tika import parser
import pikepdf

# opens a PDF with restrictive editing enabled, but that still 
# allows printing.
with pikepdf.open("restrictive_editing_enabled.pdf") as pdf:
  pdf.save("restrictive_editing_removed.pdf")

  # plain text output
  parsedPDF = parser.from_file("restrictive_editing_removed.pdf")

  # XHTML output
  # parsedPDF = parser.from_file("restrictive_editing_removed.pdf", xmlContent=True)

  pdf = parsedPDF["content"]
  pdf = pdf.replace('\n\n', '\n')
  print (pdf)

Ten kod sprawdza, czy do otwarcia pliku wymagane jest hasło. Ten kod można ulepszyć i można dodać inne funkcje. Istnieje kilka innych funkcji, które można dodać, ale dokumentacja pikepdf nie odpowiada komentarzom w bazie kodu, więc potrzeba więcej badań, aby to poprawić.

# this would be removed once logging is used
############################################
import sys
sys.tracebacklimit = 0
############################################

import pikepdf
from tika import parser

def create_pdf_copy(pdf_file_name):
  with pikepdf.open(pdf_file_name) as pdf:
    new_filename = f'copy_{pdf_file_name}'
    pdf.save(new_filename)
    return  new_filename

def extract_pdf_content(pdf_file_name):
  # plain text output
  # parsedPDF = parser.from_file("restrictive_editing_removed.pdf")

  # XHTML output
  parsedPDF = parser.from_file(pdf_file_name, xmlContent=True)

  pdf = parsedPDF["content"]
  pdf = pdf.replace('\n\n', '\n')
  return pdf

def password_required(pdf_file_name):
  try:
    pikepdf.open(pdf_file_name)

  except pikepdf.PasswordError as error:
    return ('password required')

  except pikepdf.PdfError as results:
    return ('cannot open file')


filename = 'decrypted.pdf'
password = password_required(filename)
if password != None:
  print (password)
elif password == None:
  pdf_file = create_pdf_copy(filename)
  results = extract_pdf_content(pdf_file)
  print (results)
Życie jest złożone
źródło
2
Jak otwierać bezpieczny plik PDF bez podawania hasła?
Życie jest skomplikowane
1
Masz na myśli jedynie restrykcyjną ochronę edycji?
Życie jest złożone
1
Odpowiedź zaktualizowana o kod, który działał z plikiem PDF z włączoną restrykcyjną ochroną edycji, ale zezwalającą na drukowanie.
Życie jest złożone
1
Czy umiesz używać XHTML?
Życie jest złożone
1
Zmodyfikowałem odpowiedź na wynik XHTML. JSON jest możliwy, ale wymaga zagłębienia się w kod projektu github związany z parserem tika.
Życie jest złożone
1

Możesz spróbować poradzić sobie z błędem, który powstają podczas otwierania tych plików bez hasła.

import pikepdf

def open_pdf(pdf_file_path, pdf_password=''):
    try:
        pdf_obj = pikepdf.Pdf.open(pdf_file_path)

    except pikepdf._qpdf.PasswordError:
        pdf_obj = pikepdf.Pdf.open(pdf_file_path, password=pdf_password)

    finally:
        return pdf_obj

Możesz użyć zwróconego pliku pdf_obj do analizy składni. Możesz także podać hasło w przypadku, gdy masz zaszyfrowany plik PDF.

Mahendra Singh
źródło
1
Dziękuję za Twoją odpowiedź! Staramy się czytać bez hasła. W tym czasie byliśmy w stanie to zrobić za pomocą metody opisanej w mojej AKTUALIZACJI 10/7/2019
Początkujący
To dalekie od odpowiedzi na pytanie. Wygląda na to, że nie przeczytałeś pełnego pytania.
shoonya ek
1
To obsługuje te zabezpieczone pliki PDF, w których normalnie pikepdf zawiedzie, gdy domyślna wartość hasła to Brak. Przekazując pusty ciąg, jest on w stanie poprawnie otworzyć i przeanalizować zabezpieczony dokument PDF (w testowanych przeze mnie przypadkach).
Mahendra Singh,
1
@ Rozpocznij w tym przypadku nie musisz konwertować plików PDF. Właśnie z moich wcześniejszych doświadczeń wynika, że ​​zabezpieczone pliki PDF działają, podając puste hasło.
Mahendra Singh,
1
@ Początkujący to mój cały kod. Zwraca tylko pdf_object z pikepdf. Jeśli chcesz zapisać ten plik pdf, po prostu zapisz zwrócony obiekt, używając pdf_obj.save ('twoja_plik_pliku'). Następnie możesz użyć tego pliku PDF do parsowania tekstu i innych obiektów. Do ekstrakcji tekstu używam biblioteki o nazwie PdfPlumber .
Mahendra Singh
1

W przypadku tabula-py możesz wypróbować opcję hasła w read_pdf. Zależy to od funkcji tabula-java, więc nie jestem pewien, które szyfrowanie jest obsługiwane.

Chezou
źródło