Python İle PDF İçerisinden Metin Kazımak

Bir süredir tahmin (forecasting) çalışmaları için çeşitli çözümleri deniyor, süreci ve yöntemleri anlamaya çalışıyorum. Bunlardan biri de Facebook tarafından geliştirilen, kolay bir şekilde R ve Python programlama dilleri ile kullanabilen Prophet prosedürü oldu.

AA

Prophet, ile ilgili işlemlerde kolay bir şekilde özelleştirmeler yapılabiliyor. Mevsimsellik ve tatil etkilerini modele ayrıca dahil etmek mümkün1. Bu amaçla, prophet ile ilgili çalışma yaparken resmi tatilleri de sürece dahil etmek istedim. Kullandığım veri seti 2017-2021 yıllarını kapsadığı için bu yıllar arasındaki tatil günlerini bulmaya çalışırken 2014-2022 yılları arasındaki resmi tatil günlerinin yer aldığı bir PDF dosyasına ulaştım2.

PDF içerisinden metin kazıma ile ilgili paketleri bir süredir incelemek istiyordum. Güzel bir fırsat elde etmiş oldum.

İlgili PDF dosyası 2 sayfa ve toplamda her yıl için 1 adet olmak üzere 9 tablodan oluşmakta. Aşağıdaki görselde de görüldüğü üzere her tablo başlık, 2 sütun ve 16 satırdan oluşmakta. Tarih bilgileri tabloların solunda yer almakta.

Python - PDF Metin Kazıma
2014 - 2022 Yıllara Göre Resmi Tatil Günleri

Python ve PDF Dosyasından Metin Kazıma İşlemi

Python programlama dili PDF okuma-yazma işlemleri ile ilgili olarak PDFMiner, PyPDF2, Tabula-py, Slate, PDFQuery ve xpdf gibi pek çok popüler pakete sahip3. Bu paketlerden PDFMiner artık pdfminer.six forku üzerinden geliştirilmekte. Seçenekler arasında PyPDF24 ve tabular5 popülerlikleri ile öne çıktıkları için her iki paketi de denemeye karar verdim.

PyPDF2

Python ile PDF dosyalarını okumak ve yazmak için geliştirilen bir yardımcı paket/uygulama olan PyPDF2 esasında 2 011 yılından bu yana geliştirilen bir pyPdf forku.

PdfFileReader sınıfına ait stream parametresine verilen bir file nesnesi veya bir file nesnesine benzer bir nesnesinin yanı sıra bir PDF dosyasının yolunu temsil eden bir path değeri ile işlemler gerçekleştirilebilmesini sağlamakta6.

Hemen örnek işlemimize geçelim.

#!pip install PyPDF2

import urllib.request, io, PyPDF2
import pandas as pd

url = "https://fenbil.aku.edu.tr/FENBILENS/takvim/2014-2022-1RESMI.pdf"
remoteFile = urllib.request.urlopen(url)
pdfReader = PyPDF2.PdfFileReader(io.BytesIO(remoteFile.read()))

Artık pdfReader'ın sahip olduğu method ve property'lerden faydalanabiliriz. pdfReader.numPages bize PDF dosyasına ait sayfa sayısını verecektir. Her sayfa için metin kazıma işlemi ayr ayrı gerçekleştirileceği için sayfa sayısını loop için kullanabiliriz. Örneğin, ilk sayfada yer alan metinlere pdfReader.getPage(0).extractText() şeklinde ulaşabiliriz.

Sonraki işlemlerde kullanmak üzere metinleri bir metin dosyasına alalım.

for i in range(pdfReader.numPages):
  with open('pdf.txt', 'a+') as file:
    file.write(pdfReader.getPage(i).extractText())

Metin dosyasında da görüldüğü üzere pek çok boşluk ve tablo alanları içerisinde kaymalar mevcut.

df = pd.read_csv('/content/pdf.txt', delimiter = "\n", names=['date'])
df.head()

Açıkçası çok istediğim gibi olmadı. Sütunlar arasındaki kaymalar ve bazı verilerin kazınamamış olması sebebiyle bir başka paketi daha denemeye karar verdim.

date
0   01 Ocak 2016 Cuma
1   23 Nisan 2016 Cumartesi
2   27 Temmuz 2014 Pazar
3   Arefe
4   04 Temmuz 2016 Pazartesi
5   28 Temmuz 2014 Pazartesi
6   17 Temmuz 2015 Cuma
7   18 Temmuz 2015 Cumartesi
8   19 Temmuz 2015 Pazar
9   03 Ekim 2014 Cuma
10  Arefe
...

İlgili kod parçacığına tek parça olarak Python-PyPDF2.py üzerinden ulaşabilirsiniz.

Tabula.py

Java 8+ ve Python 3.6+ gerektirmekte. Verilerin CSV, TSV, JSON gibi formatlara aktarılabilmesini sağlayan, bunun yanı sıra tabloları pandas DataFrame olarak sunabilen Tabula dosya yolu olarak URL de alabilmekte7.

Hemen örneğimize geçelim.

Döngüyü çok daha uygun bir şekilde ele alana kadar nested loop örneği paylaştım. (16, 3)'lük shape'e sahip 3 tabloyu tek bir listeye dönüştürmek beklediğimden daha uğraştırıcı oldu (: Kodun bir önceki revision ve yorum olarak gist sayfasında yer almakta.

İlgili kod parçacığına tek parça olarak Python-Tabula.py üzerinden ulaşabilirsiniz.

0     2014-01-01
1     2014-04-23
2     2014-05-01
3     2014-05-19
4     2014-07-27
         ...    
139   2022-07-11
140   2022-07-12
141   2022-08-30
142   2022-10-28
143   2022-10-29
Name: Date, Length: 144, dtype: datetime64[ns]

Bu işlemin neticesinde 144 kayıttan oluşan tatil bilgisini doğru bir şekilde elde etmiş olduk.

m = Prophet(holidays=holidays)
forecast = m.fit(df).predict(future)

Bir sonraki yazıda Prophet ile ilgili notlarımı aktarmaya çalışacağım.