You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python PDF爬取:所有者姓名及地址相关字段提取错误/缺失问题求助

Python PDF爬取:所有者姓名及地址相关字段提取错误/缺失问题求助

大家好,我在使用Python处理PDF数据爬取时遇到了棘手的问题,想请教社区的朋友们帮忙排查:

问题描述

我用pdfplumber库抓取PDF里的税务数据,控制台没有任何报错,但生成的CSV文件里,Owner's First Name到Zip Code这6个字段要么填充了错误内容,要么完全为空。这些数据本该从PDF左侧、TAX MAP PARCEL NUMBER下方的所有者信息区域提取,其他字段(比如地块ID、地址、面积、估值等)的提取都是正常的。

当前结果示例

我手动补充了部分缺失数据的CSV截图如下,方便大家理解我需要的正确结果样式:
手动补充后的CSV截图

目标PDF文件

我处理的是哥伦比亚县2022年Ancram的暂定税务记录PDF,文件地址:

https://www.columbiacountyny.com/uploads/1/0/6/8/106827239/2022_ancram_tentative_roll.pdf

我当前使用的代码

import re
import csv
import pdfplumber

# Define the file path
root_path = "C:\\Users\\jfdal\\OneDrive\\Desktop\\2022"
file_name = "austerlitz_2022_fr.pdf"
file_path = f"{root_path}\\{file_name}"

# Retain your property address pattern
property_address_pattern = r"\n([A-Za-z0-9\s]+(?:\d{1,4}\s[A-Za-z0-9\s]+)?)\n"
acreage_pattern = r"ACRES\s+(\d{1,3}\.\d{1,2})"
value_pattern = r"FULL MARKET VALUE\s+([\d,]+)"
tax_pattern = r"COUNTY TAXABLE VALUE\s+([\d,]+)"
ag_tax_pattern = r"AG\s+DISTRIC\s+41720\s+([\d,]+)"
forest_tax_pattern = r"FOREST\s+LND\s+47460\s+([\d,]+)"
solar_tax_pattern = r"RPTL 487\s+([\d,]+)"

# List to store extracted data
results = []

# Read the entire PDF
with pdfplumber.open(file_path) as pdf:
    content = "".join([page.extract_text() for page in pdf.pages])  # Process all pages

# Remove page headers before splitting into property sections
content = re.sub(r"TAX MAP PARCEL NUMBER PROPERTY LOCATION & CLASS.*?ACCOUNT NO\..*?\n", "", content, flags=re.S)

# Split content by property sections
properties = re.split(r"\*{50,}", content)

for prop in properties:
    try:
        lines = [line.strip() for line in prop.split("\n") if line.strip()]

        # Initialize placeholders
        prop_id = "0"
        owner_first_name, owner_last_name = "0", "0"
        street_address, owner_town, owner_state, zip_code = "0", "0", "0", "0"

        # First line for Prop ID and Address
        if len(lines) > 0:
            first_line = lines[0]
            # Extract Prop ID from the first line if structured like "123.-1-45"
            prop_id_match = re.match(r"(\d{1,3}\.\-?\d{1,3}\-?\d{1,3}\.?\d{0,3})", first_line)
            prop_id = prop_id_match.group(1) if prop_id_match else "0"

        # Owner's information parsing logic
        for line in lines:
            if len(line.split()) > 3:  # Likely contains owner or address information
                if "CURRENT OWNERS NAME" in line:
                    owner_name_parts = line.replace("CURRENT OWNERS NAME", "").strip().split()
                    owner_first_name = owner_name_parts[0] if len(owner_name_parts) > 0 else "0"
                    owner_last_name = owner_name_parts[-1] if len(owner_name_parts) > 1 else "0"
                elif "CURRENT OWNERS ADDRESS" in line:
                    address_parts = line.replace("CURRENT OWNERS ADDRESS", "").strip().split(", ")
                    if len(address_parts) == 3:
                        street_address = address_parts[0]
                        owner_town, owner_state, zip_code = address_parts[1], address_parts[2].split()[0], address_parts[2].split()[1]

        # Extract data using regex patterns
        property_address_match = re.search(property_address_pattern, prop)
        acreage_match = re.search(acreage_pattern, prop)
        full_market_value_match = re.search(value_pattern, prop)
        tax_value_match = re.search(tax_pattern, prop)
        ag_tax_match = re.search(ag_tax_pattern, prop)
        forest_tax_match = re.search(forest_tax_pattern, prop)
        solar_tax_match = re.search(solar_tax_pattern, prop)

        # Handle numeric fields with commas
        def parse_numeric(value_match):
            return value_match.group(1).replace(",", "") if value_match else "0"

        # Get acreage and filter properties with less than 100 acres
        acreage = float(acreage_match.group(1)) if acreage_match else 0
        if acreage < 100:
            continue

        # Append extracted results
        results.append({
            "Property Town": "Austerlitz",
            "Prop ID": prop_id,
            "Property Address": property_address_match.group(1).strip() if property_address_match else "0",
            "Acreage": f"{acreage:.2f}",
            "Owner First Name": owner_first_name,
            "Owner Last Name": owner_last_name,
            "Street Address": street_address,
            "Town": owner_town,
            "State": owner_state,
            "Zip Code": zip_code,
            "Full Market Value": parse_numeric(full_market_value_match),
            "Tax Value": parse_numeric(tax_value_match),
            "AG Tax": parse_numeric(ag_tax_match),
            "Forest Tax": parse_numeric(forest_tax_match),
            "Solar Tax": parse_numeric(solar_tax_match)
        })
    except Exception as e:
        # Handle parsing errors
        print(f"Error parsing property: {e}")

# Write results to CSV (only properties with 100+ acres)
output_file = f"{root_path}\\austerlitz_results.csv"
with open(output_file, 'w', newline='') as csvfile:
    fieldnames = [
        "Property Town", "Prop ID", "Property Address", "Acreage", "Owner First Name", "Owner Last Name", 
        "Street Address", "Town", "State", "Zip Code", "Full Market Value", "Tax Value", "AG Tax", "Forest Tax", "Solar Tax"
    ]
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(results)

print(f"Data extraction complete. Results saved to {output_file}")

预期结果

我期望代码能生成和我手动填充类似的结果,把CSV中那些填充为0的所有者姓名、街道地址、城镇、州、邮编字段,替换为PDF中对应的正确数据。

备注:内容来源于stack exchange,提问作者user29394340

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 03:35:32