開源爬蟲vidageek crawer實例

最近想寫個爬蟲下點視頻,於是乎網絡上找找開源軟件。找來找去都沒找到自己滿意的輕量級爬蟲軟件。諸如:Nutch、Heritrix、webmagic、solr都感覺太龐大太複雜。

網絡上也有很多人利用httpclient和httpparser寫的爬蟲實例,但是又感覺太隨意。

然後,就發現了vidageek crawer,我如獲至寶,這不正是我想要的超級超級輕量級爬蟲框架嘛,該框架是利用開源工具httpclient、httpparser、log4j、icu4j等寫得一個多線程爬蟲框架,代碼量不多,如果遇到問題也可以自己調測代碼:

官網:http://projetos.vidageek.net/crawler/crawler/

下載地址:https://github.com/vidageek/crawler/downloads

官方還有javadoc可以參考。他們最後發佈時間是2011年,雖然其利用的開源工具都有比較大的更新,但是也不影響使用,如果有時間,也可以對其進行修改,將vidageek crawer更新到httpclient、log4j等的同步版本。


於是乎,利用該開源爬蟲小試了一把。官方是這麼說的:

“The main goal is to abstract that boring and error-prone code from your codebase and let you focus on crawling the site. Its quite easy to use it:
CrawlerConfiguration cfg = new CrawlerConfiguration("http://www.yoursite.com");
PageCrawler crawler = new PageCrawler(cfg);

You don't even need to know that there is code to make parallel requests, handle content encoding, find links on the pages, normalize those links and so on.
You just focus on implementing your net.vidageek.crawler.PageVisitor .”

也的確是這樣。我是在windows下利用jdk進行的測試,測試代碼目錄結構:

test
  |--first
  |    |--Spider.java /* 測試程序 */
  |--firstMakefile.bat
  |--fistRun.bat
  |--lib /* 依賴的jar包 */
  |    |--crawler-1.0.jar
  |    |--httpcomponents-client-4.4.1
  |    |     |--commons-codec-1.9.jar
  |    |     |--commons-logging-1.2.jar
  |    |     |--httpclient-4.4.1.jar
  |    |     |--httpcore-4.4.1.jar
  |    |--icu4j
  |    |    |--icu4j-charsetdetector-4_4_2.jar
  |    |--log4j
  |    |    |--log4j-1.2-api-2.3.jar
  |    |    |--log4j-api-2.3.jar
  |    |    |--log4j-core-2.3.jar
  |--log4j2.xml


1.windows環境cmd下如何搭建java jdk環境我就不說了 

2.下載依賴的jar包:crawler、httpclient、icu4j、log4j

3. Spider測試代碼:

Spider.class

package first;

import net.vidageek.crawler.config.CrawlerConfiguration;
import net.vidageek.crawler.PageCrawler;
import net.vidageek.crawler.PageVisitor;
import net.vidageek.crawler.Url;
import net.vidageek.crawler.Page;
import net.vidageek.crawler.Status;
import org.apache.logging.log4j.LogManager;  
import org.apache.logging.log4j.Logger;  

public class Spider
{
	public static void main(String[] args)
	{
		final Logger logger = LogManager.getLogger(Spider.class);
		PageVisitor visitor = new PageVisitor() {
			public boolean followUrl(Url url) {
				return false;
			}
			public void visit(Page page) {
				System.out.println("pageContent: " + page.getContent());
				logger.info("pageContent:\n " + page.getContent());
			}
			public void onError(Url errorUrl, Status statusError) {
				System.err.println("onError: " + errorUrl.toString() + "statusError: " + statusError);
			}
		};
		CrawlerConfiguration cfg = new CrawlerConfiguration("http://www.baidu.com/");
		PageCrawler crawler = new PageCrawler(cfg);
	
		crawler.crawl(visitor);
	}
}

4.cmd下敲命令感覺彆扭,沒有linux shell那麼嗨皮,所以利用bat腳本來編譯和運行程序。

firstMakefile.bat

javac -Xlint:depreciation -Djava.ext.dirs=.\lib;.\lib\log4j; .\first\*.java

firstRun.bat

java -Djava.ext.dirs=.\lib;.\lib\log4j;.\lib\httpcomponents-client-4.4.1;.\lib\icu4j first.Spider

5.最好,log4j2.xml主要是爲調測代碼和配置的日誌文件,如果不關注日誌,這個文件即使不要也能正常運行。

log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>

<!--
log4j2的配置文件只能採用.xml, .json或者.jsn。
在默認情況下,系統選擇configuration文件的優先級如下(rootdir爲項目當前執行目錄):
1.rootdir目錄下log4j2-test.json或者log4j2-test.jsn文件
2.rootdir目錄下log4j2-test.xml
3.rootdir目錄下log4j2.json或者log4j2.jsn文件
4.rootdir目錄下log4j2.xml
如果配置文件不在rootdir目錄下,則需要通過下列兩種方式指定配置文件路徑,兩種方法:
1.System.setProperty("log4j.configurationFile", "配置文件路徑")
2.-Dlog4j.configurationFile=配置文件路徑
-->

<Configuration status="WARN">
  <properties>
    <property name="LogHome">./logs</property>
	<property name="CrawlerLogName">crawler</property>
	<property name="SpiderLogName">spider</property>
  </properties>
  
  <Appenders>
	<!-- for console -->
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} [%thread] [%file:%line] - %msg%n"/>
    </Console>
    <!-- for debug -->
	<!-- filePattern: 表示當日志到達指定的大小或者時間而要產生新日誌時,舊日誌的命名規則 -->
	<RollingRandomAccessFile name="CrawlerLogFile" fileName="${LogHome}/${CrawlerLogName}.recent.log" filePattern="${LogHome}/${CrawlerLogName}.%d{yyyy-MM-dd-HH}.log">
		<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} [%thread] [%file:%line] - %msg%n"/>
		<Policies>
			<!-- 
			modulate: 說明是否對封存時間進行調製
			若modulate=true,則封存時間將以0點爲邊界進行偏移計算
			比如,modulate=true,interval=4,那麼假設上次封存日誌的時間爲3:00,
			則下次封存日誌的時間爲4:00,之後的封存時間依次爲8:00,12:00,16:00...
			-->
			<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
		</Policies>
	</RollingRandomAccessFile>
	<RollingRandomAccessFile name="SpiderLogFile" fileName="${LogHome}/${SpiderLogName}.recent.log" filePattern="${LogHome}/${SpiderLogName}.%d{yyyy-MM-dd-HH}.log">
		<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} [%thread] [%file:%line] - %msg%n"/>
		<Policies>
			<!-- 
			modulate: 說明是否對封存時間進行調製
			若modulate=true,則封存時間將以0點爲邊界進行偏移計算
			比如,modulate=true,interval=4,那麼假設上次封存日誌的時間爲3:00,
			則下次封存日誌的時間爲4:00,之後的封存時間依次爲8:00,12:00,16:00...
			-->
			<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
		</Policies>
	</RollingRandomAccessFile>
	<RollingFile name="RollingFile" fileName="${LogHome}/log.log" filePattern="${LogHome}/log.%d{yyyy-MM-dd-HH}.log">
		<SizeBasedTriggeringPolicy size="50MB"/>
	</RollingFile>
  </Appenders>
  
  <Loggers>
	<!-- additivity開啓的話,若這個logger也滿足root的話,將會在兩個地方輸出。 -->
	<logger name="net.vidageek.crawler" level="trace" additivity="false">
		<AppenderRef ref="CrawlerLogFile"/>
	</logger>
	<logger name="first.Spider" level="info" additivity="false">
		<AppenderRef ref="SpiderLogFile"/>
	</logger>
    <Root level="info"> <!--  trace < debug < info < warn < error < fatal -->
	  <AppenderRef ref="RollingFile"/>
	  <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>

當然,這款開源框架也還是有許多不足之處,首先用於實際工程項目是不行的,必須對其進行擴展,但是用於學習和折騰是最好不過了,比如:你就沒法配置網絡代理,沒有post方式,沒有所謂的bloom filter等,該框架僅僅是利用了List方式保存待爬取的鏈接。但是值得利用該爬蟲框架進行修修補補,自己可以添加可以修改,想怎麼折騰就怎麼折騰去吧。

實際上,該框架給用戶更大的自由,即在考慮繼承PageVistor的時候,通過followUrl方法自行實現對待爬取鏈接的過濾,此時你可以採取bloom filter對已爬取的鏈接進行過濾,或者隊列、hashmap方式等。框架中DoesNotFollowVisitedUrlVisitor類就是採用ConcurrentHashMap結構保存已爬取過的鏈接的:

final public class DoesNotFollowVisitedUrlVisitor implements PageVisitor {

    private final PageVisitor visitor;
    // Using map since jdk 1.5 does not provide a good concurrent set
    // implementation
    private final Map<Url, String> visitedUrls = new ConcurrentHashMap<Url, String>();

    public DoesNotFollowVisitedUrlVisitor(final String beginUrl, final PageVisitor visitor) {
        this.visitor = visitor;
        visitedUrls.put(new Url(beginUrl, 0), "");
    }

    public boolean followUrl(final Url url) {
        if (visitedUrls.get(url) != null) {
            return false;
        }
        visitedUrls.put(url, "");
        return visitor.followUrl(url);
    }

    public void onError(final Url url, final Status statusError) {
        visitor.onError(url, statusError);

    }

    public void visit(final Page page) {
        visitor.visit(page);
    }

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章