提问人:Pablo Martín Calvo 提问时间:5/26/2023 最后编辑:jquriousPablo Martín Calvo 更新时间:6/30/2023 访问量:39
如何避免Python PDF解析代码中因表结构不匹配而出现重复?
How to avoid duplication in Python PDF parsing code for mismatching table structures?
问:
我有 100 多个 PDF 是匹配报告,我想从中抓取数据,以便将其存储在数据帧中,以便以后可以使用它。 问题是:这些 PDF 并不总是具有相同的结构,并且从 pdfplumber 读取的表格中的行长度不同,因此几乎不可能不为每种类型的行重复多次代码。 我想找到一种方法来使我的代码更漂亮、更易于阅读和调试。
这是我正在为每个 PDF 阅读的表格:其中一个 PDF 的摘录
我需要从每个 PDF 的两列中获取数据。
这是我从 PDF 中提取表格的代码
directory = os.fsencode('')
matchs_raw = {}
for file in os.listdir(directory):
filename = os.fsdecode(file)
if '.pdf' not in filename:
continue
matchs_raw[filename] = []
with pdfplumber.open(f'\\{filename}') as pdf:
for page in pdf.pages:
tables = page.extract_tables()
for table in tables:
for i in table:
matchs_raw[filename].append(i)
它正确地存储了我想要的所有数据,每个 PDF 有一个键,所有表中的每一行都是列表的一个元素,这是键的值,matchs_raw。
然后,我尝试从matchs_raw中提取相关数据,将其存储在 pandas DataFrame 中。 我设法用如下所示的代码来做到这一点:
columns = ['file_name','minute','role','numero','nom','recevant_ou_visiteur','equipe',
'description','score_1_ponctuel','score_2_ponctuel']
data = {col: [] for col in columns}
for match in tqdm(matchs_raw):
if matchs_raw[match][0][0]== "Organisateur":
for l in range(len(matchs_raw[match])):
if matchs_raw[match][l][0]=="Déroulé du Match" or matchs_raw[match][l][0]=="DérouléduMatch":
starting_row = l+3
break
for j in matchs_raw[match][starting_row:]:
if len(j)!=13:
## LEFT COLUMN
data['file_name'].append(matchs_raw[match][0][20])
try:
data['minute'].append(j[0])
except:
data['minute'].append(np.nan)
try:
data['role'].append(re.findall("JR|JV|OR|OV",j[3])[0][0])
except:
data['role'].append(np.nan)
try:
if re.findall("JR|JV|OR|OV",j[3])[0][0] == "J":
try:
data['numero'].append(re.findall("N[^\x00-\x7F]+\d*",j[3])[0])
except:
data['numero'].append(np.nan)
try:
data['nom'].append(re.findall("N[^\x00-\x7F]+\d*(\D+)",j[3])[0])
except:
data['nom'].append(np.nan)
try:
data['description'].append(re.findall("(.+?)(JR|JV|OR|OV)N[^\x00-\x7F]",j[3])[0][0])
except:
data['description'].append(np.nan)
else:
try:
data['numero'].append("Officiel")
except:
data['numero'].append(np.nan)
try:
data['nom'].append(re.findall("^(.+?)(OV|OR)(.+)",j[3])[0][2].strip())
except:
data['nom'].append(np.nan)
try:
data['description'].append(re.findall("^(.+?)(OV|OR)",j[3])[0][0].strip())
except:
data['description'].append(np.nan)
对于这种特殊情况,它继续一点点(type==“Organisateur”, len(j)!=13, left column)。
我必须对左列和右列的 len(j)==13 以及另一种具有 3 个不同 len 大小写的 pdf 做同样的事情。
for 循环中 j 的索引相互尊重没有任何意义(例如,和 之间并不总是有 3 个级别的差异。data['minute'].append(j[0])
data['role'].append(re.findall("JR|JV|OR|OV",j[3])[0][0])
对于如何避免对每种情况重复所有这些 try/except 块,您有什么建议吗?我们将不胜感激。
谢谢!
答:
您似乎正在解析这些 PDF 文件: https://www.ffhandball.fr/api/s3/fdm/O/A/C/P/OACPSGG.pdf
似乎您可以使用标题来标识列:
temps = page.search(r'Temps (?=Score Action)')
vlines = sorted(set(t['x0'] for t in temps)) + [ page.bbox[-2] ]
im = page.to_image(300)
im.reset().draw_vlines(vlines, stroke_width=10, stroke='black')
im.save('tbl.png')
然后,您可以输出每列:.crop()
您可以在每列中标题的第一个实例 (Temps, Score, Action
)
for left, right in itertools.pairwise(vlines):
crop = page.crop((left, 0, right, page.bbox[-1]))
left, top, right, bottom = crop.bbox
top = crop.search('Temps Score Action')[0]['top']
crop = crop.crop((left, top, right, bottom))
print(pl.DataFrame(crop.extract_table(), orient='row'))
shape: (64, 4)
┌──────────┬──────────┬──────────┬───────────────────────────────────┐
│ column_0 ┆ column_1 ┆ column_2 ┆ column_3 │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ str │
╞══════════╪══════════╪══════════╪═══════════════════════════════════╡
│ Temps ┆ Score ┆ Action ┆ null │
│ 00:57 ┆ 01 - 00 ┆ ┆ But JR N°44 DEMBELE sitha lauree… │
│ 02:24 ┆ 01 - 00 ┆ ┆ Tir JR N°28 BALLUREAU lea │
│ 02:29 ┆ 01 - 00 ┆ ┆ Arrêt JV N°12 SCHAMBACHER laura │
│ … ┆ … ┆ … ┆ … │
│ 25:23 ┆ 16 - 07 ┆ ┆ Arrêt JR N°16 PORTES laura │
│ 25:32 ┆ 16 - 07 ┆ ┆ Tir JR N°15 AUGUSTINE anne-emman… │
│ 25:35 ┆ 16 - 07 ┆ ┆ 2MN JR N°2 JACQUES emma │
│ ┆ null ┆ null ┆ null │
└──────────┴──────────┴──────────┴───────────────────────────────────┘
shape: (67, 4)
┌──────────┬──────────┬──────────┬───────────────────────────────────┐
│ column_0 ┆ column_1 ┆ column_2 ┆ column_3 │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ str │
╞══════════╪══════════╪══════════╪═══════════════════════════════════╡
│ Temps ┆ Score ┆ Action ┆ null │
│ 25:50 ┆ 16 - 08 ┆ ┆ But JV N°10 SAID AHMED anais │
│ 26:28 ┆ 16 - 08 ┆ ┆ Tir JR N°47 DEMBELE mahoua-audre… │
│ 26:30 ┆ 16 - 08 ┆ ┆ Arrêt JV N°61 NAILI yousra │
│ … ┆ … ┆ … ┆ … │
│ 49:05 ┆ 28 - 17 ┆ ┆ Tir JV N°11 BROUTIN auriane │
│ 49:09 ┆ 28 - 17 ┆ ┆ Arrêt JR N°16 PORTES laura │
│ 49:41 ┆ 28 - 17 ┆ ┆ Tir JR N°70 LE BLEVEC julie │
│ 51:00 ┆ 28 - 18 ┆ ┆ But JV N°9 DIEYE eloise │
└──────────┴──────────┴──────────┴───────────────────────────────────┘
您可以进一步优化它,但这应该允许您将所有列堆叠在一起以简化分析。
评论