在数据分析中,我们经常会遇到非结构化或半结构化数据,比如 JSON、XML 和 HTML 格式的文件。这些格式在网页数据、配置文件、跨系统交互中十分常见,但解析起来往往比 CSV/Excel 更复杂。今天我们就来聊聊 pandas 如何优雅地处理这些复杂格式,结合具体场景和代码示例,看看如何高效提取所需数据。

一、JSON 数据处理:灵活应对多种结构

JSON 是最常见的半结构化数据格式之一,pandas 的read_jsonto_json提供了丰富的参数来应对不同场景。

1. 不同 orient 模式的选择

orient参数决定了 JSON 的解析 / 生成方式,常见取值包括:

  • records:默认模式,适合列表型数据(每行一个对象)

    python

    data = '[{"name":"Alice","age":28},{"name":"Bob","age":35}]'
    df = pd.read_json(data, orient='records')
    # 输出:
    #   name  age
    # 0 Alice   28
    # 1   Bob   35
    
  • index:以索引为键的嵌套结构

    python

    data = '{"index":{"0":{"name":"Alice","age":28},"1":{"name":"Bob","age":35}}'
    df = pd.read_json(data, orient='index')
    
  • table:符合 JSON Table Schema 标准,保留元数据(如数据类型)

    python

    df.to_json('table.json', orient='table')  # 生成包含schema的JSON
    

2. 嵌套结构扁平化

遇到多层嵌套的 JSON(如 API 返回的复杂数据),可使用json_normalize

python

from pandas.io.json import json_normalize

nested_data = [
    {
        "user": {"id": 1, "name": "Alice"},
        "orders": [{"product": "book", "price": 99.9}, {"product": "pen", "price": 9.9}]
    }
]
df = json_normalize(nested_data, 
                    record_path='orders',  # 指定子数组路径
                    meta=['user.id', 'user.name'])  # 保留外层字段
# 输出:
#    product  price  user.id  user.name
# 0    book   99.9        1     Alice
# 1     pen    9.9        1     Alice

3. 线性格式(Lines JSON)

处理大文件时,使用lines=True逐行解析:

python

# 写入线性格式
df.to_json('large_data.json', orient='records', lines=True)

# 读取
with open('large_data.json', 'r') as f:
    for chunk in pd.read_json(f, lines=True, chunksize=1000):
        process(chunk)  # 分块处理

二、XML 解析:XPath 与命名空间的博弈

XML 常用于配置文件和结构化数据交换,pandas 的read_xml依赖 lxml 解析器,支持 XPath 查询和命名空间处理。

1. 基础解析与 XPath 过滤

python

xml = '''
<books>
  <book category="tech">
    <title lang="en">Python Cookbook</title>
    <price>69.9</price>
  </book>
  <book category="novel">
    <title lang="zh">三体</title>
    <price>59.9</price>
  </book>
</books>
'''
# 提取所有book节点
df = pd.read_xml(xml, xpath='//book')
# 过滤category为tech的书籍
tech_books = pd.read_xml(xml, xpath='//book[@category="tech"]')

2. 命名空间处理

当 XML 包含命名空间时(如<ns:book>),需显式声明:

python

xml_namespaced = '''
<ns:data xmlns:ns="http://example.com">
  <ns:item>value1</ns:item>
  <ns:item>value2</ns:item>
</ns:data>
'''
# 声明命名空间映射
df = pd.read_xml(xml_namespaced, 
                xpath='//ns:item', 
                namespaces={'ns': 'http://example.com'})

3. 大文件处理:iterparse

对于 GB 级 XML 文件,使用iterparse逐节点读取(需指定物理文件路径):

python

df = pd.read_xml('large_file.xml', 
                iterparse={'row': ['title', 'price']})  # 指定目标节点

三、HTML 表格提取:解析器选择与属性过滤

网页中的表格数据常以 HTML 形式存在,read_html支持自动解析表格,但需注意解析器差异。

1. 解析器对比与选择

  • lxml:速度快,但要求 HTML 结构严格
  • html5lib:容错性强,处理不规范 HTML
  • bs4:需安装 BeautifulSoup,灵活性高

python

# 显式指定解析器
df_list = pd.read_html('https://example.com/table.html', flavor='html5lib')

2. 通过属性过滤表格

利用attrs参数定位特定表格(如带有 id 或 class 的表格):

python

# 提取id为'table-report'的表格
df = pd.read_html('网页HTML', attrs={'id': 'table-report'})[0]

# 提取class包含'hover'的表格
hover_tables = pd.read_html('网页HTML', attrs={'class': 'hover'})

3. 处理复杂表头与链接

python

html = '''
<table>
  <tr>
    <th>名称</th>
    <th>链接</th>
  </tr>
  <tr>
    <td>熊猫文档</td>
    <td><a href="https://pandas.pydata.org">官网</a></td>
  </tr>
</table>
'''
# 提取链接(设置extract_links='all')
df = pd.read_html(html, extract_links='all')[0]
# 输出:
#     名称              链接
# 0  熊猫文档  (官网, https://pandas.pydata.org)

四、实战避坑指南

  1. JSON 类型推断陷阱

    • 对象数组(orient='records')会自动推断类型,嵌套字典可能转为object类型,建议用dtype指定

    python

    pd.read_json(data, dtype={'age': 'int32'})  # 强制指定列类型
    

  2. XML 命名空间坑

    • 未声明命名空间时,XPath 可能无法匹配节点,需始终检查命名空间声明
  3. HTML 解析器兼容性

    • 复杂网页建议优先用html5lib,虽然慢但稳定性高

希望本文能帮助你在处理复杂格式数据时少走弯路!实际项目中,建议根据数据特点选择最合适的解析方式:JSON 用orient匹配结构,XML 用 XPath 精准定位,HTML 结合解析器特性提取表格。

欢迎点赞收藏,后续会分享 pandas 与数据库交互、大文件分块处理等进阶技巧~ 有具体问题或想了解的场景,欢迎在评论区留言讨论!

更多推荐