Web Scraping с использованием Flutter: Извлечение данных с веб-сайтов

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

В данной статье будет рассмотрено, как использовать Flutter для извлечения данных с веб-сайтов с помощью техники, известной как веб-скрапинг. Пожалуйста, помните, что некоторые веб-сайты запрещают скрапинг в своих условиях использования. При использовании скрапинга убедитесь, что вы соблюдаете авторские права и условия использования.

Почему я решил описать web scraping?

Я начинающий flutter-разработчик, выполняю мелкие задачи, чтобы поднять уровень своих знаний. Недавно меня попросили получить данные товаров для мобильного приложения. API-запросов не было для получения данных, заказчик попросил использовать web scraping для решения данной проблемы. Хочу поделиться своим решением проблемы. Да, возможно, решение не самое лучшее, поэтому жду замечаний по моему решению.

Подготовка

Перед тем как начать, убедитесь, что у вас установлен Flutter и вы знакомы с основами языка программирования Dart. Необходимо иметь:

  1. Dart SDK version: 2.19.6

  2. 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

  1. Выполним 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) {
            
      }
    }

  2. Далее, используем пакет 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);
      }
    }

  3. Создадим модель 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,
      });
    }

  4. Извлечение данных:

    imageUrl
    // Получим все картинки товаров
    final imageElement = document.querySelector('.product-image-link img');
    final imageUrl = imageElement?.attributes['src'] ?? '';

  5. Перепишем функцию, получим все необходимые данные для товара из карточек и запушим в массив 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['src'] ?? '';
    
            final product = Product(
              name: name,
              model: model,
              description: description,
              imageUrl: imageUrl,
            );
            productList.add(product);
          }
        }
      } catch (e) {
        // Handle error
        print('Error $e');
      }
      return productList;
    }
    

Применение данных в приложении

После извлечения данных вы можете использовать их в вашем приложении.

  1. Как нам обработать состояния асинхронной операции? Для этого во Flutter есть специальный виджет FutureBuilder. В future: пишем наш метод fetchProducts, в котором мы передаем url, а также класс карточки:

    FutureBuilder
    return FutureBuilder<List<Product>>(
      future: fetchProducts('https://ga.com.tm/', '.product-grid-item'),
      ...
    )

  2. В 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,
                            ),
                          ),
                        ),
                      ),
                    ]),
                  ),
                );
              }),
        ),
      ]);
    }

Как это выглядит?

Market
Market

Заключение

В этой статье мы рассмотрели, как использовать Flutter для веб-скрапинга и получения данных с веб-сайтов. С помощью пакетов http и html, вы можете выполнять HTTP-запросы, парсить HTML и извлекать нужную информацию. Надеюсь это статья пригодиться начинающим flutter-разработчикам, а также жду замечаний, если они имеются.

github

Источник: https://habr.com/ru/articles/754042/


Интересные статьи

Интересные статьи

Звание текста с лучшими мемами получила статья про антисоветы для работы с ML-экспериментами. Привет, Хабр! Это уже четвертый выпуск дайджеста по ML и работе с данными для тех, кто тащит эти напр...
Любая компания, деятельность которой связана со сбором, хранением и обработкой персональных данных (ПДн), должна соответствовать требованиям регуляторов. Это необходимо делать как в России, так и Евро...
У многих из нас есть родственники и друзья, которые не очень хорошо разбираются в компьютерах и периодически просят помочь настроить то или иное программное обеспечение. В таких случаях помогать можно...
Если бы у data science существовал собственный зал славы, отдельная часть его нужно было бы посвятить разметке. Памятник ответственным за разметку выглядел бы как атлант, держащий огромный камень, сим...
Большинство IT профессионалов видели бизнес-процессы, в которых пользователям приходилось включать документы, такие как счета-фактуры, в одну систему, а затем повторно вв...