☰
finviz-api筛选器高级用法:从URL参数到多因子选股实战
2026/10/24 6:06:27 网站建设 项目流程

1. 从URL到数据:finviz-api筛选器高级用法与实战案例

第一次接触finviz-api是在做一个量化选股的小工具时。当时的需求很直接:每天收盘后,从美股市场里筛出符合特定条件的标的,比如市值在一定区间、市盈率低于行业均值、近期成交量放大、技术面出现特定形态。手动在网页上点筛选器,一次两次还行,天天这么干,人受不了。后来发现finviz的URL本身就携带了筛选参数,只要把URL构造对了,页面返回的就是筛选后的结果。再后来接触到finviz-api这个Python库,才算是真正把这件事流程化了。

这篇文章想聊的,就是怎么把finviz-api的筛选器用透。不是那种“安装完跑个demo”的浅尝辄止,而是从URL参数构造、筛选器组合逻辑、字段映射、数据清洗,到实际选股场景的完整拆解。如果你也在做美股数据相关的分析,或者想找一个低门槛的选股数据源,这套东西值得花时间研究。

finviz-api本质上是对finviz网站筛选器功能的一层Python封装。它的核心价值在于:你不需要手动拼接URL,也不需要解析HTML表格,库本身会把筛选条件翻译成请求参数,再把返回的表格数据解析成结构化对象。但封装归封装,底层逻辑还是那套URL参数体系。理解了这个体系,你才能知道哪些筛选条件可以组合、哪些字段能排序、哪些参数有隐藏的取值规则。

适合读这篇内容的人,大概分三类:一是做量化选股但预算有限、不想一上来就买商业数据接口的;二是做市场情绪监控、需要定期拉取特定板块数据的;三是单纯想学一下怎么把网页筛选器“逆向”成可编程接口的。不管哪一类,下面的内容都会从最基础的URL构造讲起,逐步深入到多条件组合、数据后处理和实战案例。

2. finviz筛选器的URL参数体系拆解

2.1 筛选器参数的基本结构

finviz的筛选器页面URL有一个固定的基础路径,后面跟一串以“f=”开头的参数。这个参数就是筛选条件的核心载体。比如你想筛选市值大于100亿美元、市盈率低于20的股票,URL大概长这样:

https://finviz.com/screener.ashx?v=111&f=cap_largeover,pe_low

这里的“v=111”是视图参数,控制返回表格的列组合。“f=”后面跟的就是筛选条件,多个条件用逗号分隔。每个条件都是一个预定义的代码,比如“cap_largeover”代表市值大于100亿,“pe_low”代表市盈率较低。

finviz-api做的事情,就是把这些代码封装成了更易读的Python参数。你不需要记住“cap_largeover”这种字符串,而是可以直接传“market_cap=’largeover’”。但底层请求的时候,它还是拼成上面那种URL。

理解这一点很重要,因为当你想用一些库没有显式支持的筛选条件时,你仍然可以手动构造“f=”参数,通过库的底层接口发出去。这就是“高级用法”的起点。

2.2 常用筛选器代码分类与含义

finviz的筛选器代码大致可以分成几类:估值类、财务类、技术类、板块类、市值类。每一类下面有若干具体条件。下面这张表整理了一些高频使用的代码,以及它们在finviz-api里对应的参数写法。

筛选维度finviz代码finviz-api参数示例含义
市值cap_largeovermarket_cap=’largeover’市值大于100亿
市值cap_midovermarket_cap=’midover’市值大于20亿
市盈率pe_lowpe=’low’市盈率低于行业均值
市盈率pe_highpe=’high’市盈率高于行业均值
股息率div_highdividend_yield=’high’股息率较高
成交量sh_avgvol_o500shares_avg_volume=’o500’日均成交量大于50万
价格sh_price_o10price=’o10’股价大于10美元
技术面ta_sma20_pasma20=’pa’价格在20日均线上方
技术面ta_rsi_os30rsi=’os30’RSI低于30
板块sec_technologysector=’technology’科技板块
行业ind_semiconductorindustry=’semiconductor’半导体行业

这张表不是全部,但覆盖了日常筛选最常用的维度。需要注意的是,finviz-api的参数命名和finviz代码之间不是一一对应的字符串替换,库内部有自己的映射逻辑。有些参数支持多个取值,比如market_cap可以传’largeover’、’midover’、’smallover’等,具体支持哪些值,需要看库的源码或者文档。

2.3 视图参数v的隐藏作用

很多人会忽略“v=”这个参数,觉得它只是控制显示哪些列。但实际上,它影响的不只是展示,还影响你拿到的数据字段。finviz的表格视图有多个预设,比如v=111是默认的概览视图,v=121是财务视图,v=131是技术面视图。不同视图返回的列不一样,finviz-api解析出来的对象属性也会不同。

如果你只关心价格和成交量,用默认视图就够了。但如果你想拿到ROE、毛利率、负债率这些财务指标,就需要切换到对应的视图。切换视图的方法很简单,在初始化Screener的时候传入view参数:

from finviz.screener import Screener stock_list = Screener(filters=[...], view=121)

这里的121就是财务视图。实测下来,财务视图返回的字段更全,但请求速度会稍微慢一点,因为表格列多了,页面体积大了。如果只是做初筛,建议先用默认视图,拿到股票代码列表后,再针对性地拉取详细数据。

提示:视图参数和筛选参数是独立的。你可以用默认视图配合复杂的筛选条件,也可以用财务视图配合简单的筛选条件。两者不冲突,但组合起来会影响请求效率。

3. finviz-api筛选器高级组合技巧

3.1 多条件叠加的逻辑关系

finviz的筛选器默认是“与”逻辑,也就是所有条件同时满足才会出现在结果里。这一点和大多数筛选系统一致,但有几个细节容易踩坑。

第一,同一维度的多个条件如果同时传入,finviz会取并集还是交集?答案是看具体参数。比如你同时传market_cap=’largeover’和market_cap=’midover’,这实际上是矛盾的,因为一个股票不可能同时满足市值大于100亿和市值在20亿到100亿之间。finviz-api在这种情况下会怎么处理?实测发现,它会取更严格的那个条件,也就是largeover。但这不是官方文档写明的行为,而是底层URL参数拼接后的自然结果。所以建议不要在同一维度上传多个互斥条件,容易产生预期外的结果。

第二,跨维度的条件叠加是安全的。比如市值大于100亿、市盈率低于行业均值、成交量大于50万,这三个条件分别属于市值、估值、成交量三个维度,叠加起来就是交集,逻辑清晰。

第三,有些条件本身带有方向性。比如sma20=’pa’表示价格在20日均线上方,sma20=’pb’表示价格在20日均线下方。这两个条件不能同时满足,传进去只会有一个生效。所以构造筛选条件时,要清楚每个参数的方向性。

3.2 排序与分页的参数控制

finviz的筛选结果默认按股票代码排序,但你可以通过order参数指定排序字段。比如按市值降序、按成交量降序、按涨跌幅降序。这个功能在选股时非常实用,因为你可以先筛选出一个大池子,再按某个指标排序,取前N个。

finviz-api里对应的参数是order和ascend。order传字段名,ascend传布尔值。比如:

stock_list = Screener( filters=['cap_largeover', 'pe_low'], order='market_cap', ascend=False )

这样返回的结果就是按市值从大到小排列。

分页方面,finviz每页默认显示20条记录。如果你的筛选结果超过20条,需要翻页。finviz-api提供了get_page方法,可以指定页码。但更高效的做法是直接设置每页显示数量。finviz的URL里有一个“r=”参数,可以控制每页行数,最大可以设到100。不过finviz-api没有直接暴露这个参数,你需要通过底层请求来设置。

一个变通的方法是:先用筛选器拿到第一页的结果,如果结果数量接近20,说明可能还有更多。然后手动构造带“r=100”的URL,用requests直接请求,再用BeautifulSoup解析。虽然麻烦一点,但能拿到更完整的数据。

3.3 自定义筛选条件的底层注入

finviz-api的filters参数接受的是字符串列表,每个字符串对应一个finviz代码。但库本身对这些代码的校验并不严格,你可以传入一些库没有预定义的代码,只要finviz网站认识就行。

比如,finviz有一个筛选条件是“内部人交易”,代码是“it_latestbuys”和“it_latestsales”。finviz-api的文档里可能没有提到这两个,但你直接传进去,底层URL拼接后,finviz照样能识别。这就打开了自定义筛选的大门。

我试过的一个场景是:筛选出近一周有内部人买入、同时技术面RSI低于40的股票。构造的filters列表是:

filters = ['it_latestbuys', 'ta_rsi_os40']

跑下来确实能拿到结果。但要注意,这种“未文档化”的用法有风险,因为finviz随时可能改代码。所以建议把这类自定义代码集中管理,方便后续维护。

注意:传入未定义的筛选代码时,finviz-api不会报错,但返回的结果可能为空。所以每次调整筛选条件后,最好先小范围测试,确认返回数量合理,再批量跑。

4. 从筛选结果到可用数据的完整流程

4.1 数据拉取与字段解析

finviz-api返回的Screener对象,本质上是一个二维表格的封装。你可以通过data属性拿到所有行,每行是一个字典,键是列名,值是单元格内容。但这里有几个细节需要处理。

第一,数值字段的类型转换。finviz返回的市值、成交量、市盈率等字段,都是带单位或百分号的字符串。比如“1.23B”表示12.3亿,“4.56M”表示456万,“12.34%”表示百分比。直接拿来做计算会出错,需要写一个解析函数,把“B”“M”“K”转换成数值,把百分号去掉再转浮点数。

第二,缺失值的处理。有些字段在finviz页面上显示为“-”,表示无数据。解析的时候要把它转换成None或者NaN,否则后续计算会报错。

第三,字段名的一致性。不同视图下,同一个指标可能有不同的列名。比如默认视图里叫“P/E”,财务视图里可能叫“P/E Ratio”。如果你的代码依赖列名来取数,切换视图后可能会取不到。建议在解析层做一层映射,把不同视图的列名统一成内部标准名。

下面是一个字段解析的示例:

def parse_number(value): if value == '-' or value == '': return None value = value.replace(',', '') if value.endswith('B'): return float(value[:-1]) * 1_000_000_000 if value.endswith('M'): return float(value[:-1]) * 1_000_000 if value.endswith('K'): return float(value[:-1]) * 1_000 if value.endswith('%'): return float(value[:-1]) try: return float(value) except ValueError: return None

这个函数能处理大部分情况,但遇到“1.23B”这种带小数点的也能正确解析。实测下来,finviz的数值格式比较规范,很少出现意外格式。

4.2 数据清洗与异常值过滤

拿到结构化数据后,下一步是清洗。清洗的目标是让数据适合后续分析,比如计算因子、排序、回测。

常见的清洗操作包括:剔除市值为None的股票、剔除成交量为0的股票、剔除价格低于1美元的仙股。这些操作看起来简单,但如果不做,后续计算很容易被异常值带偏。

还有一个容易被忽略的点:finviz的筛选结果里可能包含ETF、优先股、权证等非普通股标的。如果你的策略只针对普通股,需要在清洗阶段过滤掉。判断方法可以看股票代码是否包含“.”或“-”,比如“BRK.B”是普通股,“XYZ.PA”可能是优先股。但这个方法不绝对,更稳妥的方式是结合市值和成交量综合判断。

我一般会在清洗阶段加一个“最小流动性”过滤:日均成交量大于10万股,股价大于2美元。这样能过滤掉大部分流动性差的标的,减少后续回测的噪音。

4.3 结果导出与持久化存储

清洗完的数据,下一步是存下来。最简单的做法是导出CSV,用pandas的to_csv方法。但如果要定期跑筛选,建议存到SQLite或者PostgreSQL里,方便后续查询和增量更新。

存数据库的时候,有几个字段建议加索引:股票代码、筛选日期、市值、成交量。这样后续按日期查询或者按股票代码查询都会快很多。

另外,建议在存储的时候加一个“筛选条件”字段,记录这次数据是用什么条件筛出来的。这样后续回溯的时候,能知道每个数据点的来源。比如:

import sqlite3 import pandas as pd conn = sqlite3.connect('screener_results.db') df['screen_date'] = '2025-01-15' df['screen_filter'] = 'cap_largeover,pe_low,sh_avgvol_o500' df.to_sql('results', conn, if_exists='append', index=False)

这个做法在跑多组筛选条件的时候特别有用,因为你可以把不同条件的结果存在同一张表里,用screen_filter字段区分。

5. 实战案例:构建一个多因子选股流程

5.1 案例背景与筛选目标

假设我们要构建一个简单的多因子选股流程,目标是:从美股市场中筛选出市值大于50亿、市盈率低于25、近20日价格在均线上方、日均成交量大于100万的股票,然后按市值降序排列,取前30只作为候选池。

这个流程涉及四个筛选维度:市值、估值、技术面、流动性。每个维度对应一个finviz筛选代码。组合起来就是:

filters = [ 'cap_midover', # 市值大于20亿,但我们需要大于50亿,后面再过滤 'pe_low', # 市盈率低于行业均值 'ta_sma20_pa', # 价格在20日均线上方 'sh_avgvol_o1000' # 日均成交量大于100万 ]

注意,finviz的市值筛选只有几个档位:largeover是100亿以上,midover是20亿以上,smallover是3亿以上。没有50亿这个档位。所以先用midover筛出20亿以上的,再在清洗阶段过滤掉小于50亿的。

5.2 筛选器构造与数据拉取

构造Screener对象的时候,除了filters,还要指定order和ascend。order传’market_cap’,ascend传False,这样结果按市值从大到小排。

from finviz.screener import Screener filters = ['cap_midover', 'pe_low', 'ta_sma20_pa', 'sh_avgvol_o1000'] stock_list = Screener(filters=filters, order='market_cap', ascend=False)

拉取的时候要注意,finviz对请求频率有限制。如果短时间内发太多请求,可能会被临时限制。所以建议在两次请求之间加一个sleep,比如time.sleep(1)。虽然慢一点,但稳定。

拉下来的数据是一个列表,每个元素是一个字典。可以先转成pandas DataFrame,方便后续处理。

import pandas as pd data = stock_list.data df = pd.DataFrame(data)

这时候df的列名是finviz的原始列名,比如“Ticker”“Market Cap”“P/E”“Price”“Volume”。需要重命名成更简洁的名字,方便后续引用。

5.3 因子计算与排序逻辑

拿到原始数据后,先做字段解析。把Market Cap、Volume、Price这些字段从字符串转成数值。然后过滤掉市值小于50亿的、价格低于2美元的、成交量为0的。

接下来可以计算一些衍生因子。比如“市值/成交量”比值,用来衡量流动性溢价;“市盈率倒数”即盈利收益率,用来做估值排序。这些因子可以帮助你在候选池里进一步优选。

排序逻辑可以分两层:第一层按市值降序,取前50;第二层在这50只里按盈利收益率降序,取前30。这样既保证了流动性,又兼顾了估值。

df['market_cap_num'] = df['Market Cap'].apply(parse_number) df['pe_num'] = df['P/E'].apply(parse_number) df['earnings_yield'] = 1 / df['pe_num'] df_filtered = df[df['market_cap_num'] > 5_000_000_000] df_filtered = df_filtered[df_filtered['Price'] > 2] top_by_cap = df_filtered.nlargest(50, 'market_cap_num') top_by_yield = top_by_cap.nlargest(30, 'earnings_yield')

这个流程跑下来,大概能拿到30只左右的候选股票。数量不多,但质量相对可控。

5.4 结果验证与回测思路

拿到候选池后,不要急着上实盘。先做一下历史验证。最简单的方法是:把过去几个月的筛选结果拉出来,看看这些股票在随后一周或一个月的表现。如果整体跑赢基准,说明筛选逻辑有一定效果。

finviz-api本身不提供历史数据,但你可以每天跑一次筛选,把结果存下来,积累一段时间后就有了自己的历史数据集。然后用pandas做简单的回测:计算每期候选池的平均收益,和标普500同期收益对比。

这个做法虽然粗糙,但胜在门槛低。不需要复杂的回测框架,也不需要付费数据。适合个人开发者快速验证想法。

提示:回测的时候要注意幸存者偏差。finviz的筛选结果是基于当前数据的,如果你用今天的数据去回测过去,会引入未来函数。正确的做法是每天定时跑筛选,把结果按日期存下来,用当时的数据做决策。

6. 常见问题与排查技巧实录

6.1 筛选结果为空或数量异常

这是最常见的问题。可能的原因有几个:一是筛选条件太严格,导致没有股票满足;二是筛选代码拼写错误,finviz不认识;三是请求被限制,返回了空页面。

排查方法:先把筛选条件减少到只有一个,看看能不能拿到结果。如果能,再逐步加条件,直到找到导致空结果的那个条件。如果是代码拼写错误,finviz-api不会报错,但返回的data列表是空的。所以每次调整条件后,先打印len(data)确认数量。

如果是请求被限制,通常会返回一个包含验证码的页面。finviz-api解析这种页面会失败,data也是空的。这时候需要等一段时间再试,或者降低请求频率。

6.2 字段解析报错与类型转换失败

字段解析报错通常是因为遇到了意外的格式。比如某个字段的值是“N/A”而不是“-”,或者带了额外的空格。解决方法是把解析函数写得健壮一点,用try-except包住,遇到无法解析的值就返回None。

另一个常见问题是百分号的处理。有些字段是“12.34%”,有些是“0.1234”。finviz的格式比较统一,一般都是带百分号的。但如果你切换了视图,可能会遇到不带百分号的。所以解析的时候要先判断是否包含“%”,再决定怎么处理。

6.3 请求频率限制与应对策略

finviz对请求频率有限制,但没有公开具体的阈值。实测下来,每秒1次请求是比较安全的。如果并发请求,很容易被限制。

应对策略:一是加sleep,每次请求后等1到2秒;二是用缓存,把已经拉过的数据存到本地,避免重复请求;三是分批拉取,不要一次性拉太多页。

如果已经被限制了,可以尝试换一个User-Agent,或者等几个小时再试。但最根本的解决办法还是降低频率。

6.4 筛选代码变更与兼容性处理

finviz偶尔会调整筛选代码,比如把某个代码改名,或者增加新的代码。这会导致原本能跑的脚本突然失效。

应对方法:把筛选代码集中定义在一个配置文件或常量模块里,不要散落在代码各处。这样一旦某个代码失效,只需要改一个地方。另外,定期检查finviz的筛选器页面,看看有没有新增的代码或变更。

下面这张表整理了一些常见问题及其排查方法:

问题现象可能原因排查方法解决方案
返回结果为空条件太严 / 代码错误 / 被限制减少条件逐个测试放宽条件 / 修正代码 / 降低频率
字段解析报错格式意外 / 缺失值打印原始值检查健壮化解析函数
请求超时网络问题 / 频率限制检查网络 / 降低频率加sleep / 用缓存
结果数量突然减少筛选代码变更对比finviz页面更新代码映射
数据字段缺失视图参数不对检查view参数切换到对应视图

7. 筛选器用法的扩展思路

7.1 结合技术指标做二次筛选

finviz的筛选器本身支持一些技术指标,比如RSI、均线、布林带。但这些指标是静态的,只能筛选当前状态。如果你想做更复杂的技术面筛选,比如“过去5天连续上涨”,finviz的筛选器做不到。

这时候可以分两步走:先用finviz筛选出一个基础池,比如市值大于50亿、成交量大于100万的股票;然后拉取这些股票的历史价格数据,用pandas或ta-lib计算技术指标,做二次筛选。这样既利用了finviz的筛选能力,又弥补了它在复杂技术条件上的不足。

7.2 多市场与多板块的横向对比

finviz主要覆盖美股,但也支持一些其他市场的筛选。通过调整筛选参数,可以拉取不同板块、不同行业的数据,做横向对比。比如比较科技板块和医疗板块的估值分布、动量表现。

这个思路在做行业轮动策略的时候特别有用。你可以每周拉一次各板块的筛选结果,计算每个板块的平均市盈率、平均涨跌幅,然后选择估值低、动量强的板块进行配置。

7.3 定时任务与自动化监控

把筛选流程封装成脚本后,下一步就是自动化。用cron或者Windows任务计划,每天定时跑一次,把结果存到数据库,并发送邮件或消息通知。

自动化的时候要注意几点:一是异常处理,如果某天请求失败,要有重试机制;二是日志记录,每次跑完要记录筛选条件、结果数量、耗时,方便排查问题;三是数据版本管理,每次存储的数据要带日期和筛选条件,方便回溯。

我自己的做法是:每天收盘后跑一次筛选,结果存到SQLite,同时生成一个简单的HTML报告,包含候选股票列表和关键指标。报告通过邮件发给自己,早上起来看一眼,心里有数。

这个流程跑顺了之后,你会发现finviz-api不只是一个数据拉取工具,而是一个可以持续运转的选股系统的基础组件。它的价值不在于单次筛选有多强大,而在于你能把它嵌入到一个更大的工作流里,让它每天自动帮你干活。

最后分享一个小技巧:如果你发现某个筛选条件在finviz-api里没有对应的参数,不要急着放弃。先去finviz网站上手动设置这个条件,然后看URL里的“f=”参数变成了什么。把那个代码记下来,直接传给finviz-api的filters列表。大部分情况下,这样都能work。这个“逆向URL”的方法,是我用finviz-api最常用的扩展手段,几乎能覆盖所有筛选条件。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询