使用 HttpClient 和 HtmlParser 实现简易爬虫Word文档下载推荐.docx
- 文档编号:5733436
- 上传时间:2023-05-05
- 格式:DOCX
- 页数:14
- 大小:57.06KB
使用 HttpClient 和 HtmlParser 实现简易爬虫Word文档下载推荐.docx
《使用 HttpClient 和 HtmlParser 实现简易爬虫Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《使用 HttpClient 和 HtmlParser 实现简易爬虫Word文档下载推荐.docx(14页珍藏版)》请在冰点文库上搜索。
//得到值节点的值
Stringresult=node.toPlainTextString();
//若包含关键字,则简单打印出来文本
if(result.indexOf(keyword)!
=-1)
System.out.println(result);
}//endif
//孩子节点不为空,继续迭代该孩子节点
else
processNodeList(childList,keyword);
}//endelse
}//endwile
上面的中有两个方法:
1privatestaticvoidprocessNodeList(NodeListlist,Stringkeyword)
该方法是用类似深度优先的方法来迭代遍历整个网页节点,将那些包含了某个关键字的值节点的值打印出来。
1publicstaticvoidextractKeyWordText(Stringurl,Stringkeyword)
该方法生成针对String类型的url变量代表的某个特定网页的解析器,调用1中的方法实现简单的遍历。
清单3的代码展示了如何迭代所有的网页,更多的工作可以在此基础上展开。
比如找到某个特定的网页内部节点,其实就可以在遍历所有的节点基础上来判断,看被迭代的节点是否满足特定的需要。
使用NodeFilter
NodeFilter是一个接口,任何一个自定义的Filter都需要实现这个接口中的booleanaccept()方法。
如果希望迭代网页节点的时候保留当前节点,则在节点条件满足的情况下返回true;
否则返回false。
HtmlParse里提供了很多实现了NodeFilter接口的类,下面就一些笔者所用到的,以及常用的Filter做一些介绍:
1对Filter做逻辑操作的Fitler有:
,,,。
这些Filter来组合不同的Filter,形成满足两个Filter逻辑关系结果的Filter。
1判断节点的孩子,兄弟,以及父亲节点情况的Filter有:
,。
1判断节点本身情况的Filter有:
判读节点是否有特定属性;
:
判断节点是否是具有特定模式(pattern)url的节点;
判断节点是否具有特定的名字;
NodeClassFilter:
判读节点是否是某个HtmlParser定义好的Tag类型。
在org.htmlparser.tags包下有对应Html标签的各种Tag,例如LinkTag,ImgeTag等。
还有其他的一些Filter在这里不一一列举了,可以在org.htmlparser.filters下找到。
清单4展示了如何使用上面提到过的一些filter来抽取网页中的<
a>
标签里的href属性值,<
img>
标签里的src属性值,以及<
frame>
标签里的src的属性值。
//获取一个网页上所有的链接和图片链接
publicstaticvoidextracLinks(Stringurl){
//过滤<
标签的filter,用来提取frame标签里的src属性所、表示的链接
NodeFilterframeFilter=newNodeFilter(){
publicbooleanaccept(Nodenode){
if(node.getText().startsWith("
framesrc="
)){
returntrue;
}else{
returnfalse;
};
//OrFilter来设置过滤<
标签,<
标签和<
标签,三个标签是or的关系
OrFilterorFilter=newOrFilter(newNodeClassFilter(LinkTag.class),new
NodeClassFilter(ImageTag.class));
OrFilterlinkFilter=newOrFilter(orFilter,frameFilter);
//得到所有经过过滤的标签
NodeListlist=parser.extractAllNodesThatMatch(linkFilter);
for(inti=0;
i<
list.size();
i++){
Nodetag=list.elementAt(i);
if(taginstanceofLinkTag)//<
标签
LinkTaglink=(LinkTag)tag;
StringlinkUrl=link.getLink();
//url
Stringtext=link.getLinkText();
//链接文字
System.out.println(linkUrl+"
**********"
+text);
elseif(taginstanceofImageTag)//<
ImageTagimage=(ImageTag)list.elementAt(i);
System.out.print(image.getImageURL()+"
********"
//图片地址
System.out.println(image.getText());
//图片文字
else//<
//提取frame里src属性的链接如<
test.html"
/>
Stringframe=tag.getText();
intstart=frame.indexOf("
src="
frame=frame.substring(start);
intend=frame.indexOf("
"
if(end==-1)
end=frame.indexOf("
>
"
frame=frame.substring(5,end-1);
System.out.println(frame);
}catch(ParserExceptione){
}
简单强大的StringBean
如果你想要网页中去掉所有的标签后剩下的文本,那就是用StringBean吧。
以下简单的代码可以帮你解决这样的问题:
清单5
StringBeansb=newStringBean();
sb.setLinks(false);
//设置结果中去点链接
sb.setURL(url);
//设置你所需要滤掉网页标签的页面url
System.out.println(sb.getStrings());
//打印结果
HtmlParser提供了强大的类库来处理网页,由于本文旨在简单的介绍,因此只是将与笔者后续爬虫部分有关的关键类库进行了示例说明。
感兴趣的读者可以专门来研究一下HtmlParser更为强大的类库。
HttpClient提供了便利的HTTP协议访问,使得我们可以很容易的得到某个网页的源码并保存在本地;
HtmlParser提供了如此简便灵巧的类库,可以从网页中便捷的提取出指向其他网页的超链接。
笔者结合这两个开源包,构建了一个简易的网络爬虫。
学过数据结构的读者都知道有向图这种数据结构。
如下图所示,如果将网页看成是图中的某一个节点,而将网页中指向其他网页的链接看成是这个节点指向其他节点的边,那么我们很容易将整个Internet上的网页建模成一个有向图。
理论上,通过遍历算法遍历该图,可以访问到Internet上的几乎所有的网页。
最简单的遍历就是宽度优先以及深度优先。
以下笔者实现的简易爬虫就是使用了宽度优先的爬行策略。
在看简易爬虫的实现代码之前,先介绍一下简易爬虫爬取网页的流程。
对应上面的流程图,简易爬虫由下面几个类组成,各个类职责如下:
Crawler.java:
爬虫的主方法入口所在的类,实现爬取的主要流程。
LinkDb.java:
用来保存已经访问的url和待爬取的url的类,提供url出对入队操作。
Queue.java:
实现了一个简单的队列,在LinkDb.java中使用了此类。
FileDownloader.java:
用来下载url所指向的网页。
HtmlParserTool.java:
用来抽取出网页中的链接。
LinkFilter.java:
一个接口,实现其accept()方法用来对抽取的链接进行过滤。
下面是各个类的源码,代码中的注释有比较详细的说明。
packagecom.ie;
importjava.util.Set;
publicclassCrawler{
/*使用种子url初始化URL队列*/
privatevoidinitCrawlerWithSeeds(String[]seeds)
for(inti=0;
i<
seeds.length;
i++)
LinkDB.addUnvisitedUrl(seeds[i]);
/*爬取方法*/
publicvoidcrawling(String[]seeds)
LinkFilterfilter=newLinkFilter(){
//提取以开头的链接
publicbooleanaccept(Stringurl){
if(url.startsWith("
))
//初始化URL队列
initCrawlerWithSeeds(seeds);
//循环条件:
待抓取的链接不空且抓取的网页不多于1000
while(!
LinkDB.unVisitedUrlsEmpty()&
&
LinkDB.getVisitedUrlNum()<
=1000)
//队头URL出对
StringvisitUrl=LinkDB.unVisitedUrlDeQueue();
if(visitUrl==null)
continue;
FileDownLoaderdownLoader=newFileDownLoader();
//下载网页
downLoader.downloadFile(visitUrl);
//该url放入到已访问的URL中
LinkDB.addVisitedUrl(visitUrl);
//提取出下载网页中的URL
Set<
String>
links=HtmlParserTool.extracLinks(visitUrl,filter);
//新的未访问的URL入队
for(Stringlink:
links)
LinkDB.addUnvisitedUrl(link);
//main方法入口
publicstaticvoidmain(String[]args)
Crawlercrawler=newCrawler();
crawler.crawling(newString[]{"
});
importjava.util.HashSet;
/**
*用来保存已经访问过Url和待访问的Url的类
*/
publicclassLinkDB{
//已访问的url集合
privatestaticSet<
visitedUrl=newHashSet<
();
//待访问的url集合
privatestaticQueue<
unVisitedUrl=newQueue<
publicstaticQueue<
getUnVisitedUrl(){
returnunVisitedUrl;
publicstaticvoidaddVisitedUrl(Stringurl){
visitedUrl.add(url);
publicstaticvoidremoveVisitedUrl(Stringurl){
visitedUrl.remove(url);
publicstaticStringunVisitedUrlDeQueue(){
returnunVisitedUrl.deQueue();
//保证每个url只被访问一次
publicstaticvoidaddUnvisitedUrl(Stringurl){
if(url!
=null&
!
url.trim().equals("
)
&
visitedUrl.contains(url)
unVisitedUrl.contians(url))
unVisitedUrl.enQueue(url);
publicstaticintgetVisitedUrlNum(){
returnvisitedUrl.size();
publicstaticbooleanunVisitedUrlsEmpty(){
returnunVisitedUrl.empty();
importjava.util.LinkedList;
*数据结构队列
publicclassQueue<
T>
privateLinkedList<
queue=newLinkedList<
publicvoidenQueue(Tt)
queue.addLast(t);
publicTdeQueue()
returnqueue.removeFirst();
publicbooleanisQueueEmpty()
returnqueue.isEmpty();
publicbooleancontians(Tt)
returnqueue.contains(t);
publicbooleanempty()
importjava.io.DataOutputStream;
importjava.io.File;
importjava.io.FileOutputStream;
importjava.io.IOException;
importmons.httpclient.DefaultHttpMethodRetryHandler;
importmons.httpclient.HttpClient;
importmons.httpclient.HttpException;
importmons.httpclient.HttpStatus;
importmons.httpclient.methods.GetMethod;
importmons.httpclient.params.HttpMethodParams;
publicclassFileDownLoader{
/**根据url和网页类型生成需要保存的网页的文件名
*去除掉url中非文件名字符
publicStringgetFileNameByUrl(Stringurl,StringcontentType)
url=url.substring(7);
//removehttp:
//
if(contentType.indexOf("
html"
)!
=-1)//text/html
url=url.replaceAll("
[\\?
/:
*|<
\"
]"
"
_"
)+"
.html"
;
returnurl;
else//如application/pdf
returnurl.replaceAll("
."
+\
contentType.substring(contentType.lastIndexOf("
/"
)+1);
/**保存网页字节数组到本地文件
*filePath为要保存的文件的相对地址
privatevoidsaveToLocal(byte[]data,StringfilePath)
DataOutputStreamout=newDataOutputStream(
newFileOutputStream(newFile(filePath)));
data.length;
out.write(data[i]);
out.flush();
out.close();
}catch(IOExceptione){
/*下载url指向的网页*/
publicStringdownloadFile(Stringurl)
StringfilePath=null;
/*1.生成HttpClinet对象并设置参数*/
HttpClienthttpClient=newHttpClient();
//设置Http连接超时5s
httpClient.getHttpConnectionManager().getParams().
setConnectionTimeout(5000);
/*2.生成GetMethod对象并设置参数*/
GetMethodgetMethod=newGetMethod(url);
//设置get请求超时5s
getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT,5000);
//设置请求重试处理
getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
newDefaultHttpMethodRetryHandler());
/*3.执行HTTPGET请求*/
try{
intstatusCode=httpClient.executeMethod(getMethod);
//判断访问的状态码
if(statusCode!
=HttpStatus.SC_OK)
System.err.println("
Methodfailed:
+getMethod.getStatusLine());
filePath=null;
/*4.处理HTTP响应内容*/
byte[]responseBody=getMethod.getResponseBody();
//读取为字节数组
//根据网页url生成保存时的文件名
filePath="
temp\\"
+getFileNameByUrl(url,
getMethod.getResponseHeader("
Content-Type"
).getValue());
saveToLo
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 使用 HttpClient HtmlParser 实现简易爬虫 实现 简易 爬虫