23
Дек
2016

Ревью по java коду, использующему стримы (выборка ключевых слов из вакансий)

Ссылка на код единственный класс Main и результат работы result.txt https://github.com/Generalus/Java-HeadHunter-Keywords/tree/master/src/ru/thesn/app

package ru.thesn.app;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;


public class Main {

    private static final String HEADHUNTER_URL_TEMPLATE = "https://m.hh.ru/vacancies?text=java&page=";

    private static int IO_ERRORS_COUNT = 0;

    private static final Pattern DESCRIPTION_SPLIT_PATTERN = Pattern.compile("[^A-Za-z0-9 ]|( and )|( or )");
    private static final Pattern DESCRIPTION_REPLACE_PATTERN = Pattern.compile("</?.*?>");
    private static final Pattern KEYWORD_NUMBER_PATTERN = Pattern.compile("\\d+");

    public static void main(String[] args) {

        IntStream
                .rangeClosed(0, 99)
                .mapToObj(i -> HEADHUNTER_URL_TEMPLATE + i)
                .map(Main::getDocument)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .map(d -> d.getElementsByClass("vacancy-list-item-link"))
                .flatMap(Collection::stream)
                .map(d -> d.attr("href"))
                .distinct()

                // стрим всех уникальных ссылок на вакансии
                .map(Main::getDocument)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .map(Main::getEntryDescription)

                // стрим всех текстов из вакансий
                .filter(Main::isRussianText)
                .map(DESCRIPTION_SPLIT_PATTERN::split)
                .flatMap(Arrays::stream)
                .map(String::trim)
                .filter(s -> s.length() > 1)
                .filter(s -> !KEYWORD_NUMBER_PATTERN.matcher(s).matches())
                .map(String::toLowerCase)

                // стрим ключевых слов (как одиночных, так и составных): подсчитаем их количество и выведем на экран
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
                .entrySet()
                .stream()
                .sorted(Map.Entry.comparingByValue(Collections.reverseOrder()))
                .limit(2000)
                .forEach(e -> System.out.printf("%s - %s%n", e.getKey(), e.getValue()));


        System.out.println("\nIO errors: " + IO_ERRORS_COUNT);
    }



    private static Optional<Document> getDocument(String url){
        Optional<Document> document = Optional.empty();
        try {
            document = Optional.of(Jsoup.connect(url).get());
        } catch (IOException e) {
            IO_ERRORS_COUNT++;
        }
        return document;
    }

    private static boolean isRussianText(String text) {
        long rusLettersCount = text
                .chars()
                .filter(i -> i >= 'а' && i <= 'я')
                .count();
        return (text.length() - rusLettersCount) / (text.length() * 1.) < 0.5;
    }

    private static String getEntryDescription(Document document) {
        String description = document.getElementsByClass("vacancy__description").first().html();
        return DESCRIPTION_REPLACE_PATTERN.matcher(description).replaceAll("");
    }

}

Что код делает:

Выводит примерный, отсортированный по частоте упоминания набор ключевых слов, оставляемых рекрутерами российских компаний, которым требуются java-разработчики. Ключевым словом считается последовательность латинских букв (и цифр) в кириллице, возможно, разделенная пробелами.

Что хотелось бы уточнить по коду:

  • Легально ли подключение в непараллельном стриме к удаленному ресурсу? Или стримы предназначены все же для более легких операций?
  • Стримы в джаве относительно новая вещь, и я не нашел никаких правил по оформлению кода. Какие в выбранном мной варианте недостатки?
  • Простой вариант поддержать выборку всех слов, содержащих дефис, даже смесь кириллицы и латиницы, к примеру таких: front-энд?
  • Что делать с числами в итоговой выборке?
  • Составные слова (к примеру, "spring framework"), считаются отдельно от простых, в данном случае, простым является "spring". Можно ли легко учитывать при подсчете простых слов их вхождения в сложные?
  • Любые предложения по коду...

Недостатки, которые вижу сам (их можно не учитывать при ревью)

  • Ручной парсинг, вместо использования API HH.
  • Необходимость вручную добавлять JSoup, вместо автоматического выкачивания с mvn репозитория.

Источник: https://ru.stackoverflow.com/questions/606898/%D0%A0%D0%B5%D0%B2%D1%8C%D1%8E-%D0%BF%D0%BE-java-%D0%BA%D0%BE%D0%B4%D1%83-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D1%8E%D1%89%D0%B5%D0%BC%D1%83-%D1%81%D1%82%D1%80%D0%B8%D0%BC%D1%8B-%D0%B2%D1%8B%D0%B1%D0%BE%D1%80%D0%BA%D0%B0-%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%B2%D1%8B%D1%85-%D1%81%D0%BB%D0%BE%D0%B2-%D0%B8%D0%B7-%D0%B2%D0%B0%D0%BA%D0%B0%D0%BD%D1%81%D0%B8%D0%B9

Тебе может это понравится...

Добавить комментарий