我正在编写一个爬虫/解析器,它应该能够处理不同类型的内容,包括RSS,Atom和普通的html文件。为了确定正确的解析器,我编写了一个名为ParseFactory的类,它接受一个URL,尝试检测内容类型,并返回正确的解析器。
不幸的是,使用URLConnection中提供的in方法检查内容类型并不总是有效。例如,
String contentType = url.openConnection().getContentType();
并不总是提供正确的内容类型(例如“text / html”应该是RSS)或者不允许区分RSS和Atom(例如“application / xml”既可以是Atom,也可以是一个RSS提要)。为了解决这个问题,我开始在InputStream中寻找线索。问题是我遇到了一个优雅的类设计,我需要只下载一次InputStream。在我目前的设计中,我首先编写了一个单独的类来确定正确的内容类型,然后ParseFactory使用这些信息来创建相应解析器的实例,反过来,当调用方法'parse()'时,下载整个InputStream第二次。
public Parser createParser(){
InputStream inputStream = null;
String contentType = null;
String contentEncoding = null;
ContentTypeParser contentTypeParser = new ContentTypeParser(this.url);
Parser parser = null;
try {
inputStream = new BufferedInputStream(this.url.openStream());
contentTypeParser.parse(inputStream);
contentType = contentTypeParser.getContentType();
contentEncoding = contentTypeParser.getContentEncoding();
assert (contentType != null);
inputStream = new BufferedInputStream(this.url.openStream());
if (contentType.equals(ContentTypes.rss))
{
logger.info("RSS feed detected");
parser = new RssParser(this.url);
parser.parse(inputStream);
}
else if (contentType.equals(ContentTypes.atom))
{
logger.info("Atom feed detected");
parser = new AtomParser(this.url);
}
else if (contentType.equals(ContentTypes.html))
{
logger.info("html detected");
parser = new HtmlParser(this.url);
parser.setContentEncoding(contentEncoding);
}
else if (contentType.equals(ContentTypes.UNKNOWN))
logger.debug("Unable to recognize content type");
if (parser != null)
parser.parse(inputStream);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return parser;
}
基本上,我正在寻找一种解决方案,允许我消除第二个“inputStream = new BufferedInputStream(this.url.openStream())”。
非常感谢任何帮助!
附注1:为了完整起见,我也尝试使用URLConnection.guessContentTypeFromStream(inputStream)方法,但这种方法经常返回null。
附注2:XML解析器(Atom和Rss)基于SAXParser,即Jsoup上的Html解析器。
答案 0 :(得分:1)
您可以致电mark
和reset
吗?
inputStream = new BufferedInputStream(this.url.openStream());
inputStream.mark(2048); // Or some other sensible number
contentTypeParser.parse(inputStream);
contentType = contentTypeParser.getContentType();
contentEncoding = contentTypeParser.getContentEncoding();
inputstream.reset(); // Let the parser have a crack at it now
答案 1 :(得分:0)
也许您的ContentTypeParser
应该在内部缓存内容并将其提供给相应的ContentParser
,而不是从InputStream
获取数据。