Reactor 3.x (Java): для парсинга веб-страниц

Reactor noob здесь.

Это скорее вопрос HowTo.

Допустим, у меня есть веб-сайт, который я хочу очистить, содержащий разбитый на страницы набор результатов поиска. Количество страниц результатов поиска неизвестно. На каждой странице поиска есть ссылка на следующую страницу. Я хочу очистить все результаты поиска со всех страниц и обработать каждый результат поиска.

Как я могу использовать Reactor (Mono / Flux) для этого на Java?

Я хочу сделать это как можно более «реактивно».

По сути, это версия Reactor (3.x) следующего императивного псевдокода:

    String url = "http://example.com/search/1";
    Optional<Document> docOp = getNextPage(url);    (1)
    while (docOp.isPresent()) {
        Document doc = docOp.get();
        processDoc(doc);                            (2)
        docOp = getNextPage(getNextUrl(doc));       (3)
    }

    // (1) Get the first page of search results
    // (2) Process all the search results on this page asynchronously
    // (3) Find the next page URL, and get that page

person Erik    schedule 02.02.2017    source источник


Ответы (1)


С некоторой помощью https://gitter.im/reactor/reactor я пришел к этому решению. Это может быть не идеально. Я хотел бы получить отзывы о любых проблемах, которые могут возникнуть.

public void scrape() {

    Try<Document> firstDocTry = this.getSearchResultsPage(Option.<Document>none().toTry());    (1)

    // Generate a flux where each element in the flux is created using the current element
    Flux.<Try<Document>, Try<Document>>generate(() -> firstDocTry, (docTry, sink) -> {         (2)
            docTry = this.getSearchResultsPage(docTry);
            docTry.isFailure() ? sink.complete() : sink.next(docTry);
            return docTry;
        })
        .flatMap(docTry -> this.transformToScrapedLoads(docTry))                               (3)
        .log()
        .subscribe(scrapedLoad ->
            scrapedLoadRepo.save(scrapedLoad)                                                  (4)
        );
}

protected Try<Document> getSearchResultsPage(Try<Document> docTry) {
    ...
}

protected Flux<ScrapedLoad> transformToScrapedLoads(Try<Document> docTry) {
    ...
}

(1) Использование здесь монадического метода Try and Option Javaslang. 'firstDocTry' Заполняет генератор. getSearchResultsPage () знает, что начать поиск с первой страницы, если документ не предоставлен.

(2) Здесь используется генератор. Каждый элемент, опубликованный в Flux, определяется предыдущим элементом

(3) метод преобразования преобразует каждый документ в поток, которые объединяются и отправляются для подписки как один поток.

(4) Абонент работает с каждым элементом, созданным потоком. В этом случае сохраняются их.

person Erik    schedule 03.02.2017