web-dev-qa-db-ja.com

Webサイト(またはページ)で情報を「スキャン」して、プログラムに取り込む方法は?

ええと、私はWebページから情報を引き出し、それを(Javaで)プログラムに取り込む方法を見つけようとしています。

たとえば、Best Buyアイテムページを簡単にするために、情報が必要な正確なページがわかっている場合、そのページから必要な適切な情報を取得するにはどうすればよいですか?タイトル、価格、説明のような?

このプロセスは何と呼ばれますか?私はこれを研究し始めることすらわからなかった。

編集:さて、私は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があります

51
James

Jsoup のようなHTMLパーサーを使用します。これは、 Java で使用可能な他のHTMLパーサーよりも優先されます。なぜなら、 supportsjQuery like CSS selectors 。また、ノードのリストを表すクラス、 Elements は、 Iterable を実装して、繰り返し処理できるようにします。 for forforループ (したがって、平均Java DOMパーサー)でクラスのような詳細なNodeおよびNodeListを煩わせる必要はありません。

基本的なキックオフの例を次に示します(クラスパスに latest Jsoup JARファイル を配置するだけです):

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());
        }
    }

}

ご想像のとおり、これはあなた自身の質問とすべての回答者の名前を表示します。

92
BalusC

これはスクリーンスクレイピングと呼ばれます。ウィキペディアには、より具体的な webスクレイピング に関する記事があります。いくつかの、い、混乱した、ブラウザに賢くないHTMLがそこにあるので、それは大きな挑戦になる可能性があります。

10
sblundy

JTidy を使用します。これはJSoupに似ていますが、JSoupがよくわかりません。 JTidyは壊れたHTMLを処理してw3cドキュメントを返すため、これをXSLTのソースとして使用して、本当に興味のあるコンテンツを抽出できます。XSLTがわからない場合は、ドキュメントとしてJSoupを使用することもできますモデルはw3cよりも動作しやすいです。

編集:JSoup Webサイトをざっと見てみると、JSoupが本当に良い選択である可能性があります。ドキュメントからものを抽出するために、CSSセレクターをサポートしているようです。これは、XSLTを使用するよりも作業がはるかに簡単な場合があります。

6
mdma

Htmlパーサーを使用できます(ここに役立つリンクが多数あります: Java htmlパーサー )。

このプロセスは「ウェブサイトのコンテンツを取得する」と呼ばれます。さらに調査するために、「グラブWebサイトコンテンツJava」を検索してください。

4
Roman

jsoupはJava 1.5をサポートしています

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

そのスタックはバグだったようで、修正されました

3
Kalpesh Soni

おそらくHTMLを調べて、テキストの近くに一意の文字列を見つけることができるかどうかを確認し、line/char-offsetsを使用してデータを取得できます。

C#のSystem.XML.Linqにあるものに類似したXMLクラスが存在しない場合、Javaでは扱いにくいかもしれません。

2
Kurru

JSoupソリューションは素晴らしいですが、本当にシンプルなものだけを抽出する必要がある場合は、regexまたはString.indexOfを使用する方が簡単かもしれません

他の人がすでに述べたように、プロセスはスクレイピングと呼ばれます

2
Anton

jARVEST を試すこともできます。

スパイダースクレープ変換Webサイトへのpure-Javaエンジン上のJRuby DSLに基づいています。

Webページ内のすべてのリンクを検索します(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
lipido

私の答えはおそらくこの質問の著者にとっては役に立たないでしょう(私は8か月遅れているので正しいタイミングではないと思います)。

今日、私は(私の会社の名前で)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"));

}

別の短い例を見つけることができます here

これが誰かを助けることを願っています!

1
Louis-Wht