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

Python爬取Finviz数据异常:数值变为NaN求排查

爬虫异常排查:Finviz仅AAPL能获取数据,其余股票字段为NaN

问题背景

今年2月运行爬虫代码时,可正常从Finviz获取股票Sales、Income数据:

Sales     Income
AAPL   365.82B     94.68B
MSFT   184.90B     71.19B
TSLA    53.82B      5.52B
FB     112.33B     40.30B

现在运行代码后,仅AAPL数据正常,其余股票对应字段均为NaN:

Sales     Income
AAPL   365.82B     94.68B
MSFT   NaN          NaN
TSLA   NaN          NaN
FB     NaN          NaN

网站表格结构无变化,以下是原代码:

import pandas as pd
from bs4 import BeautifulSoup as bs
import requests
import numpy as np

# For custom list of stocks, edit this list below, otherwise leave commented out
v1 = ['AAPL','MSFT','TSLA','FB','BRK-B','TSM','NVDA','V','JNJ','JPM','WMT','PG','BAC','HD','BABA','TM','XOM','PFE','DIS','KO']
# Header required to scrape from Finviz
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
           'Upgrade-Insecure-Requests': '1', 'Cookie': 'v2=1495343816.182.19.234.142', 'Accept-Encoding': 'gzip, deflate, sdch',
           'Referer': "http://finviz.com/quote.ashx?t="}
    
# This function is what is used to find the metric of interest and return it
def fundamental_metric(soup, metric):
    return soup.find(text=metric).find_next(class_='snapshot-td2').text
    
# This function iterates through the index of the data frame (stock_list) and uses the fundemental_metric functinon to find the metric on Finviz for that stock
# Any stock in the list that cannot be scraped will return an error before moving on to the next stock
def get_fundamental_data(df):
    for symbol in df.index:
        try:
            #url = ("http://finviz.com/quote.ashx?t=" + symbol.lower())
            r = requests.get("http://finviz.com/quote.ashx?t="+ symbol.lower(),headers=headers)
            soup = bs(r.content,'html.parser')
            for m in df.columns:
                output = fundamental_metric(soup,m)
                df.loc[symbol,m] = output
                df.replace(['-'], np.NaN)
        except Exception as e:
            print (symbol, 'Not Found')
            print(e)
        return df
    
# List of metrics to scrape
# Before adding any metrics, ensure the metric being added is available on Finviz and the name is matched identically
metric = ['Sales','Income']
    
df = pd.DataFrame(index = v1, columns = metric)
df = get_fundamental_data(df)
    
print(df)

问题排查与修复

1. 核心bug:循环提前返回,仅处理第一个股票

get_fundamental_data函数中,return df被放在了for循环内部(try/except块之后)。这导致代码处理完第一个股票(AAPL)就直接返回DataFrame,后续股票完全没被执行,自然保持初始的NaN值。

2. 反爬拦截:Cookie过期

代码中使用的Cookie是固定旧值v2=1495343816.182.19.234.142,这类会话Cookie早已过期。Finviz的反爬机制会拦截无有效会话的请求,返回不完整页面,导致soup.find(text=metric)找不到目标元素,触发异常后数据留空。

3. 次要问题:replace操作未生效

df.replace(['-'], np.NaN)没有设置inplace=True,也未重新赋值给df,所以无法将页面中的-转换为NaN。


修正后的代码

import pandas as pd
from bs4 import BeautifulSoup as bs
import requests
import numpy as np
import time

# 自定义股票列表
v1 = ['AAPL','MSFT','TSLA','FB','BRK-B','TSM','NVDA','V','JNJ','JPM','WMT','PG','BAC','HD','BABA','TM','XOM','PFE','DIS','KO']

# 更新请求头:移除过期Cookie,使用主流浏览器UA
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36',
    'Upgrade-Insecure-Requests': '1',
    'Accept-Encoding': 'gzip, deflate, br',
    'Referer': "http://finviz.com/quote.ashx?t="
}

# 获取指定指标的函数,增加异常捕获
def fundamental_metric(soup, metric):
    try:
        return soup.find(text=metric).find_next(class_='snapshot-td2').text
    except AttributeError:
        return np.NaN

# 批量获取股票数据:循环结束后再返回
def get_fundamental_data(df):
    for symbol in df.index:
        try:
            url = f"http://finviz.com/quote.ashx?t={symbol.lower()}"
            r = requests.get(url, headers=headers)
            r.raise_for_status()  # 检查请求是否成功
            soup = bs(r.content, 'html.parser')
            
            for m in df.columns:
                output = fundamental_metric(soup, m)
                df.loc[symbol, m] = output
            
            # 执行替换操作并生效
            df.replace(['-'], np.NaN, inplace=True)
            # 添加延迟避免反爬
            time.sleep(1)
        except Exception as e:
            print(f"{symbol} 处理失败: {str(e)}")
    # 所有股票处理完再返回
    return df

# 要抓取的指标列表
metrics = ['Sales','Income']

df = pd.DataFrame(index=v1, columns=metrics)
df = get_fundamental_data(df)

print(df)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 08:45:32