Django專案實戰: Django + PyPDF2實現PDF頁面提取和PDF檔案輸出
在日常工作中我們經常需要從一個大的PDF文件中提取我們所需要的頁面,所以今天我們將教你用Django + PyPDF2開發個小Web應用: 使用者上傳一個PDF文件,輸入需要提取的頁面號碼,點選確定後瀏覽器會自動給使用者返回想提取的PDF頁面。如果你要練習這個專案,你首先要確保已安裝Django 2.X + Python 3.X。如果你還沒有安裝PyPDF2,可以通過pip install PyPDF2安裝好這個第三方包。如果你喜歡我們的原創文章,歡迎關注我們的微信公眾號【Python與Django大咖之路】。
專案開發總體思路
我們的開發思路是這樣子的。我們設計一個表單,讓使用者上傳PDF檔案和輸入提取頁面號碼。伺服器在收到PDF檔案後使用PyPDF2讀取使用者上傳的PDF檔案,提取我們所需要的頁面,然後通過HttpResponse將這個新生成的PDF檔案通過瀏覽器返回給客戶。
因為這個應用很簡單,我們只需要開發一個功能性頁面,也不需要建立什麼模型Models。重點要編寫的是檢視views.py, 用來處理使用者的請求。
第一步 建立專案,設計URL
在CMD終端裡輸入python manage.py startapp pdf建立一個叫pdf的app,然後把這個app加入到你的myproject/settings.py裡INSTALLED_APPS去。
#myproject/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes' ,
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'pdf',
]
# pdf/urls.py
from django.urls import path
from . import views
# namespace
app_name = 'pdf'
urlpatterns = [
# 上傳pdf,使用者輸入需要提取的頁面, 返回需要提取的頁面
path('extract/', views.pdf_extract, name='pdf_extract'),
]
同時我們應將上述urls加入到myproject/urls.py裡去。這樣當用戶通過瀏覽器訪問/pdf/extract/時,伺服器就會呼叫我們views裡的pdf_extract方法來處理使用者的請求。
from django.contrib import admin
from django.urls import path, include
# 對於處理靜態檔案如圖片,CSS和文字非常重要
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('pdf/', include('pdf.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
第二步 設計表單forms.py
因為我們在檢視view裡需要使用到上傳檔案的表單,所以我們這裡先設計表單。這個表單非常簡單,只有2個欄位。如下所示。
# pdf/forms.py
from django import forms
class PdfUploadForm(forms.Form):
file = forms.FileField(label="上傳PDF檔案")
page = forms.IntegerField(min_value=1, label="輸入抓取頁碼")
第三步 編寫檢視views.py
檢視view是本專案中最重要的部分。其總體思路,我已經在程式碼中加了很多註釋幫你理解。PyPDF2功能強大,可以提取PDF檔案裡的文字,也可以抓取PDF檔案中的某個頁面,還可以建立新的PDF檔案, 比Django文件裡提到的reportlab要好很多。
# pdf/views.py
from django.shortcuts import render
from django.http import HttpResponse
from .forms import PdfUploadForm
import PyPDF2
# Create your views here.
def pdf_extract(request):
if request.method == 'POST':
# 如果使用者通過POST提交
form = PdfUploadForm(request.POST, request.FILES)
if form.is_valid():
# 如表單通過驗證,獲取提取檔案頁碼
page_num = int(request.POST.get("page"))
# pdf文件頁碼物件編碼是從0開始,所以減1
page_index = page_num - 1
# 獲取上傳的檔案物件
f = request.FILES['file']
# 開啟上傳的檔案,寫入新PDF檔案
with open('original.pdf', 'wb+') as pdfFileObj:
for chunk in f.chunks():
pdfFileObj.write(chunk)
# 利用PyPDF2讀取新的PDF檔案
pdfReader = PyPDF2.PdfFileReader(pdfFileObj)
# 利用PyPDF2提取頁碼物件
pageObj = pdfReader.getPage(page_index)
# 利用PyPDF2建立新的PDF檔案物件
pdfWriter = PyPDF2.PdfFileWriter()
# 新增已讀取的頁面物件
pdfWriter.addPage(pageObj)
# 將提取頁面寫入新的PDF檔案
with open('extracted_pages.pdf', 'wb') as pdfOutputFile:
pdfWriter.write(pdfOutputFile)
# 開啟新的PDF檔案,通過HttpResponse輸出
with open('extracted_pages.pdf', 'rb') as pdfExtract:
response = HttpResponse(pdfExtract.read(), content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="extracted_page_{}.pdf"'.format(page_num)
return response
else:
# 如果使用者沒有通過POST,提交生成空表單
form = PdfUploadForm()
return render(request, 'pdf/pdf_upload.html', {'form': form})
上述程式碼中最值得需要你注意的地方是我們如何通過HttpReponse將生成的pdf文件返回給使用者的。下行程式碼是最重要的,它不僅讀取了開啟的pdf檔案的內容,還指定了返回內容型別是pdf檔案, 否則會出現亂碼。
response = HttpResponse(pdfExtract.read(), content_type='application/pdf')
第四步 編寫模板template
模板檔案非常簡單,如下所示。我們在pdf資料夾裡建立了一個templates資料夾,又在裡面建立了一個新的pdf資料夾,然後把模板檔案放裡面了。至於為什麼我們這麼佈局,請閱讀Django專案推薦性的檔案與資料夾佈局。
# pdf/templates/pdf/pdf_upload.html
{% block content %}
<h3>上傳pdf檔案, 輸入頁面號碼提取頁面</h3>
<form method="post" enctype="multipart/form-data" action="">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="確定" />
</form>
{% endblock %}
第五步 檢視效果
現在你在CMD終端裡輸入python manage.py runserver。在瀏覽器中開啟http://127.0.0.1:8000/pdf/extract/你就可以看到如下效果了。
第六步 想想需要改進的地方
整個專案離實際應用還有許多需要改進的地方,比如:
使用者輸入的頁碼超過了PDF檔案總頁數怎麼辦?
如果使用者上傳的不是PDF檔案怎麼處理?
如果使用者想一次提取多個檔案怎麼辦?比如1, 3, 5, 7...
這些問題就留給讀者你思考吧,如果有問題,也歡迎給我們留言。