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

Flutter网页爬取遇RangeError(index)索引越界问题求助

解决Dart爬取网页数据时的RangeError(index)问题

开发拉取网站数据的测验应用时,爬取过程中遇到RangeError(index): Invalid value: Valid value range is empty: 0错误。已知页面中items类包含50条数据,尝试改用for循环后问题仍未解决。

问题代码

import 'dart:async';
import 'dart:convert';
import 'package:get/get.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:html/parser.dart' as parser;

import '../../../models/sorular.dart';
import 'bitisekran/bitis_ekrani.dart';

void main() => runApp(sorubir());

class sorubir extends StatefulWidget {
  const sorubir({super.key});
  @override
  State<sorubir> createState() => _sorubirState();
}

class _sorubirState extends State<sorubir> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          leading: BackButton(
            onPressed: () => Navigator.of(context).pop(),
          ),
          title: Text('Güncel Çıkmış Sorular'),
        ),
        body: sorubirIskele(),
      ),
    );
  }
}

class sorubirIskele extends StatefulWidget {
  @override
  State<sorubirIskele> createState() => _sorubirIskeleState();
}

_sorubirIskeleState benimTestim = _sorubirIskeleState();
class _sorubirIskeleState extends State<sorubirIskele> {
  List<Sorular> sorular = [];
  var soruIndex = 0;
  bool isLoading = false;
  var url = Uri.parse('https://www.ehliyettestleri.com/coz/24ha2023e');

  Future getData() async {
    setState(() {
      isLoading = true;
    });
    var res = await http.get(url);
    final String body = utf8.decode(res.bodyBytes);
    final document = parser.parse(body);
    var response = document
        .getElementsByClassName('items')[0]
        .getElementsByClassName('mtq_question mtq_scroll_item-1')
        .forEach((element) {
      setState(() {
        sorular.add(Sorular(
          soruno: element.children[0].text.toString(),
          sorugetir: element.children[1].text.toString(),
          imggetir:
              element.querySelector('p > img')?.attributes['src'].toString(),
          agetir: element.children[2].children[1].children[0].children[1].text
              .toString(),
          bgetir: element.children[2].children[1].children[1].children[1].text
              .toString(),
          cgetir: element.children[2].children[1].children[2].children[1].text
              .toString(),
          dgetir: element.children[2].children[1].children[3].children[1].text
              .toString(),
        ));
      });
      setState(() {
        isLoading = false;
      });
    });
  }

错误原因

该错误是直接访问了空列表的索引导致的。代码中多处使用children[0]、children[1]等硬编码索引,一旦网页DOM结构和预期不符(比如某个节点的子元素为空),就会触发范围错误。另外,getData方法中在forEach内部频繁调用setState,会导致不必要的重绘,且isLoading会被提前设置为false。

修复方案

Future getData() async {
  setState(() {
    isLoading = true;
  });
  try {
    var res = await http.get(url);
    final String body = utf8.decode(res.bodyBytes);
    final document = parser.parse(body);
    
    // 先检查items元素是否存在,避免直接访问[0]报错
    final itemsElement = document.getElementsByClassName('items').firstOrNull;
    if (itemsElement == null) {
      setState(() {
        isLoading = false;
      });
      return;
    }

    final questionElements = itemsElement.getElementsByClassName('mtq_question mtq_scroll_item-1');
    List<Sorular> tempSorular = [];

    for (var element in questionElements) {
      // 检查当前问题节点的子元素数量是否符合预期
      if (element.children.length < 3) continue;

      final soruno = element.children[0].text.trim();
      final sorugetir = element.children[1].text.trim();
      final imggetir = element.querySelector('p > img')?.attributes['src'];

      // 检查选项容器是否存在
      final optionsContainer = element.children[2].children.isNotEmpty ? element.children[2].children[1] : null;
      if (optionsContainer == null || optionsContainer.children.length < 4) continue;

      // 逐个检查选项节点的子元素,避免索引越界
      final agetir = optionsContainer.children[0].children.length > 1 ? optionsContainer.children[0].children[1].text.trim() : '';
      final bgetir = optionsContainer.children[1].children.length > 1 ? optionsContainer.children[1].children[1].text.trim() : '';
      final cgetir = optionsContainer.children[2].children.length > 1 ? optionsContainer.children[2].children[1].text.trim() : '';
      final dgetir = optionsContainer.children[3].children.length > 1 ? optionsContainer.children[3].children[1].text.trim() : '';

      tempSorular.add(Sorular(
        soruno: soruno,
        sorugetir: sorugetir,
        imggetir: imggetir,
        agetir: agetir,
        bgetir: bgetir,
        cgetir: cgetir,
        dgetir: dgetir,
      ));
    }

    // 一次性更新状态,减少重绘次数
    setState(() {
      sorular = tempSorular;
      isLoading = false;
    });
  } catch (e) {
    print('爬取错误详情: $e');
    setState(() {
      isLoading = false;
    });
  }
}

关键优化点

  • 使用firstOrNull替代[0]获取items元素,避免列表为空时直接访问索引报错
  • 用临时列表存储爬取数据,最后一次性调用setState更新状态,减少UI重绘
  • 对每一层节点的子元素数量做检查,确保访问索引时列表不为空
  • 添加try-catch捕获异常,方便调试具体错误原因

内容的提问来源于stack exchange,提问作者Mehmet Hanifi Dağaçan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 10:45:29