В данной статье будет рассмотрено, как использовать Flutter для извлечения данных с веб-сайтов с помощью техники, известной как веб-скрапинг. Пожалуйста, помните, что некоторые веб-сайты запрещают скрапинг в своих условиях использования. При использовании скрапинга убедитесь, что вы соблюдаете авторские права и условия использования.
Почему я решил описать web scraping?
Я начинающий flutter-разработчик, выполняю мелкие задачи, чтобы поднять уровень своих знаний. Недавно меня попросили получить данные товаров для мобильного приложения. API-запросов не было для получения данных, заказчик попросил использовать web scraping для решения данной проблемы. Хочу поделиться своим решением проблемы. Да, возможно, решение не самое лучшее, поэтому жду замечаний по моему решению.
Подготовка
Перед тем как начать, убедитесь, что у вас установлен Flutter и вы знакомы с основами языка программирования Dart. Необходимо иметь:
Dart SDK version: 2.19.6
Flutter 3.7.12
Для выполнения HTTP-запросов и парсинга HTML мы будем использовать пакеты http
и html
соответственно. Добавьте их в файл pubspec.yaml
:
pubspec.yaml
dependencies:
flutter:
sdk: flutter
http: ^0.13.3
html: ^0.15.0
Работа с web scraping
-
Выполним GET-запрос к веб-сайту, с которого мы хотим извлечь данные. Я использую пакет
http
для этой цели:fetchProducts
import 'package:http/http.dart' as http; void fetchProducts(String url) async { final response = await http.get(Uri.parse(url)); if (response.statusCode == 200) { } }
-
Далее, используем пакет
html
для парсинга HTML-контента, полученного из запроса:parser
import 'package:html/parser.dart' as parser; import 'package:http/http.dart' as http; void fetchProducts(String url) async { final response = await http.get(Uri.parse(url)); if (response.statusCode == 200) { final document = parser.parse(response.body); } }
-
Создадим модель Product:
Product
class Product { String name; String model; String description; String imageUrl; Product({ required this.name, required this.model, required this.description, required this.imageUrl, }); }
-
Извлечение данных:
imageUrl
// Получим все картинки товаров final imageElement = document.querySelector('.product-image-link img'); final imageUrl = imageElement?.attributes['data-src'] ?? '';
-
Перепишем функцию, получим все необходимые данные для товара из карточек и запушим в массив
productList:
Product.dart
import 'package:html/parser.dart' as parser; import 'package:http/http.dart' as http; class Product { String name; String model; String description; String imageUrl; Product({ required this.name, required this.model, required this.description, required this.imageUrl, }); } Future<List<Product>> fetchProducts(String url, String cardWithDot) async { List<Product> productList = []; try { final response = await http.get(Uri.parse(url)); if (response.statusCode == 200) { final document = parser.parse(response.body); final cards = document.querySelectorAll(cardWithDot); for (final card in cards) { final name = card.querySelector('.wd-entities-title')?.text ?? ''; final model = card.querySelector('.price')?.text ?? ''; final description = card.querySelector('.phone-description')?.text ?? ''; final imageElement = card.querySelector('.product-image-link img'); final imageUrl = imageElement?.attributes['data-src'] ?? ''; final product = Product( name: name, model: model, description: description, imageUrl: imageUrl, ); productList.add(product); } } } catch (e) { // Handle error print('Error $e'); } return productList; }
Применение данных в приложении
После извлечения данных вы можете использовать их в вашем приложении.
-
Как нам обработать состояния асинхронной операции? Для этого во Flutter есть специальный виджет
FutureBuilder
. Вfuture:
пишем наш методfetchProducts
, в котором мы передаем url, а также класс карточки:FutureBuilder
return FutureBuilder<List<Product>>( future: fetchProducts('https://ga.com.tm/', '.product-grid-item'), ... )
-
В
builder
делаем проверки для построения данных и само отображение данных:builder
if (snapshot.hasError) { return Center(child: Text('Error: ${snapshot.error}')); } else { final productList = snapshot.data!; return Column(children: [ SizedBox( width: MediaQuery.of(context).size.width, child: const Padding( padding: EdgeInsets.only(top: 8, left: 20), child: Text( 'Новинки', style: TextStyle( fontSize: 25, fontWeight: FontWeight.w600, letterSpacing: 1, wordSpacing: 2, ), ), ), ), SizedBox( width: MediaQuery.of(context).size.width, child: GridView.builder( physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, padding: const EdgeInsets.only( left: 10, right: 10, top: 10, bottom: 10), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisSpacing: 12, crossAxisSpacing: 12, mainAxisExtent: 290), itemCount: productList.length, itemBuilder: (context, index) { final product = productList[index]; return InkWell( splashColor: Colors.transparent, highlightColor: Colors.transparent, onTap: () => { // }, child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), color: Theme.of(context).primaryColor), child: Column(children: [ Container( height: 175, decoration: BoxDecoration( borderRadius: const BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12)), color: Colors.white, image: DecorationImage( image: NetworkImage( product.imageUrl), fit: BoxFit.cover)), ), //title Padding( padding: const EdgeInsets.only( left: 5, top: 5, right: 5), child: SizedBox( height: 60, width: MediaQuery.of(context).size.width, child: Text( product.name, //phoneDataList[index].name, textAlign: TextAlign.start, overflow: TextOverflow.ellipsis, maxLines: 3, style: const TextStyle( fontWeight: FontWeight.bold, ), ), ), ), ]), ), ); }), ), ]); }
Как это выглядит?
Заключение
В этой статье мы рассмотрели, как использовать Flutter для веб-скрапинга и получения данных с веб-сайтов. С помощью пакетов http
и html
, вы можете выполнять HTTP-запросы, парсить HTML и извлекать нужную информацию. Надеюсь это статья пригодиться начинающим flutter-разработчикам, а также жду замечаний, если они имеются.
Chalokian
А зачем для парсинга flutter?
glebanya13 Автор
Я расписал подход, как получить данные с помощью web scraping. Я считаю, что правильнее получать данные по API и работать с ними.