【问题标题】:Extracting anchor tag from html using Java使用Java从html中提取锚标记
【发布时间】:2011-07-11 08:36:59
【问题描述】:

我在一个文本中有几个锚标签,

输入:<a href="http://stackoverflow.com" >Take me to StackOverflow</a>

输出: http://stackoverflow.com

如何在不使用 3rd 方 API 的情况下找到所有这些输入字符串并将其转换为 java 中的输出字符串???

【问题讨论】:

    标签: java html-parsing


    【解决方案1】:

    核心 API 中有一些类可用于从锚标记(如果存在!)中获取所有 href 属性:

    import java.io.*;
    import java.util.*;
    import javax.swing.text.*;
    import javax.swing.text.html.*;
    import javax.swing.text.html.parser.*;
    
    public class HtmlParseDemo {
       public static void main(String [] args) throws Exception {
    
           String html =
               "<a href=\"http://stackoverflow.com\" >Take me to StackOverflow</a> " +
               "<!--                                                               " +
               "<a href=\"http://ignoreme.com\" >...</a>                           " +
               "-->                                                                " +
               "<a href=\"http://www.google.com\" >Take me to Google</a>           " +
               "<a>NOOOoooo!</a>                                                   ";
    
           Reader reader = new StringReader(html);
           HTMLEditorKit.Parser parser = new ParserDelegator();
           final List<String> links = new ArrayList<String>();
    
           parser.parse(reader, new HTMLEditorKit.ParserCallback(){
               public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
                   if(t == HTML.Tag.A) {
                       Object link = a.getAttribute(HTML.Attribute.HREF);
                       if(link != null) {
                           links.add(String.valueOf(link));
                       }
                   }
               }
           }, true);
    
           reader.close();
           System.out.println(links);
       }
    }
    

    将打印:

    [http://stackoverflow.com, http://www.google.com]

    【讨论】:

    • 哇。我不知道 HTMLEditorKit 的存在。如果我要使用哪个 HTML 解析器最好??
    • 最好的解析器是通过所有单元测试的解析器:)。这比使用正则表达式破解更好的选择。
    【解决方案2】:
    public static void main(String[] args) {
        String test = "qazwsx<a href=\"http://stackoverflow.com\">Take me to StackOverflow</a>fdgfdhgfd"
                + "<a href=\"http://stackoverflow2.com\">Take me to StackOverflow2</a>dcgdf";
    
        String regex = "<a href=(\"[^\"]*\")[^<]*</a>";
    
        Pattern p = Pattern.compile(regex);
    
        Matcher m = p.matcher(test);
        System.out.println(m.replaceAll("$1"));
    }
    

    注意: Andrzej Doyle 的所有观点都是有效的,如果您的输入中有更多简单的 &lt;a href="X"&gt;Y&lt;/a&gt;,并且您确定这是可解析的 HTML,那么您使用 HTML 解析器效果更好。

    总结一下:

    1. 如果您在评论中有&lt;a&gt;,我发布的正则表达式将不起作用。 (您可以将其视为特殊情况)
    2. 如果&lt;a&gt; 标记中有其他属性,它就不起作用。 (同样,您可以将其视为特殊情况)
    3. 还有许多其他情况下正则表达式不起作用,并且您不能用正则表达式覆盖所有情况,因为 HTML 不是常规语言。

    但是,如果您的要求始终将 &lt;a href="X"&gt;Y&lt;/a&gt; 替换为 "X" 而不考虑上下文,那么我发布的代码将起作用。

    【讨论】:

    • -1:HTML is not a regular language。 (需要我多说吗?)
    • >>> (需要我多说吗?) 是的,给我测试用例,代码将无法达到 SO 的要求
    • 很多很多输入。 &lt;a class="stripey" href="http://stackoverflow.com"&gt;Take me...&lt;/a&gt; 会给出假阴性。 &lt;!-- Comment this out for now &lt;a href="http://stackoverflow.com"&gt;Take me...&lt;/a&gt; --&gt; 将给出误报。在这两种情况下,使用 HTML 解析器都会正确提取 href 属性(包括在第二种情况下根本找不到元素)。
    • 对不起,问题是:我有几个锚标签在一个文本中,你在哪里看到HTML?这样你就可以使用 HTML 解析器了?
    • @Op De Cirkel - 我已经删除了我的 -1,因为这过于苛刻。但除非 OP 可以保证这确实是一个任意的纯文本文件,巧合地 看起来 像 HTML(但 不是),否则我不建议使用正则表达式。 HTML 文件现在可能很简单,但是有很多合法的方式可以重写它,这会导致代码中断,因此从一开始就将其解析为 HTML 就不会那么令人头疼了。 (注释掉的是这里的“杀手级用例”——我们都习惯于这样做以暂时忽略某些东西,所以不处理这种情况的代码正在等待发生的混乱。)
    【解决方案3】:

    您可以使用JSoup

    String html = "<p>An <a href=\"http://stackoverflow.com\" >Take me to StackOverflow</a> link.</p>";
    Document doc = Jsoup.parse(html);
    Element link = doc.select("a").first();
    
    String linkHref = link.attr("href"); // "http://stackoverflow.com"
    

    另见

    【讨论】:

    • 在 Java 本身中不使用 3rd 方 API 的任何方式都可以做到这一点??
    • @Ebbu - 当然,如果您愿意,您可以随时自己编写 HTML 解析器。但是如果你想从 HTML 中提取数据,你需要一个 HTML 解析器(参见我对Op's answer 的评论,所以如果你不喜欢重新发明轮子,实际上你应该只使用第三个-party 库。您不必担心;库支持是 Java 的最大优势之一。
    • 感谢 Andrzej,但现在这是我唯一的要求,我不想为此使用第 3 方 API。否则我完全同意你说的。这是我第一次使用正则表达式,我在解决这个问题时遇到了一些困难。
    【解决方案4】:

    上面的例子很完美;如果你想解析一个 HTML 文档而不是连接字符串,写这样的东西来补充上面的代码。

    上面已有的代码~修改为显示:上面的HtmlParser.java(HtmlParseDemo.java) 用下面的 HtmlPage.java 补充代码。 HtmlPage.properties 文件的内容在本页底部。

    HtmlPage.properties 文件中的 main.url 属性为: ma​​in.url=http://www.whatever.com/

    这样你就可以解析你之后的网址。 :-) 快乐编码:-D

    import java.io.Reader;
    import java.io.StringReader;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.swing.text.MutableAttributeSet;
    import javax.swing.text.html.HTML;
    import javax.swing.text.html.HTMLEditorKit;
    import javax.swing.text.html.parser.ParserDelegator;
    
    public class HtmlParser
    {
        public static void main(String[] args) throws Exception
        {
            String html = HtmlPage.getPage();
    
            Reader reader = new StringReader(html);
            HTMLEditorKit.Parser parser = new ParserDelegator();
            final List<String> links = new ArrayList<String>();
    
            parser.parse(reader, new HTMLEditorKit.ParserCallback()
            {
                public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos)
                {
                    if (t == HTML.Tag.A)
                    {
                        Object link = a.getAttribute(HTML.Attribute.HREF);
                        if (link != null)
                        {
                            links.add(String.valueOf(link));
                        }
                    }
                }
            }, true);
    
            reader.close();
    
            // create the header
            System.out.println("<html>\n<head>\n   <title>Link City</title>\n</head>\n<body>");
    
            // spit out the links and create href
            for (String l : links)
            {
                System.out.print("   <a href=\"" + l + "\">" + l + "</a>\n");
            }
    
            // create footer
            System.out.println("</body>\n</html>");
        }
    }
    
    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.StringWriter;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.ResourceBundle;
    
    public class HtmlPage
    {
        public static String getPage()
        {
            StringWriter sw = new StringWriter();
            ResourceBundle bundle = ResourceBundle.getBundle(HtmlPage.class.getName().toString());
    
            try
            {
                URL url = new URL(bundle.getString("main.url"));
    
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.setDoOutput(true);
    
                InputStream content = (InputStream) connection.getInputStream();
                BufferedReader in = new BufferedReader(new InputStreamReader(content));
    
                String line;
    
                while ((line = in.readLine()) != null)
                {
                    sw.append(line).append("\n");
                }
    
            } catch (Exception e)
            {
                e.printStackTrace();
            }
    
            return sw.getBuffer().toString();
        }
    }
    

    例如,如果在浏览器中查看,这将输出来自http://ebay.com.au/ 的链接。 这是一个子集,因为有很多链接

    链接城市 #mainContent http://realestate.ebay.com.au/

    【讨论】:

      【解决方案5】:

      如果您需要在不使用 3d 方库的情况下构建它,最可靠的方法(正如已经建议的那样)是使用正则表达式 (java.util.regexp)。

      另一种方法是将 html 解析为 XML,或者使用 SAX 解析器来捕获和处理“a”元素的每个实例,或者作为 DOM 文档,然后使用 XPATH 搜索它(参见http://download.oracle.com/javase/6/docs/api/javax/xml/parsers/package-summary.html)。这是有问题的,因为它要求 HTML 页面在标记中完全符合 XML,这是一个非常危险的假设,而不是我推荐的方法,因为大多数“真实”的 html 页面都不符合 XML。

      不过,我还是建议您查看现有的为此目的而构建的框架(如 JSoup,上面也提到过)。无需重新发明轮子。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-11-05
        • 2013-11-23
        • 2018-12-29
        • 2015-04-24
        • 2011-03-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多