57

好吧,我非常想弄清楚如何从网页中提取信息,并将其带入我的程序(在 Java 中)。

例如,如果我知道我想从中获取信息的确切页面,为了简单起见,百思买商品页面,我如何从该页面获取我需要的适当信息?喜欢标题,价格,描述?

这个过程甚至会被称为什么?我什至不知道要开始研究这个。

编辑:好的,我正在对 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

4

10 回答 10

103

使用像Jsoup这样的 HTML 解析器。这比Java 中可用的其他 HTML 解析器更受我的青睐,因为它支持CSS 选择器这样的jQuery。此外,它的代表节点列表的类, 实现,以便您可以在增强的 for 循环中对其进行迭代(因此无需在普通 Java DOM 解析器中使用冗长的类和类似的类)。ElementsIterableNodeNodeList

这是一个基本的启动示例(只需将最新的 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());
        }
    }

}

正如您可能已经猜到的那样,这会打印您自己的问题和所有回答者的姓名。

于 2010-05-14T15:53:50.500 回答
10

这被称为屏幕抓取,维基百科有这篇关于更具体的网络抓取的文章。这可能是一个重大挑战,因为那里有一些丑陋的、混乱的、如果不是浏览器聪明的 HTML,那么祝你好运。

于 2010-05-14T15:53:15.590 回答
6

我会使用JTidy - 它与 JSoup 类似,但我不太了解 JSoup。JTidy 处理损坏的 HTML 并返回一个 w3c 文档,因此您可以使用它作为 XSLT 的源来提取您真正感兴趣的内容。如果您不了解 XSLT,那么您不妨使用 JSoup,作为文档模型比 w3c 更好用。

编辑:快速浏览 JSoup 网站表明 JSoup 确实可能是更好的选择。它似乎支持从文档中提取内容的 CSS 选择器。这可能比进入 XSLT 更容易使用。

于 2010-05-14T15:57:20.187 回答
4

您可以使用 html 解析器(这里有很多有用的链接:java html parser)。

该过程称为“抓取网站内容”。搜索“抓取网站内容 java”以进行进一步调查。

于 2010-05-14T15:53:04.420 回答
3

jsoup 支持 java 1.5

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

看起来那个堆栈是一个错误,并且已被修复

于 2011-09-19T17:09:46.797 回答
2

您可能想查看 HTML 以查看是否可以找到唯一且靠近您的文本的字符串,然后您可以使用 line/char-offsets 来获取数据。

如果没有任何类似于System.XML.LinqC# 中的 XML 类,在 Java 中可能会很尴尬。

于 2010-05-14T15:53:23.060 回答
2

您也可以尝试jARVEST

它基于纯 Java 引擎上的 JRuby DSL 来蜘蛛抓取转换网站。

示例

查找网页内的所有链接(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);
  }
于 2012-07-11T22:47:51.953 回答
2

我的回答可能对这个问题的作者没有用(我迟到了 8 个月,所以我猜这不是正确的时机),但我认为它可能对可能遇到这个答案的许多其他开发人员有用。

今天,我刚刚(以我公司的名义)发布了一个 HTML 到 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"));

}

另一个简短的例子可以在这里找到

希望这会帮助那里的人!

于 2018-07-26T08:14:34.943 回答
1

JSoup 解决方案很棒,但如果您只需要提取一些非常简单的东西,使用 regex 或 String.indexOf 可能会更容易

正如其他人已经提到的那样,该过程称为刮

于 2010-05-14T18:20:14.887 回答
-1

查看 cURL 库。我从未在 Java 中使用过它,但我确信它必须有绑定。基本上,你要做的就是向你想要“抓取”的任何页面发送一个 cURL 请求。该请求将带有源代码的字符串返回到页面。从那里,您将使用正则表达式从源代码中解析您想要的任何数据。这通常是你将要这样做的方式。

于 2010-05-14T15:53:13.217 回答