Skip to content
This repository has been archived by the owner on Mar 11, 2023. It is now read-only.

Latest commit

 

History

History
135 lines (99 loc) · 9.07 KB

README.md

File metadata and controls

135 lines (99 loc) · 9.07 KB

Sparrow.Parsing.Utils

Репозиторий содержит в себе все необходимые (мне) структуры и классы для возможности парсинга HTML-верстки

Introduction

Весь парсинг построен на принципе конвейерной обработки странички, где каждый обработчик в конвейере независимо обрабатывает исходную HTML-страницу и инициализирует необходимые поля итогового типа. Достоинством, что я хочу отметить, данного подхода является то, что благодаря изолированной модели обработки исходной страницы можно просто переключаться и тестировать отдельные модули данного конвейера. Также, данный способ призван оптимизировать процесс отладки программного кода с целью выявления ошибок.

Roadmap

  • IParsingSource - возможность авторизации, если необходимо и возможность работы с Cookies.
  • ParsingPipeline - в этом конвейере собираются все промежуточные обработчики (ParsingMiddleware), которые обрабатывают определённый участок данных, за который они ответственны.
  • ParsingMiddleware - промежуточный обработчик данных из источника. Принцип работы основан на принципе устройства Middlewares в ASP.NET Core. Отвечает за обработку определенной информации из данных с источника. Для передачи определенных данных в другие ParsingMiddleware использовать можно механизм Dependency Injection.
  • MiddlewareContext - контекст Middleware-обработчика. В нем хранятся:
    • Экземпляр источника (IParsingSource), с которого происходит непосредственное получение данных;
    • Экземпляр ParsingMiddleware, являющийся следующим в очереди обработки данных;
    • Экземпляр IServiceCollection, необходимый для передачи в другие Middlewares зарегистрированных зависимостей;
    • Экземпляр IServiceProvider, служащий для доступа к зарегистрированным сервисам.

.NET Core Dependencies

  • Microsoft.Extensions.Hosting → v6.0.1

Usage

var source = new MicrosoftSource("https://www.microsoft.com/ru-ru/");
var pipe = new ParsingPipeline<MicrosoftEntity, MicrosoftSource>(source)
            .Use<InitializerMiddleware>()
            .Use<NewsParsingMiddleware>()
            .Use<ProductsParsingMiddleware>();
var resultEntity = await pipe.StartAsync();
Console.WriteLine(resultEntity);

Во-первых, создается источник данных, откуда будет происходить получение, извиняюсь, данных. Источник должен реализовывать интерфейс IParsingSource или костомный ITextParsing.

Во-вторых, регистрируется обрабатываемого типа (в примере: MicrosoftEntity). Данный тип будет создан в объекте класса ParsingPipeline (для этого он должен иметь хотя бы один пустой конструктор).

В-третьих создается конвейер ParsingPipeline, можно не утруждаться и не писать свой - этого хватит с лихвой. Через метод Use<T>() указываются все пользовательские Middlewares, наследуемые от класса ParsingMiddleware. Экземпляры указанных типов будут инициализированы внутри ParsingPipeline.

В-четвертых, для обработки данных потребуются, непосредственно, сами обработчики, о которых упоминалось ранее. Обработчики (ParsingMiddleware'ы) должны реализовать абстрактный метод ProcessAsync(), одним параметром которого является обрабатываемый тип (в примере: MicrosoftEntity). В каждом обработчике можно также переопределить метод Process(TResult). По умолчанию он реализуется следующим образом:

public virtual void Process(TResult toProcess) => 
		ProcessAsync(toProcess).ConfigureAwait(false);
public abstract Task ProcessAsync(TResult toProcess);

Features 1.0 & 1.1

И наконец то появилась возможность внедрения зависимостей в собственные ParsingMiddlewares

private static async Task<int> Main()
{
    ConfigureLogger();
    var pipe = new ParsingPipeline<List<MeMangaItem>, MeSource>()
        .HandleAll<ExceptionHandleMiddleware>()
        .Use<InitializeMiddleware>()
        .Use<PagesParsingMiddleware>()
        .Use<PreviewsParsingMiddleware>()
        .Use<MangaParsingMiddleware>()
        .Use<FilesParsingMiddleware>()
        .OnHostBuilding(host => host.UseSerilog())
        .WithServices(services => 
		{
			services.AddSingleton<HttpClientWrapper>();
			services.AddSingleton(permission => GetAccessPermission());
		});
    var result = await pipe.StartAsync();
    if (result.Status == ExecutionStatus.Ok)
        return 0;
    else return 1;
}
internal class InitializeMiddleware : ParsingMiddleware<List<MeMangaItem>, MeSource>
{
    public InitializeMiddleware(IConfiguration config) =>
        _config = config;

    private readonly IConfiguration _config;

    public override async Task ProcessAsync(List<MeMangaItem> toProcess)
    {
        var helper = Context.ServiceProvider.GetService<QueryHelper>();
        await Context.Source.AuthorizeAsync();

        Context.Services.AddSingleton<IHtmlParser, HtmlParser>();

        await InvokeNextAsync(toProcess);
    }
}

Для внедрения зависимостей используется метод OnHostBuilding() и WithServices(). Для того, чтобы пользоваться DI внутри middlewares необходимо зависимости внедрять внутри самих Middlewares, используя:

Context.Services.AddSingleton<IService, Service>();

Поскольку используются 2 разных ServiceContainer при инициализации Middleware и при использовании внутри них.

Также после выполнения парсинга в результате выполнения pipe.StartAsync() будет получен объект типа PipelineExecutionResult<TResult>, имеющий следующую структуру:

public class PipelineExecutionResult<TResult>
{
    internal PipelineExecutionResult() { }

    public TResult Content { get; internal set; }
    public ExecutionStatus Status { get; internal set; }
}

Используя для валидации результирующих данных, воспользуйтесь ExecutionStatus

ExecutionStatus Ok;
ExecutionStatus HandleError;
ExecutionStatus NotHandleError;

Он сообщит, по какой причине был получен результат и было ли это обработано, либо же нет.

Feature 2.0 - in process

Не стоит забывать, что главной идеей данной библиотеки, в первую очередь, является возможность обеспечение удобства выгрузки контента с сайтов, файлов и чего угодно. И одним из аспектов, которые этому препятствуют является обработка ошибок. Чтобы не раздувать код в Ваших посредниках-обработчиках я думаю над возможностью обработки полученных в результате работы исключений, которые могут быть обработаны другими Middlewares, либо же обработчиком, в котором произошло само исключение

References