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

Android端Java提取Yahoo财经HTML表格数据问题求助

Android端Java提取Yahoo财经财务表格数据方案

问题说明

需提取Yahoo财经某公司(如AAPL)财务板块的表格数据,目标是获取特定值(如2022年9月30日的Total Revenue:394,328,000),并将完整表格转为Map结构。现有代码因页面数据结构变化(原Root.App.main段变为加密/混淆字符串)返回空值,需调整实现方案。

核心问题分析

  1. 页面数据结构变更:Yahoo财经更新了页面渲染逻辑,原root.App.main变量的存储形式或正则匹配规则失效,导致无法提取有效JSON数据。
  2. 原代码缺陷:存在重复变量定义(如requestURL、tableNames),且数据解析路径默认指向季度数据,与目标年度数据不匹配。

解决方案步骤

1. 修正数据提取正则

调整正则表达式,适配Yahoo最新的页面结构,匹配存储财务数据的script标签内容:

// 适配压缩/混淆后的格式,精准匹配root.App.main数据段
String re = "root\\.App\\.main\\s*=\\s*(.*?);\\s*\\}\\(this\\)\\)\\s*;";

若上述正则无法匹配,需查看页面源码确认当前存储数据的变量名(如window.__INITIAL_STATE__),再调整正则规则。

2. 处理压缩/混淆数据

提取到的JSON字符串可能存在压缩或转义字符,直接转换为JSONObject即可自动处理,无需额外解密(Yahoo当前仅做代码压缩,未加密核心数据)。

3. 修正数据解析路径

若目标为年度财务数据,将解析路径从季度数据切换为年度数据:

private JSONArray getTable(JSONObject json) throws JSONException {
    return json.getJSONObject("context")
            .getJSONObject("dispatcher")
            .getJSONObject("stores")
            .getJSONObject("QuoteSummaryStore")
            .getJSONObject("incomeStatementHistory") // 替换原quarterly路径
            .getJSONArray("incomeStatementHistory");
}

4. 转换为目标Map结构

将JSON数据转换为Map<String, Map<String, String>>,外层key为日期,内层key为财务项名称,value为对应数值:

private Map<String, Map<String, String>> convertToMap(JSONArray table, Map<String, Map<String, String>> tableNames) throws JSONException {
    Map<String, Map<String, String>> result = new LinkedHashMap<>();
    String[] dates = getDates(table);

    // 初始化每个日期的财务项Map
    for (String date : dates) {
        result.put(date, new LinkedHashMap<>());
    }

    // 填充每个财务项的数值
    for (Map.Entry<String, Map<String, String>> categoryEntry : tableNames.entrySet()) {
        for (Map.Entry<String, String> itemEntry : categoryEntry.getValue().entrySet()) {
            String itemName = itemEntry.getKey();
            String itemKey = itemEntry.getValue();
            String[] values = getRow(table, itemKey);

            for (int i = 0; i < dates.length; i++) {
                result.get(dates[i]).put(itemName, values[i]);
            }
        }
    }
    return result;
}

完整修正代码

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.*;
import android.text.TextUtils;
import android.util.Log;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class YahooFinanceExtractor {
    private static final String REQUEST_URL = "https://finance.yahoo.com/quote/AAPL/financials?p=AAPL";
    private static final String 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";

    public void extractFinancialData() {
        try {
            Document doc = Jsoup.connect(REQUEST_URL).userAgent(USER_AGENT).get();
            Elements scriptTags = doc.getElementsByTag("script");
            String re = "root\\.App\\.main\\s*=\\s*(.*?);\\s*\\}\\(this\\)\\)\\s*;";
            String data = null;

            for (Element script : scriptTags) {
                Pattern pattern = Pattern.compile(re, Pattern.DOTALL);
                Matcher matcher = pattern.matcher(script.html());
                if (matcher.find()) {
                    data = matcher.group(1);
                    break;
                }
            }

            if (data == null) {
                Log.e("err", "未找到目标数据段");
                return;
            }

            JSONObject jo = new JSONObject(data);
            JSONArray table = getTable(jo);
            Map<String, Map<String, String>> tableNames = getTableNames();
            Map<String, Map<String, String>> financialMap = convertToMap(table, tableNames);

            // 示例:获取2022年9月30日的Total Revenue
            String targetDate = "2022-09-30";
            if (financialMap.containsKey(targetDate)) {
                String totalRevenue = financialMap.get(targetDate).get("Total Revenue");
                Log.d("result", String.format("%s的Total Revenue: %s", targetDate, totalRevenue));
            }

            // 打印完整Map数据
            for (Map.Entry<String, Map<String, String>> entry : financialMap.entrySet()) {
                Log.d("full_data", String.format("日期: %s, 数据: %s", entry.getKey(), entry.getValue().toString()));
            }

        } catch (Exception e) {
            Log.e("err", "提取失败", e);
        }
    }

    private JSONArray getTable(JSONObject json) throws JSONException {
        return json.getJSONObject("context")
                .getJSONObject("dispatcher")
                .getJSONObject("stores")
                .getJSONObject("QuoteSummaryStore")
                .getJSONObject("incomeStatementHistory")
                .getJSONArray("incomeStatementHistory");
    }

    private String[] getRow(JSONArray table, String name) throws JSONException {
        String[] values = new String[table.length()];
        for (int i = 0; i < table.length(); i++) {
            JSONObject jo = table.getJSONObject(i);
            if (jo.has(name)) {
                jo = jo.getJSONObject(name);
                values[i] = jo.has("longFmt") ? jo.get("longFmt").toString() : "-";
            } else {
                values[i] = "-";
            }
        }
        return values;
    }

    private String[] getDates(JSONArray table) throws JSONException {
        String[] values = new String[table.length()];
        for (int i = 0; i < table.length(); i++) {
            values[i] = table.getJSONObject(i).getJSONObject("endDate")
                    .get("fmt").toString();
        }
        return values;
    }

    private Map<String, Map<String, String>> getTableNames() {
        final Map<String, String> revenue = new LinkedHashMap<String, String>() {{
            put("Total Revenue", "totalRevenue");
            put("Cost of Revenue", "costOfRevenue");
            put("Gross Profit", "grossProfit");
        }};
        final Map<String, String> operatingExpenses = new LinkedHashMap<String, String>() {{
            put("Research Development", "researchDevelopment");
            put("Selling General and Administrative", "sellingGeneralAdministrative");
            put("Non Recurring", "nonRecurring");
            put("Others", "otherOperatingExpenses");
            put("Total Operating Expenses", "totalOperatingExpenses");
            put("Operating Income or Loss", "operatingIncome");
        }};
        return new LinkedHashMap<String, Map<String, String>>() {{
            put("Revenue", revenue);
            put("Operating Expenses", operatingExpenses);
        }};
    }

    private Map<String, Map<String, String>> convertToMap(JSONArray table, Map<String, Map<String, String>> tableNames) throws JSONException {
        Map<String, Map<String, String>> result = new LinkedHashMap<>();
        String[] dates = getDates(table);

        for (String date : dates) {
            result.put(date, new LinkedHashMap<>());
        }

        for (Map.Entry<String, Map<String, String>> categoryEntry : tableNames.entrySet()) {
            for (Map.Entry<String, String> itemEntry : categoryEntry.getValue().entrySet()) {
                String itemName = itemEntry.getKey();
                String itemKey = itemEntry.getValue();
                String[] values = getRow(table, itemKey);

                for (int i = 0; i < dates.length; i++) {
                    result.get(dates[i]).put(itemName, values[i]);
                }
            }
        }
        return result;
    }
}

注意事项

  • User-Agent适配:需使用符合现代浏览器的UA字符串,避免被Yahoo拦截。
  • 数据结构变更:Yahoo财经可能随时调整页面数据结构,若再次失效需重新查看页面源码更新正则和解析路径。
  • 异常处理:需增加空值判断和异常捕获,避免因数据缺失导致崩溃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 07:05:24