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

Kotlin异步数据处理乱序及索引越界问题排查求助

数据爬取乱序与索引越界问题解决

核心问题原因

  • HashMap无序导致表头与数据不匹配:你用HashMap<String, ArrayList<String>>存储表头列,HashMap的key是无序的,dataMap.keys.toList()得到的顺序和HTML表头的实际顺序完全随机,这直接导致后续单元格数据被塞到错误的列里。
  • 循环索引越界:for (i in 0..row.getElementsByTag("td").size) 是闭区间循环,假设一行有n个td,i会从0取到n,而td数组的索引范围是0到n-1,当i=n时就会触发索引越界,同时也会多执行一次循环,导致最后一个key被重复赋值空数据。
  • 异步并行度限制无效:你的limitedParallelism(1)根本没解决问题,因为openinsiderGetData是同步执行的,异步只是把它放到后台线程跑,和数据乱序完全无关。

修正后的代码

修复数据爬取方法

private fun openinsiderGetData(): DataFrame<Any> {
    val doc = Jsoup.connect("http://openinsider.com/search?q=TSLA").get()
    val table = doc.select(".tinytable")
    // 用LinkedHashMap保证表头顺序和HTML里的一致
    val dataMap = LinkedHashMap<String, ArrayList<String>>()

    // 提取表头并按顺序存入LinkedHashMap
    table.select("thead tr").forEach { row ->
        row.getElementsByTag("th").forEach { cell ->
            dataMap[cell.text()] = ArrayList()
        }
    }
    val keys = dataMap.keys.toList() // 现在keys顺序和表头完全一致
    Log.d("Keys", keys.toString())

    // 处理表体数据
    table.select("tbody tr").forEach { row ->
        val cells = row.getElementsByTag("td")
        var index = 0
        // 用until代替..,避免索引越界(0到cells.size-1)
        for (i in 0 until cells.size) {
            if (index >= keys.size) break // 额外防护,避免表头和列数不匹配
            val key = keys[index]
            val cellText = cells[i].text()
            Log.d("Key", key)
            Log.d("CELL TEXT:", cellText)
            dataMap[key]?.add(cellText)
            index += 1
            Log.d("DATA SO FAR", dataMap.toString())
        }
    }
    Log.d("Map:", dataMap.toString())

    val df = emptyDataFrame<Any>()
    // 按表头顺序添加列,保证DataFrame列顺序正确
    for (key in keys) {
        val column = columnOf(dataMap[key]) named key
        df.add(column)
    }
    Log.d("Dataframe:", df.toString())

    return df
}

简化异步处理(并行度限制可移除)

private fun setupData() {
    lifecycleScope.launch {
        withContext(Dispatchers.Default) {
            data = openinsiderGetData()
            val rows = data.values(byRows=true)
            Log.d("ROWS:", rows.toString())
        }
        // 回到主线程绘制表格
        withContext(Dispatchers.Main) {
            // TODO: 绘制表格的逻辑
        }
    }
}

额外说明

  • 用LinkedHashMap替代HashMap是关键,它会保留键的插入顺序,也就是HTML表头的顺序,这样每个单元格数据都会对应到正确的列。
  • 循环里的0 until cells.size确保不会访问超出数组范围的索引,解决越界问题。
  • 最后构建DataFrame时用keys(表头顺序列表)遍历,而不是dataMap.keys,保证DataFrame的列顺序和原始表格一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 15:26:20