代码之家  ›  专栏  ›  技术社区  ›  James

如何“扫描”一个网站(或网页)的信息,并将其纳入我的程序?

  •  49
  • James  · 技术社区  · 15 年前

    我非常想知道如何从网页中提取信息,并把它带到我的程序中(在Java中)。

    例如,如果我知道我想要信息的确切页面,为了简单起见,一个百思买项目页面,我如何从该页面获得我需要的适当信息?比如标题,价格,描述?

    这个过程叫什么?我甚至不知道要开始研究这个。

    编辑: 好的,我正在为jsup(由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)
    

    我有阿帕奇公地

    10 回复  |  直到 6 年前
        1
  •  91
  •   Community CDub    8 年前

    使用类似HTML语法分析器 Jsoup . 这是我最喜欢的 other HTML parsers available in Java 因为它 supports jQuery 喜欢 CSS selectors . 另外,它的类表示一个节点列表, Elements 工具 Iterable 以便可以在 enhanced for loop (所以不必费神费神 Node NodeList 类似于普通Java DOM解析器中的类。

    下面是一个基本的启动示例(只需将 latest Jsoup JAR file 在类路径中):

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

    你可能已经猜到了,这打印出你自己的问题和所有回答者的名字。

        2
  •  10
  •   sblundy    15 年前

    这被称为屏幕抓取,维基百科有这篇文章更具体。 web scraping . 这可能是一个主要的挑战,因为有一些丑陋,混乱,打破,如果不是因为浏览器聪明的HTML在那里,所以祝你好运。

        3
  •  6
  •   mdma    15 年前

    我会用 JTidy -这对JSoup来说很相似,但我不太了解JSoup。JTIDY处理损坏的HTML并返回W3C文档,因此您可以将其用作XSLT的源代码,以提取您真正感兴趣的内容。如果您不知道XSLT,那么您也可以使用JSoup,因为文档模型比W3C更适合使用。

    编辑:在jsoup网站上快速浏览一下,可以发现jsoup确实是更好的选择。它似乎支持CSS选择器从文档中提取内容。这可能比进入XSLT要容易得多。

        4
  •  4
  •   Roman    15 年前

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

    这个过程称为“抓取网站内容”。搜索“抓取网站内容Java”进行进一步的反击。

        5
  •  3
  •   Kalpesh Soni    13 年前

    JToice支持Java 1.5

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

    看起来那个堆栈是个bug,已经修复了

        6
  •  2
  •   Anton    15 年前

    JSoup解决方案很好,但是如果只需要提取一些非常简单的内容,那么使用regex或string.indexof可能会更容易。

    正如其他人已经提到的,这个过程称为刮削。

        7
  •  2
  •   Peter Mortensen icecrime    15 年前

    您可能希望查看HTML以查看是否可以找到唯一且靠近文本的字符串,然后可以使用行/字符偏移量获取数据。

    如果Java中没有任何类似于XML的类,那么在Java中可能会很尴尬。 System.XML.Linq 在C语言中。

        8
  •  2
  •   t0mm13b    12 年前

    你也可以试试 jARVEST .

    它是基于JRuBiSDSL的一个纯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);
      }
    
        9
  •  0
  •   Louis-wht    6 年前

    我的答案可能对这个问题的作者没有帮助(我晚了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>
    

    让我们创建要映射到的pojos:

    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

    希望这能帮上忙!

        10
  •  -1
  •   Nelson    15 年前

    查看curl库。我从来没有在Java中使用过它,但我确信它一定有绑定。基本上,你要做的就是发送一个curl请求到你想要“刮掉”的任何页面。请求将向页面返回一个包含源代码的字符串。从那里,您将使用regex解析源代码中所需的任何数据。一般来说,你就是这样做的。