如何在Flutter中先执行网页操作再抓取目标数据?
Flutter模拟网页操作并抓取数据方案
方法一:直接分析接口发送请求(推荐,效率更高)
普通GET请求没法处理表单提交,你需要先抓包分析网站的搜索接口,再用http库模拟提交:
抓包分析请求
- 打开浏览器按F12进入开发者工具,切换到Network标签
- 在网页上填写出发地、到达地、日期,点击搜索按钮
- 找到触发的POST请求,查看请求URL、请求头(Headers)和表单数据(Form Data)
- 注意:有些网站用城市编码而非名称,表单参数要和抓包结果完全一致
Flutter代码实现
import 'package:http/http.dart' as http; import 'package:html/parser.dart' as parser; import 'package:html/dom.dart' as dom; Future<void> fetchSearchData() async { // 替换成抓包得到的搜索接口URL final requestUrl = Uri.parse('https://目标网站的搜索接口'); try { final response = await http.post( requestUrl, headers: { // 复制抓包得到的请求头,至少带User-Agent避免被拦截 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', 'Referer': 'https://目标网站首页URL', }, body: { // 替换成抓包得到的表单参数 'departure': '出发地编码', 'destination': '到达地编码', 'travel_date': '2024-05-20', 'submit': '搜索', }, ); if (response.statusCode == 200) { // 解析返回的HTML内容 final document = parser.parse(response.body); // 根据网页结构用选择器提取数据 final resultItems = document.querySelectorAll('.result-card'); for (var item in resultItems) { final route = item.querySelector('.route-name')?.text.trim(); final price = item.querySelector('.price-tag')?.text.trim(); print('路线:$route,价格:$price'); // 把数据存入你的Flutter数据模型 } } else { print('请求失败,状态码:${response.statusCode}'); } } catch (e) { print('请求出错:$e'); } }
方法二:用WebView模拟用户操作(适合新手,无需复杂抓包)
如果不想分析接口,可以用webview_flutter库模拟手动填写表单、点击按钮,再提取数据:
- 添加依赖
在pubspec.yaml中添加:
dependencies: webview_flutter: ^4.4.0
- Flutter代码实现
import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'dart:convert'; class WebScrapePage extends StatefulWidget { const WebScrapePage({super.key}); @override State<WebScrapePage> createState() => _WebScrapePageState(); } class _WebScrapePageState extends State<WebScrapePage> { late WebViewController _webController; @override void initState() { super.initState(); _webController = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) ..loadRequest(Uri.parse('https://目标网站URL')) // 建立JS和Flutter的通信通道,传递抓取到的数据 ..addJavaScriptChannel( 'DataChannel', onMessageReceived: (JavaScriptMessage msg) { final List<dynamic> data = jsonDecode(msg.message); // 处理数据,比如更新UI或存入本地 for (var item in data) { print('路线:${item['route']},价格:${item['price']}'); } }, ) ..setNavigationDelegate( NavigationDelegate( onPageFinished: (url) { // 页面加载完成后,执行JS填写表单并点击搜索 _webController.runJavaScript(''' // 根据网页实际元素ID/class修改选择器 document.getElementById('departure-input').value = '北京'; document.getElementById('destination-input').value = '上海'; document.getElementById('date-picker').value = '2024-05-20'; document.querySelector('.green-search-btn').click(); '''); // 等待搜索结果加载,可根据实际情况调整延迟时间 Future.delayed(const Duration(seconds: 3), () { // 执行JS提取数据并传给Flutter _webController.runJavaScript(''' const items = document.querySelectorAll('.result-item'); const resultData = Array.from(items).map(item => ({ route: item.querySelector('.route-title').textContent.trim(), price: item.querySelector('.price').textContent.trim() })); DataChannel.postMessage(JSON.stringify(resultData)); '''); }); }, ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('模拟抓取数据')), body: WebViewWidget(controller: _webController), ); } }
新手注意事项
- 抓包时要注意网站是否有反爬机制,比如需要携带Cookie或CSRF Token,这类参数需要先从首页请求中获取再提交
- 使用WebView时,要确保网页元素的选择器(id/class)和实际网页一致,可通过浏览器F12查看元素
- Android上如果访问HTTP网站,需要在
AndroidManifest.xml中添加android:usesCleartextTraffic="true";iOS上需要在Info.plist中配置NSAppTransportSecurity
内容的提问来源于stack exchange,提问作者Huseyin A.
相关产品推荐
相关产品推荐

