wangeditor富文本 提取出来的html table的宽度是0_手把手教学:提取PDF各种表格文本数据...

本文详述如何使用PDFPlumber库提取PDF中的表格数据,并通过实例展示了其可视化调试工具的使用,包括调整参数以准确识别表格边界,以及如何清洗和展示提取的数据。同时提及适用于不同Python版本的安装和依赖项设置。
摘要由CSDN通过智能技术生成

29f78a1ea37bbf61d533f740db75e7f1.png

价值前瞻 点击右侧关注,让你成为一个有远见的人! 18b6b907990358afd2f81457a7fdd34c.png

db842593c434beba47fbea37334f6db4.png

Python数据之道 点击右侧关注,开启数据之道! 18b6b907990358afd2f81457a7fdd34c.png 作者:QIML编辑部 来源:量化投资与机器学习 文章经授权转载

手把手教学:

提取PDF各种表格文本数据

还在为抓取各种PDF格式的财务、数据报表而烦恼吗? 还在为自己手工操作导致的效率低下而烦恼吗? 还在担心没有趁手的兵器吗?

今天,公众号为大家介绍一款神器:

PDFPlumbe

轻松玩转PDF,痛快抓数据!助你一臂之力!

eea890b656fbd03ede7da160f102585c.png

关于PDFPlumbe

PDFPlumb最适合提取电脑生成的PDF,而不是扫描的PDF。 它是在pdfminer和pdfmine.six基础上设计的。

适用版本: Python2.7、3.1、3.4、3.5和3.6。

安装PDFPlumbe
pip install pdfplumber

要使用pdfplumber的可视化调试工具,还需要在计算机上安装ImageMagick(https://imagemagick.org/index.php),说明如下:

8276e89f49c63f4a7bd6bb3b0817d880.png

http://docs.wand-py.org/en/latest/guide/install.html#install-imagemagick-debian

具体参数、提取流程与可视化我们将以案例进行展示,更详细的内容,请大家在文末下载安装包自行查看。

案例一
import pdfplumber

pdf = pdfplumber.open("../pdfs/ca-warn-report.pdf")
p0 = pdf.pages[0]
im = p0.to_image()
im

c4fb802de8d4dba90aa5a1e41b1dc5bb.png

使用 .extract_table 获取数据:

table = p0.extract_table()
table[:3]
f50a9aa4b5192108bee1cf2c9551f971.png

使用pandas将列表呈现为一个DataFrame,并在某些日期内删除多余的空格。

import pandas as pd
df = pd.DataFrame(table[1:], columns=table[0])
for column in ["Effective", "Received"]:
    df[column] = df[column].str.replace(" ", "")

bd41b562afcf75c7d5e73faf2b5466b7.png

大功告成!

具体是如何产生的呢?

红线代表pdfplumber在页面上找到的线,蓝色圆圈表示这些线的交叉点,淡蓝色底纹表示从这些交叉点派生的单元格。

9dff6ac69f0e842d4bcde3f1f79fd192.png

案例二:从PDF中提取图形数据
import pdfplumber
report = pdfplumber.open("../pdfs/ag-energy-round-up-2017-02-24.pdf").pages[0]
im = report.to_image()
im

1db3f8c8d235746fd077275a1a37ea01.png

页面对象具有 .curves 属性,该属性包含在页面上找到的一个curve对象列表。本报告包含12条曲线,每图4条:

len(report.curves)
12
report.curves[0]
d5d225fe52262b5f1e35f36db5183b5b.png

将它们传递 .draw_lines 确定曲线的位置:

im.draw_lines(report.curves, stroke="red", stroke_width=2)

7f9366664f4243848860f024f6ae5f39.png

我们通过循环使用四种颜色的调色板来获得更好的显示感:

im.reset()
colors = [ "gray", "red", "blue", "green" ]
for i, curve in enumerate(report.curves):
    stroke = colors[i%len(colors)]
    im.draw_circles(curve["points"], radius=3, stroke=stroke, fill="white")
    im.draw_line(curve["points"], stroke=stroke, stroke_width=2)
im

83824c9d9f3631f80c04f86e1149ec52.png

案例三
import pdfplumber

pdf = pdfplumber.open("../pdfs/background-checks.pd")
p0 = pdf.pages[0]
im = p0.to_image()
im

ca8a5838ac59545d5b7ecdd47027ac16.png

使用 PageImage.debug_tablefinder() 来检查表格:

im.reset().debug_tablefinder()

2c629efa282d834132486e868f63149e.png

默认设置正确地标识了表的垂直边界,但是没有捕获每组5个states/territories之间的水平边界。所以:

使用自定义 .extract_table :

  • 因为列由行分隔,所以我们使用 vertical_strategy="lines"

  • 因为行主要由文本之间的沟槽分隔,所以我们使用 horizontal_strategy="text"

  • 由于文本的左、右端与竖线不是很齐平,所以我们使用 intersection_tolerance: 15

table_settings = {
    "vertical_strategy": "lines",
    "horizontal_strategy": "text",
    "intersection_x_tolerance": 15
}

im.reset().debug_tablefinder(table_settings)

78abe8951b8076a6987bbe418de216d2.png

table = p0.extract_table(table_settings)

for row in table[:5]:
    print(row)

885dd21018d4d6e2477a37d4537973c8.png

清理数据(页眉页脚等):

core_table = table[3:3+56]
" • ".join(core_table[0])

294d9fa6265e5b681bd44746877d2809.png

" • ".join(core_table[-1])

ae6fbba8dd489e22dca6a59dd1389322.png

COLUMNS = [
    "state",
    "permit",
    "handgun",
    "long_gun",
    "other",
    "multiple",
    "admin",
    "prepawn_handgun",
    "prepawn_long_gun",
    "prepawn_other",
    "redemption_handgun",
    "redemption_long_gun",
    "redemption_other",
    "returned_handgun",
    "returned_long_gun",
    "returned_other",
    "rentals_handgun",
    "rentals_long_gun",
    "private_sale_handgun",
    "private_sale_long_gun",
    "private_sale_other",
    "return_to_seller_handgun",
    "return_to_seller_long_gun",
    "return_to_seller_other",
    "totals"
]
def parse_value(i, x):
    if i == 0: return x
    if x == "": return None
    return int(x.replace(",", ""))

from collections import OrderedDict
def parse_row(row):
    return OrderedDict((COLUMNS[i], parse_value(i, cell))
        for i, cell in enumerate(row))

data = [ parse_row(row) for row in core_table ]
Now here's the first row, parsed:
data[0]

832371fe4e5cf23b3c3d8b23b2ca6623.png

案例四
import pdfplumber
import re
from collections import OrderedDict

pdf = pdfplumber.open("../pdfs/san-jose-pd-firearm-sample.pdf")
p0 = pdf.pages[0]
im = p0.to_image()
im

1015092d26e6eff186c504972b075e8c.png

我们在pdfplumber检测到的每个 char 对象周围绘制矩形。通过这样做,我们可以看到报表主体的的每一行都有相同的宽度,并且每个字段都填充了空格(“”)字符。这意味着我们可以像解析标准的固定宽度数据文件一样解析这些行。

im.reset().draw_rects(p0.chars)

b5fe0af58b4bb607d3047cbabc0a826d.png

使用 page .extract_text(…) 方法,逐行抓取页面上的每个字符(文本):

text = p0.extract_text()
print(text)

5b16ae26f466318749b1b3c2e68335d1.png

清理数据(页眉页脚等):

core_pat = re.compile(r"LOCATION[\-\s]+(.*)\n\s+Flags = e", re.DOTALL)
core = re.search(core_pat, text).group(1)
print(core)

9738d7f7374a2154d3291563400dbfbb.png

在这份报告中,每f一个irearm占了两行。下面的代码将表拆分为two-line,然后根据每个字段中的字符数解析出字段:

lines = core.split("\n")
line_groups = list(zip(lines[::2], lines[1::2]))
print(line_groups[0])

263ec2d0f16b6306f55ebb3b6dd70c58.png

def parse_row(first_line, second_line):
    return OrderedDict([
        ("type", first_line[:20].strip()),
        ("item", first_line[21:41].strip()),
        ("make", first_line[44:89].strip()),
        ("model", first_line[90:105].strip()),
        ("calibre", first_line[106:111].strip()),
        ("status", first_line[112:120].strip()),
        ("flags", first_line[124:129].strip()),
        ("serial_number", second_line[0:13].strip()),
        ("report_tag_number", second_line[21:41].strip()),
        ("case_file_number", second_line[44:64].strip()),
        ("storage_location", second_line[68:91].strip())
    ])

parsed = [ parse_row(first_line, second_line)
    for first_line, second_line in line_groups ]
parsed[:2]
fd0048fc7f015c1565f41befaed1588e.png

通过DataFrame进行展示:

mport pandas as pd
columns = list(parsed[0].keys())
pd.DataFrame(parsed)[columns]

e8d23f64a7614e3ac2137b03756f9eb4.png

-------------------End-------------------

c41cb7c37e5520c37686ccf6e633b969.png

 公众号后台回复「微信群」,将邀请加入读者交流群。

753fd95f395bec52f00b428a2e066e36.png

  • 推荐 | 免费获取《Python知识手册》

  • Matplotlib最有价值的50个图表

2ff9a75506958f1c5ab366568fbea438.png

分享 ”和“ 在看 ”是更好的支持!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值