他山之石,可以攻玉--python調用exe文件批量移除pdf密碼
筆者前陣子在抽取pdf文字時,碰到了一個棘手的問題:無法直接編輯加密的pdf文本。
什麼是加密的pdf?加密的pdf 分為兩類:第一種為pdf在打開需要密碼;第二種是在pdf的操作受到了限制,需要密碼才能解除限制。本文討論第二種情況。
來看看一篇加密的pdf文檔有哪些權利受到了限制(訪問文檔屬性-->安全信息)。
連列印都不允許……這,有點過了,畢竟招股書是公開的文檔。
那麼究竟如何去除加密的pdf文檔的密碼?注意,這裡是去除,而不是破解。文章討論將帶有密碼保護的pdf轉化為沒有密碼保護的pdf,而不是如何獲取密碼。
--方案1--
推介一個網站【I LOVE PDF】
利用這個網站,你只需要上傳文件,就能對pdf進行切割、合併、破解、轉化為word等操作……挺棒的,免費,無需註冊!
但是,如果有超過10M的文件,網站是不受理的;如果你有100份文件,那麼就得上傳100次……尺有所長,尺有所短,網站已經儘力了,畢竟設計初衷不是為了大批量處理文件。涉及機密文件,建議不要上傳……
--方案2--
對於切割合併等基礎操作,使用庫pypdf操作即可--詳情可查看筆者之前的文章《利用python處理PDF文本》。
對於密碼解除,筆者嘗試了諸多方案。這裡就不廢話了,直接上乾貨--qpdf。
以上是官網對於QPDF介紹。簡言之,qpdf是一款可以移除密碼的在命令行終端操作的軟體。
qpdf 的優勢:
- 破除 256-bit encryption keys and AES
- 破除 default RC4 with 40, 128 or 256 bit key lengths
命令行語法如下:
其中:
- infilename 是指導入的文件的名字,也就是需要移除密碼的文件
- outfilename:經過處理之後的需要導出的文件的名字,也就是最終你要的文件的名字
- options:需要使用的命令。下圖是可以使用的命令的一小部分,其中黃色標註的的--decrypt 是我們需要的命令。這裡很明顯的提到:如果PDF打開時需要密碼,則需要提供密碼,這裡僅僅移除文檔的許可權加密。
因此,在轉到qpdf的安裝目錄下,你只需要在命令行終端收入一下命令:
qpdf --decrypt infilename outfilename
好了,對於單個文件確實可以這麼處理。那麼對於100個文件該如何處理?只需要在命令行終端輸入100遍即可?嗯,是的,如果你不嫌麻煩。但是,重複的勞動,應該交給機器來完成。實際上,寫個批處理就可以輕鬆搞定這個任務。
if 你會批處理:
-----文章到此結束----
else:
-----請繼續閱讀----
--------------------不會的批處理的朋友請往下看-------------------
好了,當你在使用命令行進行pdf解密時,你實際上在打開一個進程。
接下來要做的事情已經很明了,就是利用python調用命令行終端,建立多進程直接同時對多個文件進行處理。
python調用命令行終端庫主要有os、subprocess
官網推薦,盡量不使用os.system;同時,o.popen也是調用subprocess.Popen,因此,推薦直接使用subprocess.Popen。subprocess.Popen語法如下:
class Popen(object): """ Execute a child program in a new process. For a complete description of the arguments see the Python documentation. Arguments: args: A string, or a sequence of program arguments. bufsize: supplied as the buffering argument to the open() function when creating the stdin/stdout/stderr pipe file objects executable: A replacement program to execute. stdin, stdout and stderr: These specify the executed programs standard input, standard output and standard error file handles, respectively. preexec_fn: (POSIX only) An object to be called in the child process just before the child is executed. close_fds: Controls closing or inheriting of file descriptors. shell: If true, the command will be executed through the shell. cwd: Sets the current directory before the child is executed. env: Defines the environment variables for the new process. universal_newlines: If true, use universal line endings for file objects stdin, stdout and stderr. startupinfo and creationflags (Windows only) restore_signals (POSIX only) start_new_session (POSIX only) pass_fds (POSIX only) encoding and errors: Text mode encoding and error handling to use for file objects stdin, stdout and stderr. Attributes: stdin, stdout, stderr, pid, returncode """ _child_created = False # Set here since __del__ checks it def __init__(self, args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, encoding=None, errors=None): """Create new Popen instance.""" _cleanup() # Held while anything is calling waitpid before returncode has been # updated to prevent clobbering returncode if wait() or poll() are # called from multiple threads at once. After acquiring the lock, # code must re-check self.returncode to see if another thread just # finished a waitpid() call. self._waitpid_lock = threading.Lock() self._input = None self._communication_started = False#------------------------醜陋的界面--------------------
我們分步來分析subprocess.Popen
首先,Popen是一個類
其自帶一個初始化函數
初始化參數裡面有若干個參數,我們逐一展示
重點來看看shell參數。
如果直接通過命令行來執行,那麼shell = True
簡單來說,如果你的程序依賴於其他命令行輸入,那麼使用shell相當危險!由於我們不需要在命令行直接輸入命令,因此這裡並無風險。
為何我們要關注shell這個參數?好吧,如果shell= False,將直接導致程序錯誤,找不到文件([Errno 2] No such file or directory
)!
好了,函數解析完畢,直接上代碼
#批量破解pdf密碼import subprocessimport ospath = r"F:qpdf"pre = r"C:Users ctctctPycharmProjectsyxplfiles" #初始pdf文件夾final = r"D:yxpl" #存放破解的pdf的文件夾files = os.listdir(pre)exit_files = os.listdir(final)for j in files: if j not in exit_files: abs_file = os.path.join(pre,j) out_file = os.path.join(final,j) cmd = ["qpdf","--decrypt",abs_file,out_file] #命令行終端命令 sub = subprocess.Popen(args=cmd,cwd=path,shell=True) #不要忘記cwd sub.wait() #最好加上,否則可能由於多個進程同時執行帶來機器癱瘓
後續:
- 有興趣朋友可以藉助tkinter或者pyqt做一個用戶界面,方便python用戶使用。如果懂點pyinstaller,也可以將程序打包成exe文件,方便完全不懂python的小白使用。
- pdf 文章有了,如何抽取裡面的文字?請看《利用python處理PDF文本》
- 文字也有了, 如何識別出你要的內容或者進行轉化?請學習自然語言處理+正則表達式
- 拓展思路,命令行可以做的事,python可以直接做。再進一步,其他語言做好的事情,python來調用。例如,java庫pdfbpox擅長處理pdf ,那麼就寫個pdfbox python介面,直接調用。
推薦閱讀: