web-dev-qa-db-fra.com

Comment "scanner" un site Web (ou une page) pour obtenir des informations et les intégrer à mon programme?

Eh bien, j'essaie à peu près de comprendre comment extraire des informations d'une page Web et les intégrer à mon programme (en Java). 

Par exemple, si je connais la page exacte pour laquelle je souhaite obtenir des informations, par souci de simplicité, une page d’objet Best Buy, comment puis-je obtenir les informations appropriées dont j'ai besoin sur cette page? Vous aimez le titre, le prix, la description? 

Comment s'appellerait même ce processus? Je ne pensais même pas commencer à faire des recherches à ce sujet.

Edit: .__ OK, je fais un test pour le JSoup (celui posté par BalusC), mais je continue à avoir cette erreur:

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)

J'ai Apache Commons

49
James

Utilisez un analyseur HTML tel que Jsoup . Cela a ma préférence au-dessus du autres analyseurs HTML disponibles en Java puisqu'il est supportejQuery comme sélecteurs CSS . De plus, sa classe représentant une liste de nœuds, Elements , implémente Iterable afin que vous puissiez la parcourir dans un amélioré pour la boucle tracas avec les classes verbales Node et NodeList comme celles de l’analyseur Java DOM moyen).

Voici un exemple de démarrage élémentaire (il suffit de placer le dernier fichier Jsoup Jsoup dans 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());
        }
    }

}

Comme vous l'avez peut-être deviné, ceci affiche votre propre question et les noms de tous les répondants.

91
BalusC

C’est ce que l’on appelle le grattage d’écran, wikipedia publie cet article dans le fichier plus spécifique Web scraping . Cela peut être un défi majeur, car il existe un code HTML très laid, gênant, cassé si ce n’est pour le navigateur, donc bonne chance. 

10
sblundy

Je voudrais utiliser JTidy - c'est semblable à JSoup, mais je ne connais pas bien JSoup. JTidy traite le HTML cassé et renvoie un document w3c. Vous pouvez donc l'utiliser comme source pour XSLT afin d'extraire le contenu qui vous intéresse vraiment. Si vous ne connaissez pas XSLT, vous pouvez aussi bien utiliser JSoup en tant que document. le modèle est plus agréable à travailler que le w3c.

EDIT: Un rapide coup d’œil sur le site Web de JSoup montre que JSoup pourrait bien être le meilleur choix. Il semble prendre en charge les sélecteurs CSS dans la boîte pour extraire des éléments du document. Cela peut être beaucoup plus facile à utiliser que d’entrer dans XSLT.

6
mdma

Vous pouvez utiliser un analyseur HTML (de nombreux liens utiles ici: Analyseur HTML Java ).

Le processus s'appelle «saisir le contenu d'un site Web». Recherchez 'saisir le contenu du site Web Java' pour poursuivre l'investigation.

4
Roman

jsoup supporte Java 1.5

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

ressemble à cette pile était un bug, et a été corrigé

3
Kalpesh Soni

Vous voudrez probablement consulter le code HTML pour voir si vous pouvez trouver des chaînes uniques et proches de votre texte. Vous pouvez ensuite utiliser des décalages de ligne/caractères pour accéder aux données.

Cela pourrait être gênant en Java, s'il n'y a pas de classes XML similaires à celles trouvées dans System.XML.Linq en C #.

2
Kurru

La solution JSoup est excellente, mais si vous avez besoin d'extraire quelque chose de vraiment simple, il peut être plus facile d'utiliser regex ou String.indexOf

Comme d'autres l'ont déjà mentionné, le processus s'appelle raclage

2
Anton

Vous pouvez également essayer jARVEST .

Il est basé sur un DSL JRuby sur un moteur Java pur pour transformer des sites Web en spider-scrape-transform.

Exemple:

Trouvez tous les liens dans une page Web (wget et xpath sont des constructions du langage jARVEST):

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

Dans un programme 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
lipido

Ma réponse ne sera probablement pas utile à l'auteur de cette question (j'ai 8 mois de retard, donc ce n'est pas le bon timing je suppose), mais je pense que cela sera probablement utile pour de nombreux autres développeurs qui pourraient rencontrer cette réponse.

Aujourd'hui, je viens de publier (au nom de ma société) un cadre complet HTML vers POJO que vous pouvez utiliser pour mapper le code HTML vers n'importe quelle classe POJO avec simplement quelques annotations. La bibliothèque elle-même est assez pratique et propose de nombreuses autres fonctionnalités tout en étant très connectables. Vous pouvez y jeter un coup d'oeil ici: https://github.com/whimtrip/jwht-htmltopojo

Comment utiliser: Basics

Imaginons que nous devions analyser la page HTML suivante:

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

Créons les POJOs auxquels nous voulons le mapper:

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

}

Et maintenant, la classe 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.
}

Nous avons fourni quelques explications supplémentaires sur le code ci-dessus sur notre page github.

Pour le moment, voyons comment éliminer cela.

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"));

}

Vous trouverez un autre exemple court ici

J'espère que cela aidera quelqu'un là-bas!

0
Louis-Wht