vnpy搭配Tushare:Python量化交易股票历史数据获取与入库全攻略

发布时间:2026/10/3 18:22:24
vnpy搭配Tushare:Python量化交易股票历史数据获取与入库全攻略 做量化交易的第一步永远不是写策略而是把数据搞到手。数据不对、不全、不及时后面策略再漂亮也是空中楼阁。我自己在这条路上摸爬滚打从最早手工下载Excel到用爬虫抓网页再到后来用专门的金融数据库踩的坑比大部分人想象的多。今天这篇就围绕Python量化交易中的一个最实际的需求——用vnpy搭配Tushare获取股票历史数据来展开把从注册、装环境、写代码到数据入库的完整流程过一遍代码直接给到能跑的程度。适合那些刚接触量化、想用一套正经框架跑数据、又不想被各种玄学策略带偏的人。很多人问为什么偏偏是vnpy和Tushare这个组合原因很简单vnpy是目前国内开源量化框架里社区最活跃、文档最全、回测和实盘链路最完整的平台Tushare则是普通个人开发者最容易上手、数据维度足够丰富、积分制下成本可控的数据接口。两者搭配起来刚好能覆盖一个人从学习到实盘前期的全部数据需求。这篇博文不聊虚的直接开整。1. 为什么是vnpy搭配Tushare而不是其他数据源1.1 数据源选型Tushare的差异化优势市面上能拿到A股历史行情的Python接口其实就那么几个Tushare、AkShare、Baostock、聚宽、米筐。AkShare免费但接口稳定性随缘爬虫策略一变就罢工Baostock免费但数据更新频率和数据质量在个别标的上不太稳定聚宽和米筐数据质量高但平台封闭数据很难导出来给自己的框架用。Tushare在这个时候的优势就很明显了它是专业金融数据服务商数据经过了比较规范的清洗和校验而且通过统一的API接口返回结构化数据配合积分体系可以解锁各种高级接口比如财务数据、资金流、龙虎榜、期货分钟线等。还有一个很现实的问题量化学习者的成长路径是循序渐进的。刚开始可能只需要日线行情后面做因子分析需要财务数据再后面做盘口分析需要分钟数据。如果一开始选一个只能拉日线的免费库后面就全得推倒重来。Tushare的接口覆盖面广从基础行情到高频数据都有可以在同一个数据源里解决从入门到进阶的问题。另外它的Token机制很简单注册后拿到一个40位左右的字符串所有接口统一用这个Token鉴权对初学者非常友好。1.2 vnpy在整个量化栈中解决什么问题vnpy是国内开源量化框架中一个绕不开的名字。它不是单纯的回测库而是一个完整的交易系统解决方案数据管理、策略研究、回测引擎、模拟交易、实盘交易全部涵盖了。它的核心价值在于把所有原本需要自己从零搭建的底层架构都给你搭好了你要做的只是填充策略和数据的部分。具体到数据这一块vnpy设计了一个统一的数据库抽象层它内部支持SQLite、MySQL、PostgreSQL、MongoDB等多种数据库。无论你用什么数据源最终都可以把行情数据统一写入vnpy的数据库结构里然后回测、实盘都从这个数据库读取。这个设计非常聪明因为数据源是可以换的但vnpy的数据结构是稳定的。比如你今天用Tushare明天觉得数据不够用了想换聚宽只需要写一个数据同步脚本把聚宽的数据拉到vnpy的库里整个策略代码一行都不用改。更重要的是vnpy原生提供了一些数据服务模块的接口社区里也有对应的数据同步工具。不过说实话官方的那套数据服务插件在实际使用中经常会遇到积分门槛或字段对不上的问题所以我个人更推荐直接写一个数据同步脚本把Tushare数据拉下来转换成vnpy的BarData格式再写入数据库。这篇文章后面给的代码就是走这条路径的可控性最强也最容易排查问题。2. 环境准备Python、vnpy与Tushare的三件套2.1 Python版本选择与虚拟环境很多新手在环境这一步就卡住了核心原因是版本不匹配。vnpy目前对Python版本的兼容范围是3.8到3.10新版逐步支持3.11Tushare库本身用requests和pandas对Python版本要求不严格但vnpy因为依赖较多版本太新容易找不到对应的预编译包。我自己实际使用的组合是Python 3.9 64位Windows这套组合在安装vnpy时最省心。如果你是macOS或者Linux也可以但最好也选择3.9或3.10。强烈建议在安装前先检查一下自己的Python版本特别是Mac用户系统自带的Python 2千万别用去Python官网单独下载一个3.9。另外一定要用虚拟环境。vnpy安装的依赖非常多从cibn、ta-lib到grpcio如果你直接装进系统全局环境很容易和已有的包产生冲突。虚拟环境就相当于一个隔离的“实验室”就算里面装乱了删除重来也不影响系统环境。# 创建虚拟环境 python -m venv vnpy_env # 激活环境 # Windows vnpy_env\Scripts\activate # macOS / Linux source vnpy_env/bin/activate2.2 安装vnpy与依赖库激活虚拟环境后直接通过pip安装。国内用户建议配置国内镜像源不然下载速度真的很折磨人。我自己用的是清华源速度和稳定性都还不错。# 配置pip国内源清华 pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple # 安装vnpy默认包含核心功能 pip install vnpy # 安装vnpy的数据管理模块用于查看和管理数据库数据 pip install vnpy_datamanager # 安装Tushare数据接口库 pip install tushare # 安装pandasvnpy自带但如果版本冲突可以单独指定 pip install pandas这里说一个新手容易踩的坑vnpy安装包里面某些依赖在Windows上需要编译如果安装报错大概率是缺少Microsoft C Build Tools或者ta-lib。ta-lib是一个技术指标计算库vnpy的回测引擎里很多指标计算依赖它。Windows下最简单的办法是去这个网址下载预编译的whl文件对应Python版本和系统位数的ta_lib安装包然后用pip install本地whl安装再回来pip install vnpy就好了。安装完成后可以快速验证一下vnpy是否能正常导入from vnpy.trader.constant import Exchange, Interval print(vnpy导入成功)如果这行不报错说明vnpy核心已经装好可以进行下一步了。2.3 验证环境和配置数据库vnpy默认使用SQLite作为数据库好处是零配置开箱即用。对个人学习者来说SQLite完全够用等数据量大了再换MySQL就行。首次运行vnpy时它会在用户目录下创建一个配置目录Windows下是C:\Users\你的用户名\.vntradermacOS/Linux是~/.vntrader里面有一个vt_setting.json文件。这个配置文件控制了vnpy的很多行为其中数据库相关的配置长这样{ database.driver: sqlite, database.database: database.db, database.host: , database.port: 0, database.user: , database.password: }如果是SQLite保持默认即可其他字段不用管。如果你后续想切换MySQL或PostgreSQL改driver和database字段再填好host、port、user、password就行。但初次学习和验证阶段请务必用SQLite因为省去了一堆数据库服务安装配置的麻烦能让你把注意力集中在核心的数据获取和策略开发上。3. Tushare端的数据获取与复权处理3.1 注册、Token与积分机制Tushare使用Token来做用户身份验证Token在Tushare官网的个人主页可以看到。注册流程非常简单手机号注册、登录、在“个人主页-接口TOKEN”里复制那串字符即可。这里必须重点说一下积分机制因为很多人在这一步被劝退。Tushare的数据接口不是全部免费的不同接口对积分有不同的要求。基础接口比如获取股票列表和基础信息低积分就能用但像日线行情这类核心接口就需要达到一定积分才能调用。积分从哪里来注册送一部分完善个人信息送一部分每日登录签到也会送绑定社交媒体、邀请好友都有积分奖励。我最初注册的时候靠签到和完善资料大概两三天就攒够了日线行情的调用权限。后续如果确实需要更高权限的接口比如财务数据、分钟数据可以考虑充值或通过社区贡献获取积分。客观地说对个人量化学习者基础积分足够用了。获取Token后在Python里初始化Tushareimport tushare as ts # 请替换成你自己的Token ts.set_token(你的40位Token字符串) # 初始化pro接口 pro ts.pro_api()3.2 获取股票列表与日线行情有了pro接口后第一件事通常是获取全市场股票列表。使用stock_basic接口可以获取股票代码、名称、行业、上市日期等信息。# 获取全部股票基础信息 df_stock_basic pro.stock_basic( exchange, list_statusL, # L上市状态D退市P暂停上市 fieldsts_code,symbol,name,area,industry,list_date ) print(df_stock_basic.head()) print(fA股上市公司数量{len(df_stock_basic)})日线行情的获取有两个接口daily和pro_bar。daily接口更底层返回的是不复权的原始数据字段包括开高低收、成交量、成交额等pro_bar则是一个封装接口支持直接指定复权方式用起来更顺手。# 用pro_bar获取单只股票的历史日线前复权 df_daily ts.pro_bar( ts_code000001.SZ, adjqfq, # 前复权 start_date20200101, end_date20240101 ) print(df_daily.head()) print(f数据条数{len(df_daily)})这里有个细节pro_bar的ts_code参数使用的是带交易所后缀的代码比如平安银行是000001.SZ贵州茅台是600519.SH。而daily接口在查询时会自动按交易日倒序排列pro_bar则会返回顺序排列的DataFrame两者在后续处理时的索引方式不同要注意区分。3.3 复权数据的计算与原理解读股票分红、送股会导致价格出现非自然跳变直接把不复权数据丢给回测引擎会计算出大量错误的收益。所以复权是量化交易数据准备中绕不开的环节。Tushare提供了复权因子接口adj_factor你可以用它来计算后复权价格。理解复权因子之前先理解一个概念不复权价格是股票在交易所的实际成交价但它没有考虑分红送股带来的除权除息。后复权的思想是把除权除息前的价格按因子向上调整使得价格序列在除权日前后保持连续。计算公式很简单后复权价格 原始价格 × 复权因子前复权则是一个视角相反的处理保持最新价格不动把历史价格按因子反向调整。尽管Tushare的pro_bar接口已经内置了前复权和后复权的选项但在自己做因子研究时理解并能够手动计算复权价格依然很有价值。# 获取复权因子 df_adj pro.adj_factor(ts_code000001.SZ, start_date20200101, end_date20240101) # 合并行情和复权因子 df df_daily.merge(df_adj[[trade_date, adj_factor]], ontrade_date, howleft) # 手动计算后复权价 df[close_adj] df[close] * df[adj_factor] # 手动计算前复权价用最新一期复权因子做基准 latest_factor df[adj_factor].iloc[-1] df[close_qfq] df[close] * df[adj_factor] / latest_factor这里要特别注意前复权数据是会变的。因为前复权以“当前最新价格不变”为基准来调整历史价格一旦未来发生新的除权除息事件整个历史价格序列都会被重算一遍。这意味着你在不同时间下载同一只股票的前复权数据结果可能不一样。而后复权数据则不会变因为它的基准固定在最早上市日。所以做量化研究时如果对历史一致性要求高建议优先使用后复权数据或者干脆在本地存一份不复权价格加复权因子的组合随时可以自己计算任意复权方式的价格序列。这也是专业量化机构的通行做法。4. 把数据接入vnpy数据库4.1 vnpy的数据库抽象层vnpy的核心交易框架中定义了一个数据库接口规范不管是SQLite、MySQL还是MongoDB对上层提供的API都是一致的save_bar_data、load_bar_data、delete_bar_data等。这意味着你写的数据同步脚本只要面向vnpy的标准接口底层数据库怎么换都不影响你的代码。具体在代码里我们要用到get_database这个全局函数来获取数据库实例。在vnpy 3.x版本中调用方式如下from vnpy.trader.database import get_database database get_database()这个database对象就代表着你配置文件中指定的那个数据库实例。在vnpy首次运行时会自动初始化数据表结构。如果你之前没跑过vnpy建议先运行一次python -c from vnpy.trader.database import get_database; print(get_database())确认数据库初始化成功再往下走。4.2 将DataFrame转换为BarData并入库vnpy内部统一使用BarData这个数据结构来表示K线字段包括symbol代码、exchange交易所、datetime时间、interval周期、volume成交量、turnover成交额、open_interest持仓量股票为0、open_price、high_price、low_price、close_price等。把DataFrame转换为BarData列表核心是字段映射。Tushare返回的字段名是open、high、low、close而vnpy的字段名是open_price、high_price、low_price、close_price两者必须对应上。另外Tushare的trade_date是字符串格式需要转成datetime对象并指定为北京时间vnpy要求datetime带时区信息。这里给出一段核心转换代码from vnpy.trader.object import BarData from vnpy.trader.constant import Exchange, Interval from datetime import datetime def df_to_bar_data(df: pd.DataFrame, symbol: str, exchange: Exchange) - list: bars [] for _, row in df.iterrows(): bar BarData( symbolsymbol, exchangeexchange, datetimedatetime.strptime(row[trade_date], %Y%m%d), intervalInterval.DAILY, volumefloat(row[vol]) * 100, # Tushare单位是手vnpy预期是股 turnoverfloat(row[amount]) * 1000, # Tushare单位是千元vnpy预期是元 open_pricefloat(row[open]), high_pricefloat(row[high]), low_pricefloat(row[low]), close_pricefloat(row[close]), open_interest0.0, gateway_nameTUSHARE, ) bars.append(bar) return bars然后调用数据库的save_bar_data批量写入database.save_bar_data(bars)需要注意Exchange这个枚举。vnpy里对上交所和深交所的定义是Exchange.SSE和Exchange.SZSE。判断方式很简单ts_code以SH结尾的是上交所以SZ结尾的是深交所。如果股票代码是北交所的就是Exchange.BSE新版vnpy已支持。还有一点非常容易踩坑vnpy数据库里保存的BarData主键是symbol exchange interval datetime的组合。如果保存时时间和已有数据重叠会触发更新而非追加。所以重复运行脚本不会产生重复数据但如果你只更新了某一天的数据它会覆盖那天的旧数据这通常是我们想要的。4.3 数据入库后的校验数据写进去不等于万事大吉一定要校验。我自己的习惯是入库后做三件事第一查总条数看和Tushare返回的是否一致第二查最早和最晚日期确认时间范围正确第三随机抽查某天的OHLC数据在Tushare端和vnpy数据库端对比是否一致。vnpy提供了load_bar_data来进行查询from vnpy.trader.database import get_database from vnpy.trader.constant import Exchange, Interval from datetime import datetime database get_database() bars database.load_bar_data( symbol000001, exchangeExchange.SZSE, intervalInterval.DAILY, startdatetime(2020, 1, 1), enddatetime(2024, 1, 1), ) print(f数据库中共 {len(bars)} 根Bar) print(bars[0])如果发现条数不对最常见的原因是Tushare在某个时间段存在停牌导致没有数据或者筛选时用了不复权数据但复权因子合并后产生了空值。解决思路是用Tushare的trade_cal接口获取交易日历与数据库中的日期做差集找出缺失的交易日再针对性地排查。5. 完整实操代码与运行效果5.1 脚本整体结构我平时用来同步Tushare数据到vnpy数据库的脚本结构不复杂但考虑了几个点支持批量导入多只股票、支持增量更新只拉取最新数据、对网络异常做重试、对已存在的Bar不做重复写入。下面把这个脚本拆开讲每一步都给出原因。整体流程如下初始化Tushare接口和vnpy数据库实例获取股票列表或使用你指定的股票列表循环遍历每只股票调用Tushare接口获取历史日线数据将DataFrame转换为vnpy BarData列表调用数据库接口批量写入打印日志输出每只股票的入库数量和日期范围5.2 逐段拆解关键代码首先是导入和初始化部分。这里我加了重试机制因为Tushare的免费接口在实际调用中偶尔会出现超时或限流重试3次可以显著提升成功率。import time import pandas as pd import tushare as ts from datetime import datetime from vnpy.trader.constant import Exchange, Interval from vnpy.trader.object import BarData from vnpy.trader.database import get_database # Tushare初始化 TS_TOKEN 你的Token ts.set_token(TS_TOKEN) pro ts.pro_api() # vnpy数据库初始化 database get_database() def retry_call(func, *args, retries3, **kwargs): for i in range(retries): try: return func(*args, **kwargs) except Exception as e: print(f调用失败第{i 1}次重试错误{e}) time.sleep(2) raise RuntimeError(重试多次仍然失败)然后是判断交易所、转换BarData、写入数据库的几个关键函数def get_exchange(ts_code: str) - Exchange: 根据ts_code后缀判断交易所 if ts_code.endswith(SH): return Exchange.SSE elif ts_code.endswith(SZ): return Exchange.SZSE elif ts_code.endswith(BJ): return Exchange.BSE else: raise ValueError(f未知交易所{ts_code}) def df_to_bars(df: pd.DataFrame, symbol: str, exchange: Exchange) - list: bars [] if df is None or df.empty: return bars for _, row in df.iterrows(): bar BarData( symbolsymbol, exchangeexchange, datetimedatetime.strptime(str(row[trade_date]), %Y%m%d), intervalInterval.DAILY, volumefloat(row[vol]) * 100, turnoverfloat(row[amount]) * 1000, open_pricefloat(row[open]), high_pricefloat(row[high]), low_pricefloat(row[low]), close_pricefloat(row[close]), open_interest0.0, gateway_nameTUSHARE, ) bars.append(bar) return bars def sync_stock_daily(ts_code: str, start_date: str, end_date: str, adj: str qfq): 同步单只股票的日线数据到vnpy数据库 symbol ts_code.split(.)[0] # 去掉后缀vnpy里symbol不带交易所 exchange get_exchange(ts_code) # 拉取数据 df retry_call( ts.pro_bar, ts_codets_code, adjadj, start_datestart_date, end_dateend_date, ) if df is None or df.empty: print(f{ts_code} 无数据) return 0 # 统一按交易日期排序pro_bar多数情况已排序这里做个保险 df df.sort_values(trade_date) # 转换并入库 bars df_to_bars(df, symbol, exchange) database.save_bar_data(bars) print(f{ts_code} 入库完成数量 {len(bars)}) return len(bars)最后是批量同步的逻辑。这里有一个重要的增量更新技巧并不是每次都全量拉取所有历史数据而是先查一下数据库里已有的最新数据日期然后只拉取这个日期之后的数据。def get_latest_date_in_db(symbol: str, exchange: Exchange) - datetime: 查询数据库中该股票的最新Bar日期 bars database.load_bar_data( symbolsymbol, exchangeexchange, intervalInterval.DAILY, startdatetime(1990, 1, 1), enddatetime.now(), ) if bars: return bars[-1].datetime return datetime(1990, 1, 1) def sync_all_stocks(ts_codes: list, start_date: str 20200101, end_date: str 20240101, adj: str qfq): for ts_code in ts_codes: try: sync_stock_daily(ts_code, start_date, end_date, adj) except Exception as e: print(f{ts_code} 同步失败{e}) time.sleep(0.5) # 适当限速避免触发Tushare流控 if __name__ __main__: # 示例同步平安银行、贵州茅台、招商银行 codes [000001.SZ, 600519.SH, 600036.SH] sync_all_stocks(codes, start_date20200101, end_date20240101)5.3 运行日志与结果输出正常情况下你运行上述脚本会看到类似下面的输出000001.SZ 入库完成数量 971 600519.SH 入库完成数量 971 600036.SH 入库完成数量 971然后你就可以在vnpy的数据管理工具里看到这些数据也可以直接VnPy的CtaBacktester里导入这些数据进行回测。如果用的是vnpy自带图形界面打开MainEngine后在“功能”菜单里选择“数据管理”就能按交易所和合约代码查询到入库的K线数据。当我第一次成功把这些数据跑通时最大的感受是数据链路通了量化的地基才算是稳了。后面无论写均值回归、趋势跟踪还是多因子策略都是在这个地基上盖房子。6. 高频踩坑记录与排查思路6.1 常见问题速查表下面这份表是我给朋友们排查数据问题时最常参考的清单基本覆盖了vnpy Tushare搭配中最容易出问题的位置。症状可能原因解决办法Tushare返回空DataFrame积分不足接口未开通查看Tushare官网积分要求多签到攒积分Tushare报错“抱歉您没有访问该接口的权限”Token错误或积分不够检查Token是否复制完整确认接口积分门槛vnpy导入时报错缺少ta-libWindows下ta-lib未正确安装下载对应Python版本的whl包本地pip安装数据库报错无法打开SQLite路径权限或配置损坏检查vt_setting.json中database.database路径删除损坏的db重建入库后日期全部变成1970年datetime未带时区或字符串转datetime失败检查datetime.strptime的格式确保转换出的是正确年份回测收益率异常偏高使用不复权数据导致分红跳空改用前复权数据或使用后复权数据做研究同一只股票重复入库没有增量判断重复执行同步脚本先查数据库最新日期只同步该日期之后的数据成交量数据比实际少100倍vol单位是手没有乘以100转BarData时volumevol×1006.2 我的排查经验与建议第一个建议不要信任任何一次接口返回尤其是免费接口。Tushare整体数据质量很高但在极个别停牌股、新上市股上偶尔会有字段异常。我在写脚本的时候每处理完100只股票就会做一个完整性校验比如检查是否存在开高低收全为0的Bar是否存在日内High Low的情况是否连续交易日的日期存在大段缺失。这些校验代码总共也就二三十行但能在问题扩散之前及时拦住。第二个经验是关于复权选择的策略。如果你只做日线级别的策略回测前复权就够了因为它最贴近你实际看到的行情软件但如果你的策略周期跨度很长比如回测三年、五年前复权的历史价格会随着每次分红送股不断变化这会导致同一个策略在不同时间回测得到不同结果。而这种“结果漂移”往往会误导你对策略稳定性的判断。我更推荐把“不复权价 复权因子”一并存入数据库在需要的时候自己动态计算复权价格。这样既保留了原始数据的真实性又具备最高的灵活性。第三个重要的点是增量同步策略。不要每次都全量拉取不管是对Tushare服务器还是对你的数据库都没必要。我习惯的做法是先查vnpy数据库里这只股票的最新Bar是什么时间然后以这个时间的次日作为拉取起点到最新的交易日为止。这样做不仅快还能天然避免重复数据的问题日积月累下来可以节省大量API调用次数和时间。第四个容易忽略的细节是交易日历的本地化。Tushara的trade_cal接口可以获取沪深两市的交易日历但我建议把它单独存一份因为你在做回测的时候经常需要判断某一天是不是交易日比如计算持仓天数、计算年化收益等。把这部分数据也同步到本地数据库后很多后续的分析代码都会变得简单很多。还有一个我自己踩过的坑vnpy的BarData中不同交易所对应的symbol格式也有讲究。vnpy默认的合约代码通常是“600519”而不是“600519.SH”或“600519.SSE”。所以从Tushare拿到ts_code600519.SH之后记得split(.)[0]只取前六位数字。如果忘了处理后面所有策略和回测都会因为symbol不匹配而找不到数据而且这种问题报错信息还不直观排查起来特别浪费时间。最后聊一下数据入库的性能问题。有人刚开始写同步脚本会一条一条地save_bar_data比如用for循环逐根K线去插入数据库几百只股票、每只几百根K线能跑几个小时。vnpy的save_bar_data本身就支持批量写入直接把一个BarData列表传进去就行同时底层是数据库事务处理和批量插入性能提升是几十倍甚至几百倍。如果你要同步的股票数量很大比如全市场5000只股票建议一次传入一批Bar并配合适当的time.sleep既有速度又不会触发Tushare的流控限制。还有一个稍微进阶一点的经验为vnpy切换MySQL数据库。当你的数据量涨到百万级Bar之后SQLite的查询速度会明显下降尤其是做多标的、长时间跨度的回测时。vnpy支持无缝切换MySQL只需要在vt_setting.json里修改配置再在MySQL里建一个空库即可。数据迁移可以使用vnpy DataManager自带的导入导出功能或者写一个小脚本从SQLite读取再写入MySQL。切换完成后回测速度会有一个质的提升这也是为什么要从一开始就把数据访问接口统一到vnpy抽象层的好处。我在前面反复提过要把Tushare的vol手转成vnpy的volume股多问问为什么要这样转的人往往就踩过“回测结果不对但找不到原因”的坑。国内股票的1手100股但vnpy设计上一律使用“股”作为最小单位。如果不做转换回测出来的资金占用和滑点成本都会失真策略信号虽然一样但绩效统计完全不可信。这一行代码值回整篇文章。回到开头的那个问题量化交易的第一步是什么是数据。数据正确、完整、及时才有后续的一切。这套vnpy Tushare的搭配我不能说是最完美的但它是目前个人开发者最容易上手、最不容易被卡脖子的组合。跑通这一条数据流水线之后你就能把精力真正放到策略本身而不是每天和数据源搏斗。最后分享一个小技巧在做数据同步脚本时给每个接口调用加上一个简单的日志输出记录当前同步到哪只股票、拉到了多少条数据、耗费了多长时间。这样一旦同步过程中断你可以很快知道该从哪个位置续传而不是傻乎乎从头再来。这套“断点续传”的思路在以后处理更大规模的数据时会反复用到。