finviz-api筛选器高级用法:从URL参数到多因子选股实战 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?v111fcap_largeover,pe_low这里的“v111”是视图参数控制返回表格的列组合。“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的表格视图有多个预设比如v111是默认的概览视图v121是财务视图v131是技术面视图。不同视图返回的列不一样finviz-api解析出来的对象属性也会不同。如果你只关心价格和成交量用默认视图就够了。但如果你想拿到ROE、毛利率、负债率这些财务指标就需要切换到对应的视图。切换视图的方法很简单在初始化Screener的时候传入view参数from finviz.screener import Screener stock_list Screener(filters[...], view121)这里的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], ordermarket_cap, ascendFalse )这样返回的结果就是按市值从大到小排列。分页方面finviz每页默认显示20条记录。如果你的筛选结果超过20条需要翻页。finviz-api提供了get_page方法可以指定页码。但更高效的做法是直接设置每页显示数量。finviz的URL里有一个“r”参数可以控制每页行数最大可以设到100。不过finviz-api没有直接暴露这个参数你需要通过底层请求来设置。一个变通的方法是先用筛选器拿到第一页的结果如果结果数量接近20说明可能还有更多。然后手动构造带“r100”的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_existsappend, indexFalse)这个做法在跑多组筛选条件的时候特别有用因为你可以把不同条件的结果存在同一张表里用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(filtersfilters, ordermarket_cap, ascendFalse)拉取的时候要注意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最常用的扩展手段几乎能覆盖所有筛选条件。