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

如何高效将FINRA网站文本数据转换为规整的Pandas DataFrame?

20180801|ZYNE|85036|0|202011|Q N
7676 NaN

由于我需要遍历提取FINRA多年的历史数据,希望能在执行`pd.read_csv()`后直接得到干净可用的DataFrame,避免后续对DataFrame进行修改操作,同时保证数据提取的性能。请问如何实现这一目标?当前的技术方案是否合适?是否需要改用BeautifulSoup等其他网络爬虫库?

---

## 回答

### 问题根源分析
你的代码出现问题主要有两个核心原因:
1. FINRA提供的文件是**竖线(`|`)分隔**的文本文件,而`pd.read_csv()`默认用逗号作为分隔符,导致列识别完全错误。
2. 文件末尾有一行无效的汇总数据(比如示例里的`7676 NaN`),这行数据破坏了DataFrame的结构,引发索引异常。

### 一步到位的干净读取代码
直接在`pd.read_csv()`中配置正确参数,就能直接得到可用的DataFrame,无需后续修改:

```python
import requests
import pandas as pd
import io

url = "http://regsho.finra.org/CNMSshvol20180801.txt"
response_data = requests.get(url).content

df = pd.read_csv(
    io.BytesIO(response_data),
    sep="|",  # 指定正确的竖线分隔符
    skipfooter=1,  # 跳过最后一行无效的汇总数据
    dtype={  # 手动指定列类型,避免自动推断的开销和错误
        "Date": str,
        "Symbol": str,
        "ShortVolume": int,
        "ShortExemptVolume": int,
        "TotalVolume": int,
        "Market": str
    },
    engine="python"  # Python引擎支持skipfooter,C引擎不支持该参数
)

参数详解:

  • sep="|":告诉Pandas用竖线分割列,彻底解决列识别错误问题。
  • skipfooter=1:直接跳过文件末尾的无效汇总行,避免结构异常。
  • dtype:提前指定每列的数据类型,既避免Pandas自动推断时可能出现的类型错误,还能大幅提升读取性能(尤其是批量处理大量文件时)。
  • engine="python":默认的C引擎不支持skipfooter,所以需要切换到Python引擎;如果后续处理超大规模数据,也可以先读取所有行再过滤最后一行,换回C引擎进一步提升速度。

当前技术方案是否合适?需要用BeautifulSoup吗?

完全不需要改用BeautifulSoup!

你的现有方案(requests + pandas)是处理这类场景的最优选择:

  • FINRA直接提供的是原始结构化文本文件,不是HTML页面,BeautifulSoup是用来解析HTML/XML结构的,在这里完全用不上,反而会增加不必要的复杂度和性能开销。
  • requests负责高效下载文件内容,pandas负责快速解析结构化数据,两者搭配是处理批量结构化数据文件的黄金组合。

批量处理多年数据的性能优化方案

如果要遍历多年历史数据,可以结合多线程下载(文件下载是IO密集型任务,多线程能大幅提升效率),同时保持统一的读取参数,确保所有文件都直接输出干净的DataFrame:

from concurrent.futures import ThreadPoolExecutor
import datetime

def fetch_finra_data(date):
    date_str = date.strftime("%Y%m%d")
    url = f"http://regsho.finra.org/CNMSshvol{date_str}.txt"
    try:
        response = requests.get(url, timeout=10)
        response.raise_for_status()  # 捕获HTTP错误(比如文件不存在)
        df = pd.read_csv(
            io.BytesIO(response.content),
            sep="|",
            skipfooter=1,
            dtype={
                "Date": str,
                "Symbol": str,
                "ShortVolume": int,
                "ShortExemptVolume": int,
                "TotalVolume": int,
                "Market": str
            },
            engine="python"
        )
        return df
    except requests.exceptions.RequestException as e:
        print(f"Failed to fetch data for {date_str}: {e}")
        return None

# 示例:获取2018年8月1日到8月5日的数据
start_date = datetime.date(2018, 8, 1)
end_date = datetime.date(2018, 8, 5)
date_range = [start_date + datetime.timedelta(days=i) for i in range((end_date - start_date).days + 1)]

# 多线程批量下载解析,最大化处理速度
with ThreadPoolExecutor(max_workers=5) as executor:
    results = list(executor.map(fetch_finra_data, date_range))

# 合并所有有效数据
clean_dfs = [df for df in results if df is not None]
combined_df = pd.concat(clean_dfs, ignore_index=True)

这个方案既能保证每一个文件都直接输出干净的DataFrame,又通过多线程最大化了数据提取的性能。


内容的提问来源于stack exchange,提问作者Bython

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 06:42:39