Как «отсканировать» сайт (или страницу) на предмет информации и включить его в мою программу? - PullRequest
51 голосов
/ 14 мая 2010

Ну, я в значительной степени пытаюсь понять, как извлечь информацию с веб-страницы и перенести ее в мою программу (на Java).

Например, если я знаю точную страницу, с которой я хочу получить информацию, для простоты - страницу товара Best Buy, как я могу получить необходимую информацию с этой страницы? Понравилось название, цена, описание?

Как бы этот процесс вообще назывался? Я понятия не имею, должны были даже начать исследовать это.

Edit: Хорошо, я запускаю тест для JSoup (тот, который опубликовал BalusC), но я продолжаю получать эту ошибку:

Exception in thread "main" java.lang.NoSuchMethodError: java.util.LinkedList.peekFirst()Ljava/lang/Object;
at org.jsoup.parser.TokenQueue.consumeWord(TokenQueue.java:209)
at org.jsoup.parser.Parser.parseStartTag(Parser.java:117)
at org.jsoup.parser.Parser.parse(Parser.java:76)
at org.jsoup.parser.Parser.parse(Parser.java:51)
at org.jsoup.Jsoup.parse(Jsoup.java:28)
at org.jsoup.Jsoup.parse(Jsoup.java:56)
at test.main(test.java:12)

У меня есть Apache Commons

Ответы [ 10 ]

92 голосов
/ 14 мая 2010

Используйте анализатор HTML, например Jsoup . Это мое предпочтение перед другими парсерами HTML, доступными в Java , поскольку он поддерживает jQuery , как CSS-селекторы . Кроме того, его класс, представляющий список узлов, Elements, реализует Iterable, так что вы можете перебирать его в , расширенном для цикла (так нет необходимости возиться с подробными Node и NodeList классами в среднем анализаторе Java DOM).

Вот основной базовый пример (просто поместите последний файл JAR Jsoup в classpath):

package com.stackoverflow.q2835505;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class Test {

    public static void main(String[] args) throws Exception {
        String url = "https://stackoverflow.com/questions/2835505";
        Document document = Jsoup.connect(url).get();

        String question = document.select("#question .post-text").text();
        System.out.println("Question: " + question);

        Elements answerers = document.select("#answers .user-details a");
        for (Element answerer : answerers) {
            System.out.println("Answerer: " + answerer.text());
        }
    }

}

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

10 голосов
/ 14 мая 2010

Это называется очисткой экрана, в Википедии есть статья о более конкретном просмотре веб-страниц . Это может быть серьезной проблемой, потому что есть какой-то уродливый, испорченный, сломанный, если не для браузера, умный HTML, так что удачи.

6 голосов
/ 14 мая 2010

Я бы использовал JTidy - это похоже на JSoup, но я плохо знаю JSoup. JTidy обрабатывает неработающий HTML и возвращает документ w3c, так что вы можете использовать его в качестве источника для XSLT для извлечения контента, который вам действительно интересен. Если вы не знаете XSLT, то вы можете использовать JSoup в качестве документа с моделью лучше работать, чем с w3c.

РЕДАКТИРОВАТЬ: быстрый просмотр на сайте JSoup показывает, что JSoup действительно может быть лучшим выбором. Кажется, он поддерживает CSS-селекторы из коробки для извлечения материала из документа. С этим гораздо проще работать, чем с XSLT.

4 голосов
/ 14 мая 2010

Вы можете использовать html-парсер (много полезных ссылок здесь: java html parser ).

Процесс называется «захват контента сайта». Ищите «захватить содержимое сайта Java» для дальнейшего расследования.

3 голосов
/ 19 сентября 2011

jsoup поддерживает Java 1.5

https://github.com/tburch/jsoup/commit/d8ea84f46e009a7f144ee414a9fa73ea187019a3

похоже, что этот стек был ошибкой и был исправлен

2 голосов
/ 12 июля 2012

Вы также можете попробовать JARVEST .

Он основан на JRuby DSL поверх чисто Java-движка для преобразования веб-сайтов.

Пример

Найти все ссылки внутри веб-страницы (wget и xpath являются конструкциями языка jARVEST):

wget | xpath('//a/@href')

Внутри Java-программы:

Jarvest jarvest = new Jarvest();
  String[] results = jarvest.exec(
    "wget | xpath('//a/@href')", //robot! 
    "http://www.google.com" //inputs
  );
  for (String s : results){
    System.out.println(s);
  }
2 голосов
/ 14 мая 2010

Решение JSoup отлично, но если вам нужно извлечь что-то действительно простое, может быть проще использовать regex или String.indexOf

Как уже упоминали другие, этот процесс называется очисткой

2 голосов
/ 14 мая 2010

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

Может быть неудобно в Java, если нет классов XML, похожих на классы, найденные в System.XML.Linq в C #.

1 голос
/ 26 июля 2018

Мой ответ, вероятно, не будет полезен для автора этого вопроса (я опаздываю на 8 месяцев, так что я не думаю, что это правильное время), но я думаю, что он, вероятно, будет полезен для многих других разработчиков, которые могут встретить этот ответ.

Сегодня я только что выпустил (от имени моей компании) полный фреймворк HTML to POJO, который вы можете использовать для отображения HTML на любой класс POJO с помощью нескольких аннотаций. Сама библиотека довольно удобна и в то же время содержит множество других функций, в то же время будучи очень подключаемой. Вы можете посмотреть на это прямо здесь: https://github.com/whimtrip/jwht-htmltopojo

Как использовать: Основы

Представьте, что нам нужно проанализировать следующую HTML-страницу:

<html>
    <head>
        <title>A Simple HTML Document</title>
    </head>
    <body>
        <div class="restaurant">
            <h1>A la bonne Franquette</h1>
            <p>French cuisine restaurant for gourmet of fellow french people</p>
            <div class="location">
                <p>in <span>London</span></p>
            </div>
            <p>Restaurant n*18,190. Ranked 113 out of 1,550 restaurants</p>  
            <div class="meals">
                <div class="meal">
                    <p>Veal Cutlet</p>
                    <p rating-color="green">4.5/5 stars</p>
                    <p>Chef Mr. Frenchie</p>
                </div>

                <div class="meal">
                    <p>Ratatouille</p>
                    <p rating-color="orange">3.6/5 stars</p>
                    <p>Chef Mr. Frenchie and Mme. French-Cuisine</p>
                </div>

            </div> 
        </div>    
    </body>
</html>

Давайте создадим POJO, к которым мы хотим привязать его:

public class Restaurant {

    @Selector( value = "div.restaurant > h1")
    private String name;

    @Selector( value = "div.restaurant > p:nth-child(2)")
    private String description;

    @Selector( value = "div.restaurant > div:nth-child(3) > p > span")    
    private String location;    

    @Selector( 
        value = "div.restaurant > p:nth-child(4)"
        format = "^Restaurant n\*([0-9,]+). Ranked ([0-9,]+) out of ([0-9,]+) restaurants$",
        indexForRegexPattern = 1,
        useDeserializer = true,
        deserializer = ReplacerDeserializer.class,
        preConvert = true,
        postConvert = false
    )
    // so that the number becomes a valid number as they are shown in this format : 18,190
    @ReplaceWith(value = ",", with = "")
    private Long id;

    @Selector( 
        value = "div.restaurant > p:nth-child(4)"
        format = "^Restaurant n\*([0-9,]+). Ranked ([0-9,]+) out of ([0-9,]+) restaurants$",
        // This time, we want the second regex group and not the first one anymore
        indexForRegexPattern = 2,
        useDeserializer = true,
        deserializer = ReplacerDeserializer.class,
        preConvert = true,
        postConvert = false
    )
    // so that the number becomes a valid number as they are shown in this format : 18,190
    @ReplaceWith(value = ",", with = "")
    private Integer rank;

    @Selector(value = ".meal")    
    private List<Meal> meals;

    // getters and setters

}

А теперь и класс Meal:

public class Meal {

    @Selector(value = "p:nth-child(1)")
    private String name;

    @Selector(
        value = "p:nth-child(2)",
        format = "^([0-9.]+)\/5 stars$",
        indexForRegexPattern = 1
    )
    private Float stars;

    @Selector(
        value = "p:nth-child(2)",
        // rating-color custom attribute can be used as well
        attr = "rating-color"
    )
    private String ratingColor;

    @Selector(
        value = "p:nth-child(3)"
    )
    private String chefs;

    // getters and setters.
}

Мы предоставили еще несколько пояснений к приведенному выше коду на нашей странице github.

Пока посмотрим, как это отменить.

private static final String MY_HTML_FILE = "my-html-file.html";

public static void main(String[] args) {


    HtmlToPojoEngine htmlToPojoEngine = HtmlToPojoEngine.create();

    HtmlAdapter<Restaurant> adapter = htmlToPojoEngine.adapter(Restaurant.class);

    // If they were several restaurants in the same page, 
    // you would need to create a parent POJO containing
    // a list of Restaurants as shown with the meals here
    Restaurant restaurant = adapter.fromHtml(getHtmlBody());

    // That's it, do some magic now!

}


private static String getHtmlBody() throws IOException {
    byte[] encoded = Files.readAllBytes(Paths.get(MY_HTML_FILE));
    return new String(encoded, Charset.forName("UTF-8"));

}

Еще один короткий пример можно найти здесь

Надеюсь, это поможет кому-то там!

0 голосов
/ 14 мая 2010

Загляните в библиотеку cURL. Я никогда не использовал его в Java, но я уверен, что для этого должны быть привязки. По сути, вы будете отправлять запрос cURL на любую страницу, которую хотите «почистить». Запрос вернет на страницу строку с исходным кодом. Оттуда вы будете использовать регулярные выражения для анализа любых данных из исходного кода. Как правило, это то, как вы собираетесь это сделать.

...