มีโมดูลหลามแปลงไฟล์ PDF เป็นข้อความหรือไม่? ฉันลองใช้รหัสหนึ่งชิ้นที่พบใน Activestate ซึ่งใช้ pypdf แต่ข้อความที่สร้างขึ้นไม่มีช่องว่างระหว่างและไม่มีประโยชน์
มีโมดูลหลามแปลงไฟล์ PDF เป็นข้อความหรือไม่? ฉันลองใช้รหัสหนึ่งชิ้นที่พบใน Activestate ซึ่งใช้ pypdf แต่ข้อความที่สร้างขึ้นไม่มีช่องว่างระหว่างและไม่มีประโยชน์
คำตอบ:
ลองPDFMiner มันสามารถแยกข้อความจากไฟล์ PDF เป็นรูปแบบ HTML, SGML หรือ "Tagged PDF"
รูปแบบ PDF ที่ติดแท็กดูเหมือนจะสะอาดที่สุดและการแยกแท็ก XML ออกจากข้อความเปล่าเท่านั้น
เวอร์ชัน Python 3 มีให้ใน:
PDFMinerแพคเกจมีการเปลี่ยนแปลงตั้งแต่codeapeโพสต์
แก้ไข (อีกครั้ง):
PDFMiner ได้รับการอัปเดตอีกครั้งในเวอร์ชัน 20100213
คุณสามารถตรวจสอบเวอร์ชั่นที่คุณติดตั้งด้วยดังต่อไปนี้:
>>> import pdfminer
>>> pdfminer.__version__
'20100213'
นี่คือเวอร์ชันที่อัปเดต (พร้อมความคิดเห็นเกี่ยวกับสิ่งที่ฉันเปลี่ยน / เพิ่ม):
def pdf_to_csv(filename):
from cStringIO import StringIO #<-- added so you can copy/paste this to try it
from pdfminer.converter import LTTextItem, TextConverter
from pdfminer.pdfparser import PDFDocument, PDFParser
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
class CsvConverter(TextConverter):
def __init__(self, *args, **kwargs):
TextConverter.__init__(self, *args, **kwargs)
def end_page(self, i):
from collections import defaultdict
lines = defaultdict(lambda : {})
for child in self.cur_item.objs:
if isinstance(child, LTTextItem):
(_,_,x,y) = child.bbox #<-- changed
line = lines[int(-y)]
line[x] = child.text.encode(self.codec) #<-- changed
for y in sorted(lines.keys()):
line = lines[y]
self.outfp.write(";".join(line[x] for x in sorted(line.keys())))
self.outfp.write("\n")
# ... the following part of the code is a remix of the
# convert() function in the pdfminer/tools/pdf2text module
rsrc = PDFResourceManager()
outfp = StringIO()
device = CsvConverter(rsrc, outfp, codec="utf-8") #<-- changed
# becuase my test documents are utf-8 (note: utf-8 is the default codec)
doc = PDFDocument()
fp = open(filename, 'rb')
parser = PDFParser(fp) #<-- changed
parser.set_document(doc) #<-- added
doc.set_parser(parser) #<-- added
doc.initialize('')
interpreter = PDFPageInterpreter(rsrc, device)
for i, page in enumerate(doc.get_pages()):
outfp.write("START PAGE %d\n" % i)
interpreter.process_page(page)
outfp.write("END PAGE %d\n" % i)
device.close()
fp.close()
return outfp.getvalue()
แก้ไข (ยังอีกครั้ง):
นี่คือการปรับปรุงสำหรับรุ่นล่าสุดในpypi20100619p1
, ในระยะสั้นฉันแทนที่LTTextItem
ด้วยLTChar
และส่งตัวอย่างของ LAParams ไปยัง CsvConverter Constructor
def pdf_to_csv(filename):
from cStringIO import StringIO
from pdfminer.converter import LTChar, TextConverter #<-- changed
from pdfminer.layout import LAParams
from pdfminer.pdfparser import PDFDocument, PDFParser
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
class CsvConverter(TextConverter):
def __init__(self, *args, **kwargs):
TextConverter.__init__(self, *args, **kwargs)
def end_page(self, i):
from collections import defaultdict
lines = defaultdict(lambda : {})
for child in self.cur_item.objs:
if isinstance(child, LTChar): #<-- changed
(_,_,x,y) = child.bbox
line = lines[int(-y)]
line[x] = child.text.encode(self.codec)
for y in sorted(lines.keys()):
line = lines[y]
self.outfp.write(";".join(line[x] for x in sorted(line.keys())))
self.outfp.write("\n")
# ... the following part of the code is a remix of the
# convert() function in the pdfminer/tools/pdf2text module
rsrc = PDFResourceManager()
outfp = StringIO()
device = CsvConverter(rsrc, outfp, codec="utf-8", laparams=LAParams()) #<-- changed
# becuase my test documents are utf-8 (note: utf-8 is the default codec)
doc = PDFDocument()
fp = open(filename, 'rb')
parser = PDFParser(fp)
parser.set_document(doc)
doc.set_parser(parser)
doc.initialize('')
interpreter = PDFPageInterpreter(rsrc, device)
for i, page in enumerate(doc.get_pages()):
outfp.write("START PAGE %d\n" % i)
if page is not None:
interpreter.process_page(page)
outfp.write("END PAGE %d\n" % i)
device.close()
fp.close()
return outfp.getvalue()
แก้ไข (อีกครั้ง):
อัปเดตสำหรับรุ่น20110515
(ขอบคุณ Oeufcoque Penteano!):
def pdf_to_csv(filename):
from cStringIO import StringIO
from pdfminer.converter import LTChar, TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfparser import PDFDocument, PDFParser
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
class CsvConverter(TextConverter):
def __init__(self, *args, **kwargs):
TextConverter.__init__(self, *args, **kwargs)
def end_page(self, i):
from collections import defaultdict
lines = defaultdict(lambda : {})
for child in self.cur_item._objs: #<-- changed
if isinstance(child, LTChar):
(_,_,x,y) = child.bbox
line = lines[int(-y)]
line[x] = child._text.encode(self.codec) #<-- changed
for y in sorted(lines.keys()):
line = lines[y]
self.outfp.write(";".join(line[x] for x in sorted(line.keys())))
self.outfp.write("\n")
# ... the following part of the code is a remix of the
# convert() function in the pdfminer/tools/pdf2text module
rsrc = PDFResourceManager()
outfp = StringIO()
device = CsvConverter(rsrc, outfp, codec="utf-8", laparams=LAParams())
# becuase my test documents are utf-8 (note: utf-8 is the default codec)
doc = PDFDocument()
fp = open(filename, 'rb')
parser = PDFParser(fp)
parser.set_document(doc)
doc.set_parser(parser)
doc.initialize('')
interpreter = PDFPageInterpreter(rsrc, device)
for i, page in enumerate(doc.get_pages()):
outfp.write("START PAGE %d\n" % i)
if page is not None:
interpreter.process_page(page)
outfp.write("END PAGE %d\n" % i)
device.close()
fp.close()
return outfp.getvalue()
LTTextItem
unixuser.org/~euske/python/pdfminer/index.html#changesLTChar
20110515
ตามความคิดเห็นของคุณ
เนื่องจากไม่มีโซลูชันเหล่านี้รองรับ PDFMiner เวอร์ชันล่าสุดฉันจึงเขียนโซลูชันง่าย ๆ ที่จะส่งคืนข้อความของ PDF โดยใช้ PDFMiner วิธีนี้จะทำงานสำหรับผู้ที่ได้รับข้อผิดพลาดในการนำเข้าด้วยprocess_pdf
import sys
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.converter import XMLConverter, HTMLConverter, TextConverter
from pdfminer.layout import LAParams
from cStringIO import StringIO
def pdfparser(data):
fp = file(data, 'rb')
rsrcmgr = PDFResourceManager()
retstr = StringIO()
codec = 'utf-8'
laparams = LAParams()
device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
# Create a PDF interpreter object.
interpreter = PDFPageInterpreter(rsrcmgr, device)
# Process each page contained in the document.
for page in PDFPage.get_pages(fp):
interpreter.process_page(page)
data = retstr.getvalue()
print data
if __name__ == '__main__':
pdfparser(sys.argv[1])
ดูรหัสด้านล่างที่ใช้งานได้กับ Python 3:
import sys
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.converter import XMLConverter, HTMLConverter, TextConverter
from pdfminer.layout import LAParams
import io
def pdfparser(data):
fp = open(data, 'rb')
rsrcmgr = PDFResourceManager()
retstr = io.StringIO()
codec = 'utf-8'
laparams = LAParams()
device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
# Create a PDF interpreter object.
interpreter = PDFPageInterpreter(rsrcmgr, device)
# Process each page contained in the document.
for page in PDFPage.get_pages(fp):
interpreter.process_page(page)
data = retstr.getvalue()
print(data)
if __name__ == '__main__':
pdfparser(sys.argv[1])
python3
นอกเหนือจากวงเล็บที่ชัดเจนหลังprint
คำสั่งเราจะต้องแทนที่file
คำสั่งด้วยopen
และนำเข้าStringIO
จากแพ็คเกจio
Pdftotextโปรแกรมโอเพนซอร์ซ (ส่วนหนึ่งของ Xpdf) ซึ่งคุณสามารถโทรจากไพ ธ อน (ไม่ใช่สิ่งที่คุณขอ แต่อาจมีประโยชน์) ฉันใช้มันโดยไม่มีปัญหา ฉันคิดว่า google ใช้มันใน google desktop
-layout
ตัวเลือกในการเก็บข้อความในตำแหน่งเดียวกับที่อยู่ในรูปแบบ PDF ตอนนี้ถ้าเพียงฉันสามารถหาวิธีการไพพ์เนื้อหาของ PDF ลงไปได้
pdftotext
ดูเหมือนว่าจะทำงานได้ดี แต่ต้องการอาร์กิวเมนต์ที่สองที่เป็นยัติภังค์หากคุณต้องการดูผลลัพธ์ใน stdout
find . -iname "*.pdf" -exec pdftotext -enc UTF-8 -eol unix -raw {} \;
ตามค่าเริ่มต้นไฟล์ที่สร้างขึ้นจะใช้ชื่อเดิมพร้อม.txt
นามสกุล
pyPDFทำงานได้ดี (สมมติว่าคุณทำงานกับ PDF ที่มีรูปแบบถูกต้อง) หากสิ่งที่คุณต้องการคือข้อความ (มีช่องว่าง) คุณสามารถทำได้:
import pyPdf
pdf = pyPdf.PdfFileReader(open(filename, "rb"))
for page in pdf.pages:
print page.extractText()
คุณสามารถเข้าถึงข้อมูลเมตาข้อมูลภาพและอื่น ๆ ได้อย่างง่ายดาย
ความคิดเห็นในหมายเหตุโค้ด extractText:
ค้นหาคำสั่งการวาดข้อความทั้งหมดตามลำดับที่ให้ไว้ในสตรีมเนื้อหาและแยกข้อความ สิ่งนี้ทำงานได้ดีสำหรับไฟล์ PDF บางไฟล์ แต่ไม่ดีสำหรับไฟล์อื่นขึ้นอยู่กับตัวสร้างที่ใช้ สิ่งนี้จะได้รับการปรับปรุงในอนาคต อย่าพึ่งพาลำดับของข้อความที่ออกมาจากฟังก์ชั่นนี้เนื่องจากมันจะเปลี่ยนไปถ้าฟังก์ชั่นนี้มีความซับซ้อนมากขึ้น
ไม่ว่าจะเป็นปัญหาหรือไม่ขึ้นอยู่กับสิ่งที่คุณกำลังทำกับข้อความ (เช่นถ้าคำสั่งไม่สำคัญก็ดีหรือถ้าเครื่องกำเนิดไฟฟ้าเพิ่มข้อความในสตรีมตามลำดับที่จะแสดงก็ไม่เป็นไร) . ฉันมีรหัสการแยก pyPdf ในการใช้งานทุกวันโดยไม่มีปัญหาใด ๆ
คุณยังสามารถใช้ pdfminer เป็นห้องสมุดได้อย่างง่ายดาย คุณสามารถเข้าถึงรูปแบบเนื้อหาของ pdf และสามารถสร้างการแยกข้อความของคุณเอง ฉันทำสิ่งนี้เพื่อแปลงเนื้อหา pdf เป็นข้อความคั่นด้วยเครื่องหมายอัฒภาคโดยใช้โค้ดด้านล่าง
ฟังก์ชั่นจะเรียงลำดับวัตถุเนื้อหา TextItem ตามพิกัด y และ x ของพวกเขาและแสดงรายการด้วยพิกัด y เดียวกันกับบรรทัดข้อความเดียวโดยแยกวัตถุในบรรทัดเดียวกันด้วย ';' ตัวละคร
การใช้วิธีการนี้ทำให้ฉันสามารถดึงข้อความจาก pdf ที่ไม่มีเครื่องมืออื่นที่สามารถแยกเนื้อหาที่เหมาะสมสำหรับการแยกวิเคราะห์เพิ่มเติมได้ เครื่องมืออื่น ๆ ที่ฉันลองใช้รวมถึง pdftotext, ps2ascii และเครื่องมือออนไลน์ pdftextonline.com
pdfminer เป็นเครื่องมือที่มีค่าสำหรับการคัดลอก PDF
def pdf_to_csv(filename):
from pdflib.page import TextItem, TextConverter
from pdflib.pdfparser import PDFDocument, PDFParser
from pdflib.pdfinterp import PDFResourceManager, PDFPageInterpreter
class CsvConverter(TextConverter):
def __init__(self, *args, **kwargs):
TextConverter.__init__(self, *args, **kwargs)
def end_page(self, i):
from collections import defaultdict
lines = defaultdict(lambda : {})
for child in self.cur_item.objs:
if isinstance(child, TextItem):
(_,_,x,y) = child.bbox
line = lines[int(-y)]
line[x] = child.text
for y in sorted(lines.keys()):
line = lines[y]
self.outfp.write(";".join(line[x] for x in sorted(line.keys())))
self.outfp.write("\n")
# ... the following part of the code is a remix of the
# convert() function in the pdfminer/tools/pdf2text module
rsrc = PDFResourceManager()
outfp = StringIO()
device = CsvConverter(rsrc, outfp, "ascii")
doc = PDFDocument()
fp = open(filename, 'rb')
parser = PDFParser(doc, fp)
doc.initialize('')
interpreter = PDFPageInterpreter(rsrc, device)
for i, page in enumerate(doc.get_pages()):
outfp.write("START PAGE %d\n" % i)
interpreter.process_page(page)
outfp.write("END PAGE %d\n" % i)
device.close()
fp.close()
return outfp.getvalue()
อัปเดต :
รหัสด้านบนเขียนขึ้นจาก API เวอร์ชันเก่าดูความคิดเห็นของฉันด้านล่าง
pdfminer
ไม่ใช่pdflib
) ฉันขอแนะนำให้คุณดูที่แหล่งที่มาของแหล่งpdf2txt.py
PDFminer โค้ดข้างต้นได้รับแรงบันดาลใจจากไฟล์เวอร์ชันเก่า
slate
เป็นโครงการที่ทำให้การใช้ PDFMiner นั้นง่ายมากจากไลบรารี:
>>> with open('example.pdf') as f:
... doc = slate.PDF(f)
...
>>> doc
[..., ..., ...]
>>> doc[1]
'Text from page 2...'
ฉันต้องการแปลง PDF เฉพาะเป็นข้อความธรรมดาภายในโมดูลหลาม ฉันใช้PDFMiner 20110515 หลังจากอ่านเครื่องมือpdf2txt.pyของพวกเขาฉันเขียนตัวอย่างง่ายๆนี้:
from cStringIO import StringIO
from pdfminer.pdfinterp import PDFResourceManager, process_pdf
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
def to_txt(pdf_path):
input_ = file(pdf_path, 'rb')
output = StringIO()
manager = PDFResourceManager()
converter = TextConverter(manager, output, laparams=LAParams())
process_pdf(manager, converter, input_)
return output.getvalue()
C:\Python27\Scripts\pdfminer\tools\pdf2txt.py
การนำเสนอโค้ด pdf2txt.py อีกครั้งที่มาพร้อมกับ pdfminer คุณสามารถสร้างฟังก์ชั่นที่จะพาไปยังไฟล์ pdf; เป็นตัวเลือก outtype (txt | html | xml | tag) และ opts เช่น commandline pdf2txt {'-o': '/path/to/outfile.txt' ... } โดยค่าเริ่มต้นคุณสามารถโทร:
convert_pdf(path)
ไฟล์ข้อความจะถูกสร้างขึ้นซึ่งเป็นพี่น้องในระบบไฟล์กับไฟล์ PDF ต้นฉบับ
def convert_pdf(path, outtype='txt', opts={}):
import sys
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter, process_pdf
from pdfminer.converter import XMLConverter, HTMLConverter, TextConverter, TagExtractor
from pdfminer.layout import LAParams
from pdfminer.pdfparser import PDFDocument, PDFParser
from pdfminer.pdfdevice import PDFDevice
from pdfminer.cmapdb import CMapDB
outfile = path[:-3] + outtype
outdir = '/'.join(path.split('/')[:-1])
debug = 0
# input option
password = ''
pagenos = set()
maxpages = 0
# output option
codec = 'utf-8'
pageno = 1
scale = 1
showpageno = True
laparams = LAParams()
for (k, v) in opts:
if k == '-d': debug += 1
elif k == '-p': pagenos.update( int(x)-1 for x in v.split(',') )
elif k == '-m': maxpages = int(v)
elif k == '-P': password = v
elif k == '-o': outfile = v
elif k == '-n': laparams = None
elif k == '-A': laparams.all_texts = True
elif k == '-D': laparams.writing_mode = v
elif k == '-M': laparams.char_margin = float(v)
elif k == '-L': laparams.line_margin = float(v)
elif k == '-W': laparams.word_margin = float(v)
elif k == '-O': outdir = v
elif k == '-t': outtype = v
elif k == '-c': codec = v
elif k == '-s': scale = float(v)
#
CMapDB.debug = debug
PDFResourceManager.debug = debug
PDFDocument.debug = debug
PDFParser.debug = debug
PDFPageInterpreter.debug = debug
PDFDevice.debug = debug
#
rsrcmgr = PDFResourceManager()
if not outtype:
outtype = 'txt'
if outfile:
if outfile.endswith('.htm') or outfile.endswith('.html'):
outtype = 'html'
elif outfile.endswith('.xml'):
outtype = 'xml'
elif outfile.endswith('.tag'):
outtype = 'tag'
if outfile:
outfp = file(outfile, 'w')
else:
outfp = sys.stdout
if outtype == 'txt':
device = TextConverter(rsrcmgr, outfp, codec=codec, laparams=laparams)
elif outtype == 'xml':
device = XMLConverter(rsrcmgr, outfp, codec=codec, laparams=laparams, outdir=outdir)
elif outtype == 'html':
device = HTMLConverter(rsrcmgr, outfp, codec=codec, scale=scale, laparams=laparams, outdir=outdir)
elif outtype == 'tag':
device = TagExtractor(rsrcmgr, outfp, codec=codec)
else:
return usage()
fp = file(path, 'rb')
process_pdf(rsrcmgr, device, fp, pagenos, maxpages=maxpages, password=password)
fp.close()
device.close()
outfp.close()
return
PDF อาจให้ฉันหนึ่งบรรทัด [หน้า 1 จาก 7 ... ] ในทุกหน้าของไฟล์ PDF ที่ฉันลองด้วย
คำตอบที่ดีที่สุดที่ฉันมีคือ pdftoipe หรือรหัส c ++ ตาม Xpdf
ดูคำถามของฉันสำหรับสิ่งที่ผลลัพธ์ของ pdftoipe ดูเหมือน
นอกจากนี้ยังมีPDFTextStreamซึ่งเป็นห้องสมุด Java เชิงพาณิชย์ที่สามารถใช้งานได้จาก Python
ฉันได้ใช้pdftohtml
กับ-xml
อาร์กิวเมนต์อ่านผลด้วยsubprocess.Popen()
ที่จะทำให้คุณ x COORD, COORD Y, ความกว้างความสูงและตัวอักษรของทุกข้อมูลโค้ดของข้อความในรูปแบบไฟล์ PDF ฉันคิดว่านี่เป็นสิ่งที่ 'evince' อาจใช้เช่นกันเนื่องจากข้อความแสดงข้อผิดพลาดเดียวกันพ่นออกมา
หากคุณต้องการประมวลผลข้อมูลเรียงเป็นแนวมันจะซับซ้อนกว่าเล็กน้อยเนื่องจากคุณต้องคิดค้นอัลกอริทึมที่เหมาะกับไฟล์ pdf ของคุณ ปัญหาคือโปรแกรมที่สร้างไฟล์ PDF นั้นไม่จำเป็นต้องจัดวางข้อความในรูปแบบโลจิคัลใด ๆ คุณสามารถลองใช้อัลกอริธึมการเรียงลำดับแบบง่าย ๆ ได้และบางครั้งก็ใช้งานได้ แต่อาจมี 'พลัดหลง' และ 'หลงทาง' ชิ้นส่วนของข้อความที่ไม่เรียงตามลำดับที่คุณคิด ดังนั้นคุณต้องมีความคิดสร้างสรรค์
ฉันใช้เวลาประมาณ 5 ชั่วโมงกว่าจะคิดออกหนึ่งไฟล์สำหรับไฟล์ pdf ที่ฉันใช้งานอยู่ แต่ตอนนี้มันใช้งานได้ดีมาก โชคดี.
พบว่าทางออกในวันนี้ ใช้งานได้ดีสำหรับฉัน แม้แต่การแสดงผลหน้า PDF เป็นภาพ PNG http://www.swftools.org/gfx_tutorial.html