TinySpider網(wǎng)絡(luò)數(shù)據(jù)抓取框架
TinySpider是一個(gè)基于Tiny HtmlParser的網(wǎng)絡(luò)數(shù)據(jù)抓取框架。
Maven引用坐標(biāo):
<dependency> <groupId>org.tinygroup</groupId> <artifactId>org.tinygroup.spider</artifactId> <version>0.1.0-SNAPSHOT</version> </dependency>
網(wǎng)絡(luò)爬蟲(chóng),一般用在全文檢索或內(nèi)容獲取上面。
Tiny框架對(duì)此也做了有限的支持,雖然功能不多,但是想做全文檢索或從網(wǎng)頁(yè)上獲取數(shù)據(jù)也是非常方便的。
框架特性
-
強(qiáng)大的節(jié)點(diǎn)過(guò)濾能力
-
支持post與get兩種數(shù)據(jù)提交方式
-
避免網(wǎng)頁(yè)重復(fù)處理功能
-
支持多站點(diǎn)內(nèi)容抓取功能
-
較強(qiáng)的Html容錯(cuò)處理
框架設(shè)計(jì)
網(wǎng)絡(luò)爬蟲(chóng)
public interface Spinder {
/**
* 添加站點(diǎn)訪問(wèn)器
*
* @param siteVisitor
*/
void addSiteVisitor(SiteVisitor siteVisitor);
/**
* 添加監(jiān)視器
*
* @param watcher
*/
void addWatcher(Watcher watcher);
/**
* 處理url
*
* @param url
*/
void processUrl(String url);
/**
* 處理url
* @param url
* @param parameter
*/
void processUrl(String url, Mapparameter);
/**
* 設(shè)置URL倉(cāng)庫(kù)
*
* @param urlRepository
*/
void setUrlRepository(UrlRepository urlRepository);
}
一個(gè)爬蟲(chóng),至少需要包含一個(gè)站點(diǎn)訪問(wèn)器,站點(diǎn)訪問(wèn)器用于對(duì)URL進(jìn)行訪問(wèn)。如果沒(méi)有匹配的站點(diǎn)訪問(wèn)器,URL將被忽略,不做繼續(xù)處理。
一個(gè)爬蟲(chóng)至少需要包含一個(gè)監(jiān)視器,監(jiān)視器用于對(duì)URL中的內(nèi)容進(jìn)行過(guò)濾,并對(duì)命中的節(jié)點(diǎn)進(jìn)行處理。如果沒(méi)有監(jiān)視器,爬蟲(chóng)爬回的內(nèi)容就沒(méi)有任何價(jià)值。
一個(gè)爬蟲(chóng)至少需要一個(gè)Url倉(cāng)庫(kù),Url倉(cāng)庫(kù)用于對(duì)ur進(jìn)行判斷,是否已經(jīng)抓取并處理過(guò)。如果沒(méi)有url倉(cāng)庫(kù),將無(wú)法判斷url是否處理過(guò),在非常多的時(shí)候,會(huì)造成死循環(huán),無(wú)法退出。
當(dāng)然,一個(gè)爬蟲(chóng),也必須能夠?qū)rl進(jìn)行處理。
網(wǎng)站訪問(wèn)者
由于一個(gè)爬蟲(chóng)可以有多個(gè)站點(diǎn)訪問(wèn)器,因此,需要有isMatch方法告訴爬蟲(chóng)是否應(yīng)該由自己進(jìn)行處理。
訪問(wèn)方式,可以設(shè)置是通過(guò)get還是post方式獲取數(shù)據(jù)。
URL倉(cāng)庫(kù)
public interface UrlRepository {
/**
* 返回url是否已經(jīng)在倉(cāng)庫(kù)中存在
*
* @param url
* @return
*/
boolean isExist(String url);
/**
* 返回url是否已經(jīng)在倉(cāng)庫(kù)中存在,帶有參數(shù)
*
* @param url
* @param parameter
* @return
*/
boolean isExist(String url, Mapparameter);
/**
* 如果不存在,則放放,如果已經(jīng)存在,則替換
*
* @param url
* @param content
*/
void putUrlWithContent(String url, String content);
/**
* 如果不存在,則放放,如果已經(jīng)存在,則替換
*
* @param url
* @param parameter
* @param content
*/
void putUrlWithContent(String url, Mapparameter,
String content);
/**
* 如果存在,則返回內(nèi)容;如果不存在,則拋出運(yùn)行時(shí)異常
*
* @param url
* @return
*/
String getContent(String url);
/**
* 如果存在,則返回內(nèi)容;如果不存在,則拋出運(yùn)行時(shí)異常
*
* @param url
* @param parameter
* @return
*/
String getContent(String url, Mapparameter);
}
url倉(cāng)庫(kù)用于對(duì)url及其內(nèi)容進(jìn)行管理。由于方法都簡(jiǎn)單明了,因此不做更多介紹。
監(jiān)視器
public interface Watcher {
/**
* 設(shè)置節(jié)點(diǎn)過(guò)濾器
*
* @param filter
*/
void setNodeFilter(NodeFilterfilter);
/**
* 獲取節(jié)點(diǎn)過(guò)濾器
*
* @return
*/
NodeFiltergetNodeFilter();
/**
* 添加處理器
*
* @param processor
*/
void addProcessor(Processor processor);
/**
* 獲取處理器列表
*
* @return
*/
ListgetProcessorList();
}
一個(gè)監(jiān)視器,必須一個(gè)節(jié)點(diǎn)過(guò)濾器,但是可以有多個(gè)處理器。
處理器
public interface Processor {
/**
* 處理節(jié)點(diǎn)
*
* @param node
*/
void process(HtmlNode node);
}
處理器非常簡(jiǎn)單, 就是對(duì)命中的節(jié)點(diǎn)進(jìn)行處理即可。
示例
通過(guò)訪問(wèn)[http://www.oschina.net/question?catalog=1]可以看到,里面有許多技術(shù)問(wèn)答的問(wèn)題。
下面我們就來(lái)編寫一段程序來(lái)把這些標(biāo)題打出來(lái):
編寫爬蟲(chóng)
public static void main(String[] args) {
Spinder spinder = new SpinderImpl();
Watcher watcher = new WatcherImpl();
watcher.addProcessor(new PrintOsChinaProcessor());
QuickNameFilternodeFilter = new QuickNameFilter();
nodeFilter.setNodeName("div");
nodeFilter.setIncludeAttribute("class", "qbody");
watcher.setNodeFilter(nodeFilter);
spinder.addWatcher(watcher);
spinder.processUrl("http://www.oschina.net/question?catalog=1");
}
編寫處理器
public class PrintOsChinaProcessor implements Processor {
public void process(HtmlNode node) {
FastNameFilterfilter = new FastNameFilter(node);
filter.setNodeName("h2");
filter.setIncludeNode("a");
HtmlNode h3 = filter.findNode();
if (h3 != null) {
System.out.println(h3.getSubNode("a").getContent());
}
}
}
運(yùn)行結(jié)果
輸出結(jié)果可能與結(jié)果不相同,因?yàn)閿?shù)據(jù)是一直在變化的。
約瑟夫環(huán)問(wèn)題,一段代碼求講解 求推薦一款分享,回復(fù)的前端開(kāi)源js MySQL什么情況使用MyISAM,什么時(shí)候使用InnoDB? phpstorm中使用搜狗輸入中文出現(xiàn)亂行問(wèn)題怎樣解決? Android中如何實(shí)現(xiàn)快播中娛樂(lè)風(fēng)向標(biāo)的效果 使用java做手機(jī)后臺(tái)開(kāi)發(fā)! Chrome 29的alert對(duì)話框好漂亮,有木有啊有木有 Eclipse+ADT+Android環(huán)境配置問(wèn)題 關(guān)于android holderview的疑惑 蛋疼 從一個(gè)公司到另外一個(gè)公司都是一個(gè)人開(kāi)發(fā) 有木有 wsunit 官方訪問(wèn)不了 android求大神給我看看什么問(wèn)題 關(guān)于Hibernate search 查詢結(jié)果與數(shù)據(jù)庫(kù)不相符的問(wèn)題 求推薦Oracle好的書(shū)籍或PDF 關(guān)于"記事本"的 "自動(dòng)換行" 的實(shí)現(xiàn) swing在線html文本編輯器 android下網(wǎng)絡(luò)阻塞問(wèn)題 文件上線系統(tǒng)該如何做(代碼上線) ztree節(jié)點(diǎn)設(shè)置成check多選框的時(shí)候如何只獲取葉節(jié)點(diǎn),不要其他節(jié)點(diǎn) 怎么設(shè)置上傳的圖片不自動(dòng)壓縮 js 正則表達(dá)式問(wèn)題 eclipse 經(jīng)常loading descriptor for XXX ,然后卡死 關(guān)于android開(kāi)發(fā)xml顯示問(wèn)題 RMI遠(yuǎn)程對(duì)象是共享的吧? 參與開(kāi)源項(xiàng)目如何進(jìn)行文檔編寫 php如何以文件圖標(biāo)的形式列出服務(wù)器上的所有文件? php中一個(gè)簡(jiǎn)單的問(wèn)題?請(qǐng)幫助解決一下,菜鳥(niǎo) 請(qǐng)教 solr query分詞查詢,結(jié)果為空的問(wèn)題 這段代碼有問(wèn)題嗎,怎么我運(yùn)行報(bào)錯(cuò)? jquery mobile 頁(yè)面中切換閃屏問(wèn)題 你幫我改好,我給你講個(gè)笑話可好TUT asp.net問(wèn)題:Js如何獲取cookie中的值? android 電話攔截并處理 iis7 下 php 如何顯示報(bào)錯(cuò)? 安裝virtualbox的時(shí)候提示要安裝通用串行總線控制器,這個(gè)要安裝嗎? API獲取新浪微博消息 工廠該不該有默認(rèn)行為 如何處理開(kāi)發(fā)過(guò)程中遺留無(wú)用的代碼 ireport 設(shè)計(jì)時(shí)報(bào)表模板時(shí),無(wú)法使用sybase驅(qū)動(dòng)com.sybase.jdbc3.jdbc.SybDriver? 關(guān)于 使用druid后的一些問(wèn)題.
小結(jié)
從示例可以看出,要從網(wǎng)頁(yè)里獲取數(shù)據(jù),確實(shí)是非常容易的一件事情,只寥寥幾行(20行左右),就采集出了我們想要的數(shù)據(jù),要想抓出更多的數(shù)據(jù),只要逐層細(xì)化分析即可。
