从HTML中提取文本的Java方法

20

我正在编写一个程序,用于下载HTML页面并选择其中一些信息,并将其写入另一个文件中。我想提取在段落标签之间的信息,但是我只能得到段落的一行。我的代码如下:

FileReader fileReader = new FileReader(file);
BufferedReader buffRd = new BufferedReader(fileReader);
BufferedWriter out = new BufferedWriter(new FileWriter(newFile.txt));
String s;

while ((s = br.readLine()) !=null) {
    if(s.contains("<p>")) {
        try {
            out.write(s);
        } catch (IOException e) {
        }
    }
}

我试图添加另一个while循环,让程序不断写入文件,直到行包含</p>标签,方法是这样的:

while ((s = br.readLine()) !=null) {
    if(s.contains("<p>")) {
        while(!s.contains("</p>") {
            try {
                out.write(s);
            } catch (IOException e) {
            }
        }
    }
}

但这样并不起作用。请问有人能帮忙吗?


我们确实发现了 Stack Overflow 在转义 HTML 标签时出现了一个错误。 - Yishai
你是用反引号将它们引用为代码吗? - pjp
HTML解析器确实存在,而且有很多种。 - lazy
8个回答

33

jsoup

我非常喜欢使用的另一个HTML解析器是jsoup。你可以用两行代码获取所有的<p>元素。

Document doc = Jsoup.connect("http://en.wikipedia.org/").get();
Elements ps = doc.select("p");

然后在一行中将其写入文件。

out.write(ps.text());  //it will append all of the p elements together in one long string

如果你想让它们分成不同行,你可以迭代元素并将它们单独输出。


1
如果一个文档没有使用p标签(非语义化标记),我认为这将无法工作。 - sinθ
2
@sinθ 这个问题明确要求“p”元素。这个回答是完全正确的。 - Basil Bourque
谢谢 @Danny,我喜欢这个汤! - frogatto

10

Jericho是多个可能的HTML解析器之一,可以使此任务既简单又安全。


4

JTidy可以将HTML文档(即使是格式不正确的文档)表示为文档模型,这使得提取<p>标签内容的过程比手动处理原始文本更加优雅。


0
尝试以下方法(如果您不想使用HTML解析库):

        FileReader fileReader = new FileReader(file);
        BufferedReader buffRd = new BufferedReader(fileReader);
        BufferedWriter out = new BufferedWriter(new FileWriter(newFile.txt));
        String s;
        int writeTo = 0;
        while ((s = br.readLine()) !=null) 
        {
                if(s.contains("<p>"))
                {
                        writeTo = 1;

                        try 
                        {
                            out.write(s);
                    } 
                        catch (IOException e) 
                        {

                    }
                }
                if(s.contains("</p>"))
                {
                        writeTo = 0;

                        try 
                        {
                            out.write(s);
                    } 
                        catch (IOException e) 
                        {

                    }
                }
                else if(writeTo==1)
                {
                        try 
                        {
                            out.write(s);
                    } 
                        catch (IOException e) 
                        {

                    }
                }
}

1
如果<p></p>在同一行会发生什么?在这种情况下,字符串将被写两次。我猜这真的取决于输入。 - pjp
你可以添加一些状态来判断是否已经输出了该行,以避免重复输出。 - pjp

0
使用ParserCallback。它是JDK中包含的一个简单类。每次找到新标签时,它会通知您,然后您可以提取标签的文本。以下是一个简单的示例:
import java.io.*;
import java.net.*;
import javax.swing.text.*;
import javax.swing.text.html.*;
import javax.swing.text.html.parser.*;

public class ParserCallbackTest extends HTMLEditorKit.ParserCallback
{
    private int tabLevel = 1;
    private int line = 1;

    public void handleComment(char[] data, int pos)
    {
        displayData(new String(data));
    }

    public void handleEndOfLineString(String eol)
    {
        System.out.println( line++ );
    }

    public void handleEndTag(HTML.Tag tag, int pos)
    {
        tabLevel--;
        displayData("/" + tag);
    }

    public void handleError(String errorMsg, int pos)
    {
        displayData(pos + ":" + errorMsg);
    }

    public void handleMutableTag(HTML.Tag tag, MutableAttributeSet a, int pos)
    {
        displayData("mutable:" + tag + ": " + pos + ": " + a);
    }

    public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet a, int pos)
    {
        displayData( tag + "::" + a );
//      tabLevel++;
    }

    public void handleStartTag(HTML.Tag tag, MutableAttributeSet a, int pos)
    {
        displayData( tag + ":" + a );
        tabLevel++;
    }

    public void handleText(char[] data, int pos)
    {
        displayData( new String(data) );
    }

    private void displayData(String text)
    {
        for (int i = 0; i < tabLevel; i++)
            System.out.print("\t");

        System.out.println(text);
    }

    public static void main(String[] args)
    throws IOException
    {
        ParserCallbackTest parser = new ParserCallbackTest();

        // args[0] is the file to parse

        Reader reader = new FileReader(args[0]);
//      URLConnection conn = new URL(args[0]).openConnection();
//      Reader reader = new InputStreamReader(conn.getInputStream());

        try
        {
            new ParserDelegator().parse(reader, parser, true);
        }
        catch (IOException e)
        {
            System.out.println(e);
        }
    }
}

所以你需要做的就是在找到段落标签时设置一个布尔标志。然后在handleText()方法中提取文本即可。

0

试试这个。

 public static void main( String[] args )
{
    String url = "http://en.wikipedia.org/wiki/Big_data";

    Document document;
    try {
        document = Jsoup.connect(url).get();
        Elements paragraphs = document.select("p");

        Element firstParagraph = paragraphs.first();
        Element lastParagraph = paragraphs.last();
        Element p;
        int i=1;
        p=firstParagraph;
        System.out.println("*  " +p.text());
        while (p!=lastParagraph){
            p=paragraphs.get(i);
            System.out.println("*  " +p.text());
            i++;
        } 
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
}

这个“Element”和“Document”是什么?这是第三方解析器吗?请展示导入行。 - James

0

-3

你可能只是在使用错误的工具:

perl -ne "print if m|<p>| .. m|</p>|" infile.txt >outfile.txt

这是公平的抓住。不过有点晚了。 - brianary

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接