Flutter爬取Trenord列车信息遇Cookie弹窗问题求助
解决Flutter跨平台爬取Trenord列车数据的问题
方案1:手动添加Cookie授权标识
网站的Cookie弹窗本质是要求会话中携带特定Cookie值,可通过以下步骤绕过:
- 用浏览器打开目标链接,点击同意Cookie后,在开发者工具的Application标签中找到网站设置的Cookie,记录下类似
cookie_consent=accepted这类核心授权Cookie的键值对。 - 在Flutter的HTTP请求中手动添加这些Cookie和浏览器UA,模拟正常访问:
import 'package:http/http.dart' as http; Future<void> fetchTrainPage() async { final url = Uri.parse('https://www.trenord.it/store/#/en/booking/step1?departure=MILANO%20PORTA%20GARIBALDI&destination=MILANO%20BOVISA%20POLITECNICO&from=20241211&fromstart=08:58'); final response = await http.get( url, headers: { 'Cookie': 'cookie_consent=accepted; 替换为实际抓取到的其他必要Cookie', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' }, ); // 处理返回的页面内容 print(response.body); }
方案2:用WebView跨平台处理Cookie授权
利用Flutter的WebView组件在全平台加载页面,自动处理Cookie弹窗后抓取内容,步骤如下:
- 在
pubspec.yaml添加依赖:
dependencies: webview_flutter: ^4.4.0
- 实现WebView页面,模拟点击Cookie同意按钮并获取HTML:
import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; class TrainScraperPage extends StatefulWidget { const TrainScraperPage({super.key}); @override State<TrainScraperPage> createState() => _TrainScraperPageState(); } class _TrainScraperPageState extends State<TrainScraperPage> { late WebViewController _controller; @override void initState() { super.initState(); _controller = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) ..loadRequest(Uri.parse('https://www.trenord.it/store/#/en/booking/step1?departure=MILANO%20PORTA%20GARIBALDI&destination=MILANO%20BOVISA%20POLITECNICO&from=20241211&fromstart=08:58')) ..addJavaScriptChannel( 'PageScraper', onMessageReceived: (message) { // 拿到完整页面HTML final pageHtml = message.message; // 后续解析列车数据逻辑 print(pageHtml); }, ) ..setNavigationDelegate( NavigationDelegate( onPageFinished: (url) async { // 替换为实际的Cookie同意按钮选择器(用浏览器开发者工具查看) await _controller.runJavaScript('document.querySelector("button.accept-cookie-btn").click();'); // 等待页面更新 await Future.delayed(const Duration(seconds: 2)); // 获取页面HTML并传递到Flutter端 await _controller.runJavaScript('PageScraper.postMessage(document.documentElement.outerHTML);'); }, ), ); } @override Widget build(BuildContext context) { return Scaffold( body: WebViewWidget(controller: _controller), ); } }
注意:需要用浏览器开发者工具查看Cookie同意按钮的CSS选择器,替换代码中的选择器字符串。
方案3:直接调用网站内部API(推荐)
票务网站通常会用API加载列车数据,比爬HTML更稳定:
- 打开浏览器开发者工具的Network标签,刷新目标页面,过滤XHR/fetch请求,找到加载列车列表的API接口。
- 查看该API的请求方法、参数、请求头,直接在Flutter中调用:
import 'package:http/http.dart' as http; import 'dart:convert'; Future<void> fetchTrainDataViaApi() async { // 替换为实际抓取到的API地址 final apiUrl = Uri.parse('https://www.trenord.it/api/booking/get-trains'); final headers = { 'Cookie': 'cookie_consent=accepted', 'Content-Type': 'application/json', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' }; final body = jsonEncode({ 'departure': 'MILANO PORTA GARIBALDI', 'destination': 'MILANO BOVISA POLITECNICO', 'date': '20241211', 'time': '08:58' // 替换为API要求的实际参数 }); final response = await http.post(apiUrl, headers: headers, body: body); final trainData = jsonDecode(response.body); // 直接处理结构化数据 print(trainData); }
内容的提问来源于stack exchange,提问作者Simone Sesana
相关产品推荐
相关产品推荐

