init
commit
359e965c7f
@ -0,0 +1,2 @@
|
|||||||
|
target/*
|
||||||
|
*.iml
|
@ -0,0 +1,105 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<groupId>us.codecraft</groupId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>webmagic</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
<version>4.2.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.7</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>13.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
<version>3.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>log4j</groupId>
|
||||||
|
<artifactId>log4j</artifactId>
|
||||||
|
<version>1.2.17</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-collections</groupId>
|
||||||
|
<artifactId>commons-collections</artifactId>
|
||||||
|
<version>3.2.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sourceforge.htmlcleaner</groupId>
|
||||||
|
<artifactId>htmlcleaner</artifactId>
|
||||||
|
<version>2.4</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>1.3.2</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>attach-sources</id>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>attach-javadocs</id>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-release-plugin</artifactId>
|
||||||
|
<version>2.0-beta-7</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,93 @@
|
|||||||
|
package us.codecraft.spider;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import us.codecraft.spider.selector.Selectable;
|
||||||
|
import us.codecraft.spider.utils.UrlUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 上午11:22
|
||||||
|
*/
|
||||||
|
public class Page {
|
||||||
|
|
||||||
|
private Request request;
|
||||||
|
|
||||||
|
private Map<String, Selectable> fields = new ConcurrentHashMap<String, Selectable>();
|
||||||
|
|
||||||
|
private Selectable html;
|
||||||
|
|
||||||
|
private Selectable url;
|
||||||
|
|
||||||
|
private List<Request> targetRequests = new ArrayList<Request>();
|
||||||
|
|
||||||
|
public void process() {
|
||||||
|
fields.put("title", html.x("").r(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Selectable> getFields() {
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putField(String key, Selectable field) {
|
||||||
|
fields.put(key, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Selectable getHtml() {
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHtml(Selectable html) {
|
||||||
|
this.html = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Request> getTargetRequests() {
|
||||||
|
return targetRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTargetRequests(List<String> requests) {
|
||||||
|
synchronized (targetRequests) {
|
||||||
|
for (String s : requests) {
|
||||||
|
if (StringUtils.isBlank(s) || s.equals("#") || s.startsWith("javascript:")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
s = UrlUtils.fixRelativeUrl(s, url.toString());
|
||||||
|
targetRequests.add(new Request(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTargetRequests(String requestString) {
|
||||||
|
if (StringUtils.isBlank(requestString) || requestString.equals("#")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
synchronized (targetRequests) {
|
||||||
|
requestString = UrlUtils.fixRelativeUrl(requestString, url.toString());
|
||||||
|
targetRequests.add(new Request(requestString));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Selectable getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrl(Selectable url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Request getRequest() {
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequest(Request request) {
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package us.codecraft.spider;
|
||||||
|
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 上午11:37
|
||||||
|
*/
|
||||||
|
public class Request {
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
private Object[] extra;
|
||||||
|
|
||||||
|
public Request(String url, Object... extra) {
|
||||||
|
this.url = url;
|
||||||
|
this.extra = extra;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object[] getExtra() {
|
||||||
|
return extra;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,126 @@
|
|||||||
|
package us.codecraft.spider;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午12:13
|
||||||
|
*/
|
||||||
|
public class Site {
|
||||||
|
|
||||||
|
private String domain;
|
||||||
|
|
||||||
|
private String userAgent;
|
||||||
|
|
||||||
|
private String cookie;
|
||||||
|
|
||||||
|
private String encoding;
|
||||||
|
|
||||||
|
private String startUrl;
|
||||||
|
|
||||||
|
private int sleepTime = 3000;
|
||||||
|
|
||||||
|
private static final Set<Integer> DEFAULT_STATUS_CODE_SET = new HashSet<Integer>();
|
||||||
|
|
||||||
|
private Set<Integer> acceptStatCode = DEFAULT_STATUS_CODE_SET;
|
||||||
|
|
||||||
|
static {
|
||||||
|
DEFAULT_STATUS_CODE_SET.add(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Site me() {
|
||||||
|
return new Site();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Site setCookie(String cookie) {
|
||||||
|
this.cookie = cookie;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Site setUserAgent(String userAgent) {
|
||||||
|
this.userAgent = userAgent;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCookie() {
|
||||||
|
return cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserAgent() {
|
||||||
|
return userAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDomain() {
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Site setDomain(String domain) {
|
||||||
|
this.domain = domain;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEncoding() {
|
||||||
|
return encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Site setEncoding(String encoding) {
|
||||||
|
this.encoding = encoding;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Integer> getAcceptStatCode() {
|
||||||
|
return acceptStatCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Site setAcceptStatCode(Set<Integer> acceptStatCode) {
|
||||||
|
this.acceptStatCode = acceptStatCode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStartUrl() {
|
||||||
|
return startUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Site setStartUrl(String startUrl) {
|
||||||
|
this.startUrl = startUrl;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSleepTime() {
|
||||||
|
return sleepTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Site setSleepTime(int sleepTime) {
|
||||||
|
this.sleepTime = sleepTime;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
Site site = (Site) o;
|
||||||
|
|
||||||
|
if (acceptStatCode != null ? !acceptStatCode.equals(site.acceptStatCode) : site.acceptStatCode != null)
|
||||||
|
return false;
|
||||||
|
if (cookie != null ? !cookie.equals(site.cookie) : site.cookie != null) return false;
|
||||||
|
if (!domain.equals(site.domain)) return false;
|
||||||
|
if (encoding != null ? !encoding.equals(site.encoding) : site.encoding != null) return false;
|
||||||
|
if (userAgent != null ? !userAgent.equals(site.userAgent) : site.userAgent != null) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = domain.hashCode();
|
||||||
|
result = 31 * result + (userAgent != null ? userAgent.hashCode() : 0);
|
||||||
|
result = 31 * result + (cookie != null ? cookie.hashCode() : 0);
|
||||||
|
result = 31 * result + (encoding != null ? encoding.hashCode() : 0);
|
||||||
|
result = 31 * result + (acceptStatCode != null ? acceptStatCode.hashCode() : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
package us.codecraft.spider;
|
||||||
|
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import us.codecraft.spider.downloader.Downloader;
|
||||||
|
import us.codecraft.spider.downloader.HttpClientDownloader;
|
||||||
|
import us.codecraft.spider.pipeline.ConsolePipeline;
|
||||||
|
import us.codecraft.spider.pipeline.Pipeline;
|
||||||
|
import us.codecraft.spider.processor.PageProcessor;
|
||||||
|
import us.codecraft.spider.schedular.QueueSchedular;
|
||||||
|
import us.codecraft.spider.schedular.Schedular;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 上午6:53
|
||||||
|
*/
|
||||||
|
public class Spider implements Runnable {
|
||||||
|
|
||||||
|
private Downloader downloader = new HttpClientDownloader();
|
||||||
|
|
||||||
|
private Pipeline pipeline = new ConsolePipeline();
|
||||||
|
|
||||||
|
private PageProcessor pageProcessor;
|
||||||
|
|
||||||
|
private Schedular schedular = new QueueSchedular();
|
||||||
|
|
||||||
|
private Logger logger = Logger.getLogger(getClass());
|
||||||
|
|
||||||
|
public static Spider me() {
|
||||||
|
return new Spider();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Spider processor(PageProcessor pageProcessor) {
|
||||||
|
this.pageProcessor = pageProcessor;
|
||||||
|
schedular.push(new Request(pageProcessor.getSite().getStartUrl()), pageProcessor.getSite());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Thread thread() {
|
||||||
|
return new Thread(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Spider schedular(Schedular schedular) {
|
||||||
|
this.schedular = schedular;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Spider pipeline(Pipeline pipeline) {
|
||||||
|
this.pipeline = pipeline;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Site site = pageProcessor.getSite();
|
||||||
|
Request request = schedular.poll(site);
|
||||||
|
while (request != null) {
|
||||||
|
Page page = downloader.download(request,site);
|
||||||
|
if (page == null) {
|
||||||
|
sleep(site.getSleepTime());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pageProcessor.process(page);
|
||||||
|
addRequest(page);
|
||||||
|
pipeline.process(page,site);
|
||||||
|
sleep(site.getSleepTime());
|
||||||
|
request = schedular.poll(site);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sleep(int time) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(time);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRequest(Page page) {
|
||||||
|
if (CollectionUtils.isNotEmpty(page.getTargetRequests())) {
|
||||||
|
for (Request request : page.getTargetRequests()) {
|
||||||
|
schedular.push(request,pageProcessor.getSite());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package us.codecraft.spider.downloader;
|
||||||
|
|
||||||
|
import us.codecraft.spider.Page;
|
||||||
|
import us.codecraft.spider.Request;
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午12:14
|
||||||
|
*/
|
||||||
|
public interface Downloader {
|
||||||
|
|
||||||
|
public Page download(Request request,Site site);
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package us.codecraft.spider.downloader;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import us.codecraft.spider.Page;
|
||||||
|
import us.codecraft.spider.Request;
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
import us.codecraft.spider.selector.Html;
|
||||||
|
import us.codecraft.spider.selector.PlainText;
|
||||||
|
import us.codecraft.spider.utils.UrlUtils;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午12:15
|
||||||
|
*/
|
||||||
|
public class HttpClientDownloader implements Downloader {
|
||||||
|
|
||||||
|
private Logger logger = Logger.getLogger(getClass());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page download(Request request, Site site) {
|
||||||
|
logger.info("downloading page " + request.getUrl());
|
||||||
|
HttpClient httpClient = HttpClientPool.getInstance().getClient(site);
|
||||||
|
try {
|
||||||
|
HttpGet httpGet = new HttpGet(request.getUrl());
|
||||||
|
HttpResponse httpResponse = httpClient.execute(httpGet);
|
||||||
|
int statusCode = httpResponse.getStatusLine().getStatusCode();
|
||||||
|
if (site.getAcceptStatCode().contains(statusCode)) {
|
||||||
|
String content = IOUtils.toString(httpResponse.getEntity().getContent(),
|
||||||
|
site.getEncoding() == null ? site.getEncoding() : httpResponse.getEntity().getContentType().getValue());
|
||||||
|
Page page = new Page();
|
||||||
|
page.setHtml(new Html(UrlUtils.fixAllRelativeHrefs(content, request.getUrl())));
|
||||||
|
page.setUrl(new PlainText(request.getUrl()));
|
||||||
|
page.setRequest(request);
|
||||||
|
return page;
|
||||||
|
} else {
|
||||||
|
logger.warn("code error " + statusCode);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("download page " + request.getUrl() + " error", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
package us.codecraft.spider.downloader;
|
||||||
|
|
||||||
|
import org.apache.http.HttpVersion;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.client.params.ClientPNames;
|
||||||
|
import org.apache.http.client.params.CookiePolicy;
|
||||||
|
import org.apache.http.conn.scheme.PlainSocketFactory;
|
||||||
|
import org.apache.http.conn.scheme.Scheme;
|
||||||
|
import org.apache.http.conn.scheme.SchemeRegistry;
|
||||||
|
import org.apache.http.impl.client.DefaultHttpClient;
|
||||||
|
import org.apache.http.impl.conn.PoolingClientConnectionManager;
|
||||||
|
import org.apache.http.params.*;
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午12:29
|
||||||
|
*/
|
||||||
|
public class HttpClientPool {
|
||||||
|
|
||||||
|
public static final HttpClientPool INSTANCE = new HttpClientPool(5);
|
||||||
|
|
||||||
|
public static HttpClientPool getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int poolSize;
|
||||||
|
|
||||||
|
private HttpClientPool(int poolSize) {
|
||||||
|
this.poolSize = poolSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpClient getClient(Site site) {
|
||||||
|
return generateClient(site);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpClient generateClient(Site site) {
|
||||||
|
HttpParams params = new BasicHttpParams();
|
||||||
|
params.setParameter(CoreProtocolPNames.USER_AGENT, site.getUserAgent());
|
||||||
|
params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 1000);
|
||||||
|
params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 2000);
|
||||||
|
|
||||||
|
HttpProtocolParamBean paramsBean = new HttpProtocolParamBean(params);
|
||||||
|
paramsBean.setVersion(HttpVersion.HTTP_1_1);
|
||||||
|
paramsBean.setContentCharset("UTF-8");
|
||||||
|
paramsBean.setUseExpectContinue(false);
|
||||||
|
|
||||||
|
SchemeRegistry schemeRegistry = new SchemeRegistry();
|
||||||
|
schemeRegistry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
|
||||||
|
|
||||||
|
PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager(schemeRegistry);
|
||||||
|
connectionManager.setMaxTotal(100);
|
||||||
|
connectionManager.setDefaultMaxPerRoute(100);
|
||||||
|
HttpClient httpClient = new DefaultHttpClient(connectionManager, params);
|
||||||
|
httpClient.getParams().setIntParameter("http.socket.timeout", 60000);
|
||||||
|
httpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BEST_MATCH);
|
||||||
|
return httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pushBack(HttpClient httpClient) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package us.codecraft.spider.pipeline;
|
||||||
|
|
||||||
|
import us.codecraft.spider.Page;
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
import us.codecraft.spider.selector.Selectable;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午1:45
|
||||||
|
*/
|
||||||
|
public class ConsolePipeline implements Pipeline{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(Page page,Site site) {
|
||||||
|
System.out.println("get page: "+page.getUrl());
|
||||||
|
for (Map.Entry<String, Selectable> entry : page.getFields().entrySet()) {
|
||||||
|
System.out.println(entry.getKey()+":\t"+entry.getValue().toStrings());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package us.codecraft.spider.pipeline;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import us.codecraft.spider.Page;
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
import us.codecraft.spider.selector.Selectable;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午6:28
|
||||||
|
*/
|
||||||
|
public class FilePipeline implements Pipeline {
|
||||||
|
|
||||||
|
private String path = "/data/temp/spider/";
|
||||||
|
|
||||||
|
public FilePipeline(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public FilePipeline(String path) {
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(Page page,Site site) {
|
||||||
|
String domain = site.getDomain();
|
||||||
|
domain = StringUtils.removeStart(domain, "http://");
|
||||||
|
domain = StringUtils.removeStart(domain, "https://");
|
||||||
|
domain = StringUtils.replace(domain, "/", "");
|
||||||
|
String path = this.path + "" + domain + "/";
|
||||||
|
File file = new File(path);
|
||||||
|
if (!file.exists()) {
|
||||||
|
file.mkdir();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
PrintWriter printWriter = new PrintWriter(new FileWriter(path + DigestUtils.md5Hex(page.getUrl().toString()) + ".html"));
|
||||||
|
printWriter.println("url:\t" + page.getUrl());
|
||||||
|
for (Map.Entry<String, Selectable> entry : page.getFields().entrySet()) {
|
||||||
|
printWriter.println(entry.getKey() + ":\t" + entry.getValue().toStrings());
|
||||||
|
}
|
||||||
|
printWriter.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package us.codecraft.spider.pipeline;
|
||||||
|
|
||||||
|
import us.codecraft.spider.Page;
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午1:39
|
||||||
|
*/
|
||||||
|
public interface Pipeline {
|
||||||
|
|
||||||
|
public void process(Page page,Site site);
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package us.codecraft.spider.processor;
|
||||||
|
|
||||||
|
import us.codecraft.spider.Page;
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 上午11:42
|
||||||
|
*/
|
||||||
|
public interface PageProcessor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* extends the class to implements variaty spiders
|
||||||
|
* @param page
|
||||||
|
*/
|
||||||
|
public void process(Page page);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the site the processor for
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Site getSite();
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package us.codecraft.spider.processor;
|
||||||
|
|
||||||
|
import us.codecraft.spider.Page;
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
import us.codecraft.spider.utils.UrlUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-22
|
||||||
|
* Time: 下午9:15
|
||||||
|
*/
|
||||||
|
public class SimplePageProcessor implements PageProcessor {
|
||||||
|
|
||||||
|
private String urlPattern;
|
||||||
|
|
||||||
|
private static final String UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31";
|
||||||
|
|
||||||
|
private Site site;
|
||||||
|
|
||||||
|
public SimplePageProcessor(String startUrl, String urlPattern) {
|
||||||
|
this.site = Site.me().setStartUrl(startUrl).
|
||||||
|
setDomain(UrlUtils.getDomain(startUrl)).setUserAgent(UA);
|
||||||
|
this.urlPattern = "("+urlPattern.replace(".","\\.").replace("*","[^\"'#]*")+")";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(Page page) {
|
||||||
|
List<String> requests = page.getHtml().as().rs(urlPattern).toStrings();
|
||||||
|
page.addTargetRequests(requests);
|
||||||
|
page.putField("title", page.getHtml().x("//title"));
|
||||||
|
page.putField("content", page.getHtml().sc());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Site getSite() {
|
||||||
|
return site;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,142 @@
|
|||||||
|
package us.codecraft.spider.schedular;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.math.NumberUtils;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
import us.codecraft.spider.Request;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午1:13
|
||||||
|
*/
|
||||||
|
public class FileCacheQueueSchedular implements Schedular {
|
||||||
|
|
||||||
|
private Logger logger = Logger.getLogger(getClass());
|
||||||
|
|
||||||
|
private String filePath = System.getProperty("java.io.tmpdir");
|
||||||
|
|
||||||
|
private String fileUrlAllName = ".urls.txt";
|
||||||
|
|
||||||
|
private Site site;
|
||||||
|
|
||||||
|
private String fileCursor = ".cursor.txt";
|
||||||
|
|
||||||
|
private PrintWriter fileUrlWriter;
|
||||||
|
|
||||||
|
private PrintWriter fileCursorWriter;
|
||||||
|
|
||||||
|
private AtomicInteger cursor = new AtomicInteger();
|
||||||
|
|
||||||
|
private AtomicBoolean inited = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
private BlockingQueue<Request> queue;
|
||||||
|
|
||||||
|
private Set<String> urls;
|
||||||
|
|
||||||
|
public FileCacheQueueSchedular(Site site) {
|
||||||
|
this.site = site;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileCacheQueueSchedular(Site site, String filePath) {
|
||||||
|
this.filePath = filePath;
|
||||||
|
this.site = site;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void flush() {
|
||||||
|
fileUrlWriter.flush();
|
||||||
|
fileCursorWriter.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
readFile();
|
||||||
|
initWriter();
|
||||||
|
initFlushThread();
|
||||||
|
inited.set(true);
|
||||||
|
logger.info("init cache schedular success");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initFlushThread() {
|
||||||
|
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
}, 10, 10, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initWriter() {
|
||||||
|
try {
|
||||||
|
fileUrlWriter = new PrintWriter(new FileWriter(filePath + site.getDomain() + fileUrlAllName, true));
|
||||||
|
fileCursorWriter = new PrintWriter(new FileWriter(filePath + site.getDomain() + fileCursor, false));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("init cache schedular error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readFile() {
|
||||||
|
try {
|
||||||
|
queue = new LinkedBlockingQueue<Request>();
|
||||||
|
urls = new LinkedHashSet<String>();
|
||||||
|
readCursorFile();
|
||||||
|
readUrlFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readUrlFile() throws IOException {
|
||||||
|
String line;
|
||||||
|
BufferedReader fileUrlReader = new BufferedReader(new FileReader(filePath + site.getDomain() + fileUrlAllName));
|
||||||
|
int lineReaded = 0;
|
||||||
|
while ((line = fileUrlReader.readLine()) != null) {
|
||||||
|
urls.add(line.trim());
|
||||||
|
lineReaded++;
|
||||||
|
if (lineReaded > cursor.get()) {
|
||||||
|
queue.add(new Request(line, site));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readCursorFile() throws IOException {
|
||||||
|
BufferedReader fileCursorReader = new BufferedReader(new FileReader(filePath + site.getDomain() + fileCursor));
|
||||||
|
String line = null;
|
||||||
|
//read the last number
|
||||||
|
while ((line = fileCursorReader.readLine()) != null) {
|
||||||
|
cursor = new AtomicInteger(NumberUtils.toInt(line));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void push(Request request,Site site) {
|
||||||
|
if (!inited.get()) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("push to queue " + request.getUrl());
|
||||||
|
}
|
||||||
|
if (urls.add(request.getUrl())) {
|
||||||
|
queue.add(request);
|
||||||
|
fileUrlWriter.println(request.getUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Request poll(Site site) {
|
||||||
|
if (!inited.get()) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
fileCursorWriter.println(cursor.incrementAndGet());
|
||||||
|
return queue.poll();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package us.codecraft.spider.schedular;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import us.codecraft.spider.Request;
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午1:13
|
||||||
|
*/
|
||||||
|
public class QueueSchedular implements Schedular {
|
||||||
|
|
||||||
|
private Logger logger = Logger.getLogger(getClass());
|
||||||
|
|
||||||
|
private BlockingQueue<Request> queue = new LinkedBlockingQueue<Request>();
|
||||||
|
|
||||||
|
private Set<String> urls = new HashSet<String>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void push(Request request,Site site) {
|
||||||
|
if (logger.isDebugEnabled()){
|
||||||
|
logger.debug("push to queue "+request.getUrl());
|
||||||
|
}
|
||||||
|
if (urls.add(request.getUrl())){
|
||||||
|
queue.add(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized Request poll(Site site) {
|
||||||
|
return queue.poll();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package us.codecraft.spider.schedular;
|
||||||
|
|
||||||
|
import us.codecraft.spider.Request;
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午1:12
|
||||||
|
*/
|
||||||
|
public interface Schedular {
|
||||||
|
|
||||||
|
public void push(Request request,Site site);
|
||||||
|
|
||||||
|
public Request poll(Site site);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
package us.codecraft.spider.selector;
|
||||||
|
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 上午7:54
|
||||||
|
*/
|
||||||
|
public class Html extends PlainText {
|
||||||
|
|
||||||
|
public Html(List<String> strings) {
|
||||||
|
super(strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Html(String text) {
|
||||||
|
super(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Selectable x(String xpath) {
|
||||||
|
XpathSelector xpathSelector = SelectorFactory.getInstatnce().newXpathSelector(xpath);
|
||||||
|
return select(xpathSelector,strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Selectable select(Selector selector, List<String> strings) {
|
||||||
|
List<String> results = new ArrayList<String>();
|
||||||
|
for (String string : strings) {
|
||||||
|
String result = selector.select(string);
|
||||||
|
if (result!=null){
|
||||||
|
results.add(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Html(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Selectable selectList(Selector selector, List<String> strings) {
|
||||||
|
List<String> results = new ArrayList<String>();
|
||||||
|
for (String string : strings) {
|
||||||
|
List<String> result = selector.selectList(string);
|
||||||
|
results.addAll(result);
|
||||||
|
}
|
||||||
|
return new Html(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Selectable sc() {
|
||||||
|
SmartContentSelector smartContentSelector = SelectorFactory.getInstatnce().newSmartContentSelector();
|
||||||
|
return select(smartContentSelector,strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Selectable a() {
|
||||||
|
XpathSelector xpathSelector = SelectorFactory.getInstatnce().newXpathSelector("//a/@href");
|
||||||
|
return select(xpathSelector,strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Selectable as() {
|
||||||
|
XpathSelector xpathSelector = SelectorFactory.getInstatnce().newXpathSelector("//a/@href");
|
||||||
|
return selectList(xpathSelector,strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Selectable xs(String xpath) {
|
||||||
|
XpathSelector xpathSelector = SelectorFactory.getInstatnce().newXpathSelector(xpath);
|
||||||
|
return selectList(xpathSelector, strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
package us.codecraft.spider.selector;
|
||||||
|
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 上午7:54
|
||||||
|
*/
|
||||||
|
public class PlainText implements Selectable {
|
||||||
|
|
||||||
|
protected List<String> strings;
|
||||||
|
|
||||||
|
public PlainText(List<String> strings) {
|
||||||
|
this.strings = strings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlainText(String text) {
|
||||||
|
List<String> results = new ArrayList<String>();
|
||||||
|
results.add(text);
|
||||||
|
this.strings = results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Selectable x(String xpath) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Selectable xs(String xpath) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Selectable sc() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Selectable a() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Selectable as() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Selectable r(String regex) {
|
||||||
|
RegexSelector regexSelector = SelectorFactory.getInstatnce().newRegexSelector(regex);
|
||||||
|
return select(regexSelector, strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Selectable rs(String regex) {
|
||||||
|
RegexSelector regexSelector = SelectorFactory.getInstatnce().newRegexSelector(regex);
|
||||||
|
return selectList(regexSelector, strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Selectable select(Selector selector, List<String> strings) {
|
||||||
|
List<String> results = new ArrayList<String>();
|
||||||
|
for (String string : strings) {
|
||||||
|
String result = selector.select(string);
|
||||||
|
if (result!=null){
|
||||||
|
results.add(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new PlainText(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Selectable selectList(Selector selector, List<String> strings) {
|
||||||
|
List<String> results = new ArrayList<String>();
|
||||||
|
for (String string : strings) {
|
||||||
|
List<String> result = selector.selectList(string);
|
||||||
|
results.addAll(result);
|
||||||
|
}
|
||||||
|
return new PlainText(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Selectable rp(String regex, String replacement) {
|
||||||
|
ReplaceSelector replaceSelector = SelectorFactory.getInstatnce().newReplaceSelector(regex, replacement);
|
||||||
|
return select(replaceSelector, strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> toStrings() {
|
||||||
|
return strings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (CollectionUtils.isNotEmpty(toStrings())) {
|
||||||
|
return toStrings().get(0);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package us.codecraft.spider.selector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 上午7:39
|
||||||
|
*/
|
||||||
|
class RegexResult {
|
||||||
|
|
||||||
|
private String[] groups;
|
||||||
|
|
||||||
|
public static final RegexResult EMPTY_RESULT = new RegexResult();
|
||||||
|
|
||||||
|
public RegexResult() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public RegexResult(String[] groups) {
|
||||||
|
this.groups = groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String get(int groupId) {
|
||||||
|
if (groups == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return groups[groupId];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
package us.codecraft.spider.selector;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.regex.PatternSyntaxException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 上午7:09
|
||||||
|
*/
|
||||||
|
public class RegexSelector implements Selector {
|
||||||
|
|
||||||
|
private String regexStr;
|
||||||
|
|
||||||
|
private Pattern regex;
|
||||||
|
|
||||||
|
public RegexSelector(String regexStr) {
|
||||||
|
if (StringUtils.isBlank(regexStr)){
|
||||||
|
throw new IllegalArgumentException("regex must not be empty");
|
||||||
|
}
|
||||||
|
if (!StringUtils.contains(regexStr,"(")||!StringUtils.contains(regexStr,")")){
|
||||||
|
throw new IllegalArgumentException("regex must have capture group 1");
|
||||||
|
}
|
||||||
|
this.regexStr = regexStr;
|
||||||
|
try {
|
||||||
|
regex = Pattern.compile(regexStr,Pattern.DOTALL|Pattern.CASE_INSENSITIVE);
|
||||||
|
} catch (PatternSyntaxException e) {
|
||||||
|
throw new IllegalArgumentException("invalid regex", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String select(String text) {
|
||||||
|
return selectGroup(text).get(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> selectList(String text) {
|
||||||
|
List<String> strings=new ArrayList<String>();
|
||||||
|
List<RegexResult> results = selectGroupList(text);
|
||||||
|
for (RegexResult result : results) {
|
||||||
|
strings.add(result.get(1));
|
||||||
|
}
|
||||||
|
return strings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RegexResult selectGroup(String text) {
|
||||||
|
Matcher matcher = regex.matcher(text);
|
||||||
|
if (matcher.find()) {
|
||||||
|
String[] groups = new String[matcher.groupCount()+1];
|
||||||
|
for (int i = 0; i < groups.length; i++) {
|
||||||
|
groups[i] = matcher.group(i);
|
||||||
|
}
|
||||||
|
return new RegexResult(groups);
|
||||||
|
}
|
||||||
|
return RegexResult.EMPTY_RESULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RegexResult> selectGroupList(String text) {
|
||||||
|
Matcher matcher = regex.matcher(text);
|
||||||
|
List<RegexResult> resultList = new ArrayList<RegexResult>();
|
||||||
|
while (matcher.find()) {
|
||||||
|
String[] groups = new String[matcher.groupCount()+1];
|
||||||
|
for (int i = 0; i < groups.length; i++) {
|
||||||
|
groups[i] = matcher.group(i);
|
||||||
|
}
|
||||||
|
resultList.add(new RegexResult(groups));
|
||||||
|
}
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return regexStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package us.codecraft.spider.selector;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.regex.PatternSyntaxException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 上午7:09
|
||||||
|
*/
|
||||||
|
public class ReplaceSelector implements Selector {
|
||||||
|
|
||||||
|
private String regexStr;
|
||||||
|
|
||||||
|
private String replacement;
|
||||||
|
|
||||||
|
private Pattern regex;
|
||||||
|
|
||||||
|
public ReplaceSelector(String regexStr, String replacement) {
|
||||||
|
this.regexStr = regexStr;
|
||||||
|
this.replacement = replacement;
|
||||||
|
try {
|
||||||
|
regex = Pattern.compile(regexStr);
|
||||||
|
} catch (PatternSyntaxException e) {
|
||||||
|
throw new IllegalArgumentException("invalid regex", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String select(String text) {
|
||||||
|
Matcher matcher = regex.matcher(text);
|
||||||
|
return matcher.replaceAll(replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> selectList(String text) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return regexStr + "_" + replacement;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
package us.codecraft.spider.selector;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-20
|
||||||
|
* Time: 下午7:51
|
||||||
|
*/
|
||||||
|
public interface Selectable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* select with xpath
|
||||||
|
*
|
||||||
|
* @param xpath
|
||||||
|
* @return new Selectable after extract
|
||||||
|
*/
|
||||||
|
public Selectable x(String xpath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* select list with xpath
|
||||||
|
*
|
||||||
|
* @param xpath
|
||||||
|
* @return new Selectable after extract
|
||||||
|
*/
|
||||||
|
public Selectable xs(String xpath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* select smart content with ReadAbility algorithm
|
||||||
|
*
|
||||||
|
* @return content
|
||||||
|
*/
|
||||||
|
public Selectable sc();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* select a link
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Selectable a();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* select all links
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Selectable as();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* select with regex
|
||||||
|
*
|
||||||
|
* @param regex
|
||||||
|
* @return new Selectable after extract
|
||||||
|
*/
|
||||||
|
public Selectable r(String regex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* select list with regex
|
||||||
|
*
|
||||||
|
* @param regex
|
||||||
|
* @return new Selectable after extract
|
||||||
|
*/
|
||||||
|
public Selectable rs(String regex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* replace with regex
|
||||||
|
*
|
||||||
|
* @param regex
|
||||||
|
* @param replacement
|
||||||
|
* @return new Selectable after extract
|
||||||
|
*/
|
||||||
|
public Selectable rp(String regex, String replacement);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* single string result
|
||||||
|
*
|
||||||
|
* @return single string result
|
||||||
|
*/
|
||||||
|
public String toString();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* multi string result
|
||||||
|
*
|
||||||
|
* @return multi string result
|
||||||
|
*/
|
||||||
|
public List<String> toStrings();
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package us.codecraft.spider.selector;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-20
|
||||||
|
* Time: 下午8:02
|
||||||
|
*/
|
||||||
|
public interface Selector {
|
||||||
|
|
||||||
|
public String select(String text);
|
||||||
|
|
||||||
|
public List<String> selectList(String text);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
package us.codecraft.spider.selector;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 上午7:56
|
||||||
|
*/
|
||||||
|
public class SelectorFactory {
|
||||||
|
|
||||||
|
private Map<String, Selector> innerCache = new ConcurrentHashMap<String, Selector>();
|
||||||
|
|
||||||
|
private static final SelectorFactory INSTATNCE = new SelectorFactory();
|
||||||
|
|
||||||
|
public static SelectorFactory getInstatnce() {
|
||||||
|
return INSTATNCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RegexSelector newRegexSelector(String regex) {
|
||||||
|
return newSelector(RegexSelector.class, regex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReplaceSelector newReplaceSelector(String regex, String replacement) {
|
||||||
|
return newSelector(ReplaceSelector.class, regex, replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
public XpathSelector newXpathSelector(String xpath) {
|
||||||
|
return newSelector(XpathSelector.class, xpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SmartContentSelector newSmartContentSelector(){
|
||||||
|
return newSelector(SmartContentSelector.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Selector> T newAndCacheSelector(Class<T> clazz, String... param) {
|
||||||
|
String cacheKey = getCacheKey(RegexSelector.class, param);
|
||||||
|
if (innerCache.get(cacheKey) != null) {
|
||||||
|
return (T) innerCache.get(cacheKey);
|
||||||
|
}
|
||||||
|
T selector = newSelector(clazz, param);
|
||||||
|
if (selector != null) {
|
||||||
|
innerCache.put(cacheKey, selector);
|
||||||
|
}
|
||||||
|
return selector;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Selector> T newSelector(Class<T> clazz, String... param) {
|
||||||
|
try {
|
||||||
|
if (param.length == 0) {
|
||||||
|
Constructor<T> constructor
|
||||||
|
= clazz.getConstructor();
|
||||||
|
T selector = constructor.newInstance();
|
||||||
|
return selector;
|
||||||
|
} else if (param.length == 1) {
|
||||||
|
Constructor<T> constructor
|
||||||
|
= clazz.getConstructor(String.class);
|
||||||
|
T selector = constructor.newInstance(param[0]);
|
||||||
|
return selector;
|
||||||
|
} else if (param.length == 2) {
|
||||||
|
Constructor<T> constructor
|
||||||
|
= clazz.getConstructor(String.class, String.class);
|
||||||
|
T selector = constructor.newInstance(param[0], param[1]);
|
||||||
|
return selector;
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw new IllegalArgumentException("init object error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCacheKey(Class<?> clazz, String... param) {
|
||||||
|
return clazz.toString() + "_" + StringUtils.join(param, "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
package us.codecraft.spider.selector;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.htmlcleaner.HtmlCleaner;
|
||||||
|
import org.htmlcleaner.TagNode;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 找到clear
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午4:42
|
||||||
|
*/
|
||||||
|
public class SmartContentSelector implements Selector {
|
||||||
|
|
||||||
|
private Logger logger = Logger.getLogger(getClass());
|
||||||
|
|
||||||
|
public SmartContentSelector() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String select(String text) {
|
||||||
|
HtmlCleaner htmlCleaner = new HtmlCleaner();
|
||||||
|
TagNode tagNode = htmlCleaner.clean(text);
|
||||||
|
if (tagNode == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
TagNode[] nodes = tagNode.getElementsByName("p", true);
|
||||||
|
TagNode[] pres = tagNode.getElementsByName("pre", true);
|
||||||
|
Map<TagNode, Double> pDensityCountMap = new HashMap<TagNode, Double>();
|
||||||
|
countPdensity(nodes, pDensityCountMap);
|
||||||
|
countPdensity(pres, pDensityCountMap);
|
||||||
|
for (TagNode pre : pres) {
|
||||||
|
addCounter(pre, pDensityCountMap, 2);
|
||||||
|
}
|
||||||
|
List<Map.Entry<TagNode, Double>> sortList = new ArrayList<Map.Entry<TagNode, Double>>();
|
||||||
|
if (pDensityCountMap.size() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (Map.Entry<TagNode, Double> entry : pDensityCountMap.entrySet()) {
|
||||||
|
// if (logger.isDebugEnabled()) {
|
||||||
|
// logger.debug("p\t" + entry.getKey().getName() + "#" + entry.getKey().getAttributeByName("id") +
|
||||||
|
// "@" + entry.getKey().getAttributeByName("class") + ":" + entry.getValue());
|
||||||
|
// }
|
||||||
|
sortList.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(sortList, new Comparator<Map.Entry<TagNode, Double>>() {
|
||||||
|
@Override
|
||||||
|
public int compare(Map.Entry<TagNode, Double> o1, Map.Entry<TagNode, Double> o2) {
|
||||||
|
Double d1 = o1.getValue();
|
||||||
|
Double d2 = o2.getValue();
|
||||||
|
return -d1.compareTo(d2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
TagNode contentNode = sortList.get(0).getKey();
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("p\t" + contentNode.getName() + "#" + contentNode.getAttributeByName("id") +
|
||||||
|
"@" + contentNode.getAttributeByName("class"));
|
||||||
|
}
|
||||||
|
return htmlCleaner.getInnerHtml(contentNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCounter(TagNode node, Map<TagNode, Double> countMap, double delta) {
|
||||||
|
Double counter = countMap.get(node);
|
||||||
|
if (counter == null) {
|
||||||
|
countMap.put(node, delta);
|
||||||
|
} else {
|
||||||
|
countMap.put(node, counter + delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final double parentWeight = 0.7;
|
||||||
|
|
||||||
|
private void countPdensity(TagNode[] nodes, Map<TagNode, Double> pDensityCountMap) {
|
||||||
|
for (TagNode node : nodes) {
|
||||||
|
if (node == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
TagNode parent = node.getParent();
|
||||||
|
double pDensity = 1;
|
||||||
|
while (parent != null) {
|
||||||
|
addCounter(parent, pDensityCountMap, pDensity);
|
||||||
|
parent = parent.getParent();
|
||||||
|
pDensity = pDensity * parentWeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TagNode findLowestCommonParent(List<TagNode> tagNodes, int maxMargin, Map<TagNode, AtomicInteger> countMap) {
|
||||||
|
TagNode contentNode = tagNodes.get(0);
|
||||||
|
return contentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> selectList(String text) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
package us.codecraft.spider.selector;
|
||||||
|
|
||||||
|
import org.htmlcleaner.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 上午9:39
|
||||||
|
*/
|
||||||
|
public class XpathSelector implements Selector {
|
||||||
|
|
||||||
|
private String xpathStr;
|
||||||
|
|
||||||
|
public XpathSelector(String xpathStr) {
|
||||||
|
this.xpathStr = xpathStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String select(String text) {
|
||||||
|
HtmlCleaner htmlCleaner = new HtmlCleaner();
|
||||||
|
TagNode tagNode = htmlCleaner.clean(text);
|
||||||
|
if (tagNode == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Object[] objects = tagNode.evaluateXPath(xpathStr);
|
||||||
|
if (objects != null && objects.length >= 1) {
|
||||||
|
if (objects[0] instanceof TagNode) {
|
||||||
|
TagNode tagNode1 = (TagNode) objects[0];
|
||||||
|
return htmlCleaner.getInnerHtml(tagNode1);
|
||||||
|
} else {
|
||||||
|
return objects[0].toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (XPatherException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> selectList(String text) {
|
||||||
|
HtmlCleaner htmlCleaner = new HtmlCleaner();
|
||||||
|
TagNode tagNode = htmlCleaner.clean(text);
|
||||||
|
if (tagNode == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<String> results = new ArrayList<String>();
|
||||||
|
try {
|
||||||
|
Object[] objects = tagNode.evaluateXPath(xpathStr);
|
||||||
|
if (objects != null && objects.length >= 1) {
|
||||||
|
for (int i = 0; i < objects.length; i++) {
|
||||||
|
if (objects[i] instanceof TagNode) {
|
||||||
|
TagNode tagNode1 = (TagNode) objects[i];
|
||||||
|
results.add(htmlCleaner.getInnerHtml(tagNode1));
|
||||||
|
} else {
|
||||||
|
results.add(objects[i].toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (XPatherException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
package us.codecraft.spider.utils;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午1:52
|
||||||
|
*/
|
||||||
|
public class UrlUtils {
|
||||||
|
|
||||||
|
private static Pattern relativePathPattern = Pattern.compile("^([\\.]+)/");
|
||||||
|
|
||||||
|
public static String fixRelativeUrl(String url, String refer) {
|
||||||
|
if (StringUtils.isBlank(url) || StringUtils.isBlank(refer)) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
if (url.startsWith("http") || url.startsWith("ftp") || url.startsWith("mailto") || url.startsWith("javascript:")) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
if (StringUtils.startsWith(url, "/")) {
|
||||||
|
String host = getHost(refer);
|
||||||
|
return host + url;
|
||||||
|
} else if (!StringUtils.startsWith(url, ".")) {
|
||||||
|
refer = reversePath(refer, 1);
|
||||||
|
return refer + "/" + url;
|
||||||
|
} else {
|
||||||
|
Matcher matcher = relativePathPattern.matcher(url);
|
||||||
|
if (matcher.find()) {
|
||||||
|
int reverseDepth = matcher.group(1).length();
|
||||||
|
refer = reversePath(refer, reverseDepth);
|
||||||
|
String substring = StringUtils.substring(url, matcher.end());
|
||||||
|
return refer + "/" + substring;
|
||||||
|
} else {
|
||||||
|
refer = reversePath(refer, 1);
|
||||||
|
return refer + "/" + url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String reversePath(String url, int depth) {
|
||||||
|
int i = StringUtils.lastOrdinalIndexOf(url, "/", depth);
|
||||||
|
if (i < 10) {
|
||||||
|
url = getHost(url);
|
||||||
|
} else {
|
||||||
|
url = StringUtils.substring(url, 0, i);
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getHost(String url) {
|
||||||
|
String host = url;
|
||||||
|
int i = StringUtils.ordinalIndexOf(url, "/", 3);
|
||||||
|
if (i > 0) {
|
||||||
|
host = StringUtils.substring(url, 0, i);
|
||||||
|
}
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Pattern patternForProtocal = Pattern.compile("[\\w]+://");
|
||||||
|
|
||||||
|
public static String removeProtocal(String url) {
|
||||||
|
return patternForProtocal.matcher(url).replaceAll("");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getDomain(String url) {
|
||||||
|
String domain = removeProtocal(url);
|
||||||
|
int i = StringUtils.indexOf(domain, "/", 1);
|
||||||
|
if (i > 0) {
|
||||||
|
domain = StringUtils.substring(domain, 0, i);
|
||||||
|
}
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Pattern patternForHref = Pattern.compile("(<a[^<>]*href=)[\"']{0,1}([^\"']*)[\"']{0,1}", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
|
public static String fixAllRelativeHrefs(String html, String url) {
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
Matcher matcher = patternForHref.matcher(html);
|
||||||
|
int lastEnd = 0;
|
||||||
|
while (matcher.find()) {
|
||||||
|
stringBuilder.append(StringUtils.substring(html, lastEnd, matcher.start()));
|
||||||
|
stringBuilder.append(matcher.group(1));
|
||||||
|
stringBuilder.append("\"" + fixRelativeUrl(matcher.group(2), url) + "\"");
|
||||||
|
lastEnd = matcher.end();
|
||||||
|
}
|
||||||
|
stringBuilder.append(StringUtils.substring(html, lastEnd));
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
|
||||||
|
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
|
||||||
|
|
||||||
|
<appender name="stdout" class="org.apache.log4j.ConsoleAppender">
|
||||||
|
<layout class="org.apache.log4j.PatternLayout">
|
||||||
|
<param name="ConversionPattern" value="%d{yy-MM-dd HH:mm:ss,SSS} %-5p %c(%F:%L) ## %m%n" />
|
||||||
|
</layout>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<logger name="org.springframework" additivity="false">
|
||||||
|
<level value="warn" />
|
||||||
|
<appender-ref ref="stdout" />
|
||||||
|
</logger>
|
||||||
|
|
||||||
|
<logger name="net.sf.ehcache" additivity="false">
|
||||||
|
<level value="warn" />
|
||||||
|
<appender-ref ref="stdout" />
|
||||||
|
</logger>
|
||||||
|
|
||||||
|
<root>
|
||||||
|
<level value="info" />
|
||||||
|
<appender-ref ref="stdout" />
|
||||||
|
</root>
|
||||||
|
|
||||||
|
</log4j:configuration>
|
@ -0,0 +1,20 @@
|
|||||||
|
package us.codecraft.spider;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import us.codecraft.spider.selector.Html;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 上午8:42
|
||||||
|
*/
|
||||||
|
public class HtmlTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRegexSelector() {
|
||||||
|
Html selectable = new Html("aaaaaaab");
|
||||||
|
Assert.assertEquals("abbabbab", (selectable.r("(.*)").rp("aa(a)", "$1bb").toString()));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,132 @@
|
|||||||
|
package us.codecraft.spider;
|
||||||
|
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
import us.codecraft.spider.pipeline.ConsolePipeline;
|
||||||
|
import us.codecraft.spider.pipeline.FilePipeline;
|
||||||
|
import us.codecraft.spider.processor.SimplePageProcessor;
|
||||||
|
import us.codecraft.spider.samples.DianpingBlogProcessor;
|
||||||
|
import us.codecraft.spider.samples.HuxiuProcessor;
|
||||||
|
import us.codecraft.spider.schedular.FileCacheQueueSchedular;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-20
|
||||||
|
* Time: 下午7:46
|
||||||
|
*/
|
||||||
|
public class SpiderTest {
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSpider() throws InterruptedException {
|
||||||
|
Spider me = Spider.me().pipeline(new FilePipeline()).processor(new HuxiuProcessor());
|
||||||
|
me.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGlobalSpider(){
|
||||||
|
SimplePageProcessor pageProcessor = new SimplePageProcessor("http://2012guang.diandian.com/", "http://2012guang.diandian.com/post/*");
|
||||||
|
Spider.me().pipeline(new FilePipeline()).schedular(new FileCacheQueueSchedular(pageProcessor.getSite(),"/data/temp/spider/cache/")).
|
||||||
|
processor(pageProcessor).thread().start();
|
||||||
|
SimplePageProcessor pageProcessor2 = new SimplePageProcessor("http://lol.duowan.com/", "http://lol.duowan.com/*.html");
|
||||||
|
Spider.me().pipeline(new FilePipeline()).schedular(new FileCacheQueueSchedular(pageProcessor2.getSite(),"/data/temp/spider/cache/")).
|
||||||
|
processor(pageProcessor2).run();
|
||||||
|
|
||||||
|
Spider.me().processor(new SimplePageProcessor("http://my.oschina.net/", "http://my.oschina.net/*/blog/*")).run();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test(){
|
||||||
|
System.out.println(System.getProperty("java.io.tmpdir"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
@Test
|
||||||
|
public void languageSchema() {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* _hrefs = rs("<a[^<>]*href=[\"']{1}(/yewu/.*?)[\"']{1}")
|
||||||
|
* title = r(""<title>(.*)</title>"")
|
||||||
|
* body = x("//dd[@class='w133']")
|
||||||
|
*
|
||||||
|
* site.domain = "sh.58.com"
|
||||||
|
* site.ua=""
|
||||||
|
* site.cookie="aa:bb"
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* if (page == r('') && refer(1) == 1) {
|
||||||
|
*
|
||||||
|
* type = _refer(1)
|
||||||
|
* content = _text.t().c()
|
||||||
|
* title = x("asd@asd").r("",1)
|
||||||
|
* body[r(_currentUrl).g(1)] = body[r(_currentUrl).g(1)] + (x("").r("",1,2).c())
|
||||||
|
*
|
||||||
|
* body=body[r(_currentUrl).g(1)]
|
||||||
|
* tags[%] = (tags[%] + xs('')) . r('')
|
||||||
|
*
|
||||||
|
* _targetUrls.add('' + x('').r(''))
|
||||||
|
* _sourceUrls.add()
|
||||||
|
* _header.put("","");
|
||||||
|
* _cookie.add("asdsadasdsa");
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* _cookie.add(_cookie[''])
|
||||||
|
*
|
||||||
|
* if (page == r('') && refer(1) == 1)
|
||||||
|
* (
|
||||||
|
* _targetUrl = '' + x('') & r('')
|
||||||
|
* _sourceUrl = ''
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <condition></>
|
||||||
|
* <selector>
|
||||||
|
* <fields>
|
||||||
|
*
|
||||||
|
* <type>
|
||||||
|
* <selector></selector>
|
||||||
|
* <selector></selector>
|
||||||
|
* </type>
|
||||||
|
* </>
|
||||||
|
* </>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* if (model.url('') && model.refer(1) == 1)
|
||||||
|
* (
|
||||||
|
*
|
||||||
|
* model.set(type, model.refer(1))
|
||||||
|
* content = t(_html) > c()
|
||||||
|
* title = x(_html, 'asd@asd') > r('',1)
|
||||||
|
* body[r(_currentUrl).g(1)] = body[r(_currentUrl).g(1)] + (x('') > r('',1,2) > c()) | x('')
|
||||||
|
* tags[%] = tags + xs('') > r('')
|
||||||
|
* model.setTargetUrl();
|
||||||
|
*
|
||||||
|
* _targetUrl = '' + x('') & r('')
|
||||||
|
* _sourceUrl = ''
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* _cookie.add(_cookie[''])
|
||||||
|
*
|
||||||
|
* if (page == r('') && refer(1) == 1)
|
||||||
|
* (
|
||||||
|
* _targetUrl = '' + x('') & r('')
|
||||||
|
* _sourceUrl = ''
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package us.codecraft.spider.samples;
|
||||||
|
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
import us.codecraft.spider.Page;
|
||||||
|
import us.codecraft.spider.processor.PageProcessor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午8:08
|
||||||
|
*/
|
||||||
|
public class DiandianBlogProcessor implements PageProcessor {
|
||||||
|
@Override
|
||||||
|
public void process(Page page) {
|
||||||
|
//http://progressdaily.diandian.com/post/2013-01-24/40046867275
|
||||||
|
List<String> requests = page.getHtml().rs("<a[^<>]*href=[\"']{1}(http://17dujingdian\\.com/post/[^#]*?)[\"']{1}").toStrings();
|
||||||
|
page.addTargetRequests(requests);
|
||||||
|
page.putField("title",page.getHtml().x("//div[@id='content']//h2/a"));
|
||||||
|
page.putField("content",page.getHtml().sc());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Site getSite() {
|
||||||
|
return Site.me().setDomain("www.diandian.com").setStartUrl("http://17dujingdian.com/").
|
||||||
|
setUserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package us.codecraft.spider.samples;
|
||||||
|
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
import us.codecraft.spider.Page;
|
||||||
|
import us.codecraft.spider.processor.PageProcessor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午8:08
|
||||||
|
*/
|
||||||
|
public class DianpingBlogProcessor implements PageProcessor {
|
||||||
|
@Override
|
||||||
|
public void process(Page page) {
|
||||||
|
//http://progressdaily.diandian.com/post/2013-01-24/40046867275
|
||||||
|
List<String> requests = page.getHtml().rs("<a[^<>]*href=[\"']{1}(/shop/.*?)[\"']{1}").toStrings();
|
||||||
|
page.addTargetRequests(requests);
|
||||||
|
requests = page.getHtml().rs("<a[^<>]*href=[\"']{1}(/search/category/.*?)[\"']{1}").toStrings();
|
||||||
|
page.addTargetRequests(requests);
|
||||||
|
if (page.getUrl().toString().contains("shop")){
|
||||||
|
page.putField("title", page.getHtml().x("//h1[@class='shop-title']"));
|
||||||
|
page.putField("content", page.getHtml().sc());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Site getSite() {
|
||||||
|
return Site.me().setDomain("www.dianping.com").setStartUrl("http://www.dianping.com/").
|
||||||
|
setUserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package us.codecraft.spider.samples;
|
||||||
|
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
import us.codecraft.spider.Page;
|
||||||
|
import us.codecraft.spider.processor.PageProcessor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午1:48
|
||||||
|
*/
|
||||||
|
public class F58PageProcesser implements PageProcessor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(Page page) {
|
||||||
|
List<String> strings = page.getHtml().rs("<a[^<>]*href=[\"']{1}(/yewu/.*?)[\"']{1}").toStrings();
|
||||||
|
page.addTargetRequests(strings);
|
||||||
|
page.putField("title",page.getHtml().r("<title>(.*)</title>"));
|
||||||
|
page.putField("body",page.getHtml().x("//dd[@class='w133']"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Site getSite() {
|
||||||
|
return Site.me().setDomain("sh.58.com").setStartUrl("http://sh.58.com/"); //To change body of implemented methods use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package us.codecraft.spider.samples;
|
||||||
|
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
import us.codecraft.spider.Page;
|
||||||
|
import us.codecraft.spider.processor.PageProcessor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午8:08
|
||||||
|
*/
|
||||||
|
public class HuxiuProcessor implements PageProcessor {
|
||||||
|
@Override
|
||||||
|
public void process(Page page) {
|
||||||
|
//http://progressdaily.diandian.com/post/2013-01-24/40046867275
|
||||||
|
List<String> requests = page.getHtml().rs("<a[^<>\"']*href=[\"']{1}([/]{0,1}article[^<>#\"']*?)[\"']{1}").toStrings();
|
||||||
|
page.addTargetRequests(requests);
|
||||||
|
page.putField("title",page.getHtml().x("//div[@class='neirong']//h1[@class='ph xs5']"));
|
||||||
|
page.putField("content",page.getHtml().sc());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Site getSite() {
|
||||||
|
return Site.me().setDomain("www.huxiu.com").setStartUrl("http://www.huxiu.com/").
|
||||||
|
setUserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package us.codecraft.spider.samples;
|
||||||
|
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
import us.codecraft.spider.Page;
|
||||||
|
import us.codecraft.spider.processor.PageProcessor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午8:08
|
||||||
|
*/
|
||||||
|
public class NjuBBSProcessor implements PageProcessor {
|
||||||
|
@Override
|
||||||
|
public void process(Page page) {
|
||||||
|
List<String> requests = page.getHtml().rs("<a[^<>]*href=(bbstcon\\?board=Pictures&file=[^>]*)").toStrings();
|
||||||
|
page.addTargetRequests(requests);
|
||||||
|
page.putField("title",page.getHtml().x("//div[@id='content']//h2/a"));
|
||||||
|
page.putField("content",page.getHtml().sc());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Site getSite() {
|
||||||
|
return Site.me().setDomain("bbs.nju.edu.cn").setStartUrl("http://bbs.nju.edu.cn/board?board=Pictures").
|
||||||
|
setUserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package us.codecraft.spider.samples;
|
||||||
|
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
import us.codecraft.spider.Page;
|
||||||
|
import us.codecraft.spider.processor.PageProcessor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午1:48
|
||||||
|
*/
|
||||||
|
public class OschinaBlogPageProcesser implements PageProcessor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(Page page) {
|
||||||
|
List<String> strings = page.getHtml().as().r("(http://my\\.oschina\\.net)").toStrings();
|
||||||
|
page.addTargetRequests(strings);
|
||||||
|
page.putField("title", page.getHtml().xs("//div[@class='BlogEntity']/div[@class='BlogTitle']/h1"));
|
||||||
|
page.putField("content", page.getHtml().sc());
|
||||||
|
page.putField("author", page.getUrl().r("my\\.oschina\\.net/(\\w+)/blog/\\d+"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Site getSite() {
|
||||||
|
return Site.me().setDomain("my.oschina.net").setStartUrl("http://www.oschina.net/").
|
||||||
|
setUserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package us.codecraft.spider.samples;
|
||||||
|
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
import us.codecraft.spider.Page;
|
||||||
|
import us.codecraft.spider.processor.PageProcessor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午1:48
|
||||||
|
*/
|
||||||
|
public class OschinaPageProcesser implements PageProcessor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(Page page) {
|
||||||
|
List<String> strings = page.getHtml().rs("<a[^<>]*href=[\"']{1}(http://www\\.oschina\\.net/question/[\\w]+)[\"']{1}").toStrings();
|
||||||
|
page.addTargetRequests(strings);
|
||||||
|
page.putField("title", page.getHtml().x("//div[@class='QTitle']/h1/a"));
|
||||||
|
page.putField("content", page.getHtml().xs("//div[@class='Question']//div[@class='Content']/div[@class='detail']"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Site getSite() {
|
||||||
|
return Site.me().setDomain("www.oschina.net").setStartUrl("http://www.oschina.net/").
|
||||||
|
setUserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package us.codecraft.spider.samples;
|
||||||
|
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
import us.codecraft.spider.Page;
|
||||||
|
import us.codecraft.spider.processor.PageProcessor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午1:48
|
||||||
|
*/
|
||||||
|
public class SinaBlogProcesser implements PageProcessor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(Page page) {
|
||||||
|
page.addTargetRequests(page.getHtml().rs("<a[^<>]*href=[\"']{1}(http://blog\\.sina\\.com\\.cn/s/blog_.*?)[\"']{1}").toStrings());
|
||||||
|
page.putField("title", page.getHtml().x("//div[@class='articalTitle']/h2"));
|
||||||
|
page.putField("body",page.getHtml().sc());
|
||||||
|
//x("//dd[@class='w133']")
|
||||||
|
page.putField("date",page.getHtml().x("//div[@id='articlebody']//span[@class='time SG_txtc']").r("\\((.*)\\)"));
|
||||||
|
page.putField("tags",page.getHtml().xs("//td[@class='blog_tag']/h3/a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Site getSite() {
|
||||||
|
return Site.me().setDomain("blog.sina.com.cn").setStartUrl("http://blog.sina.com.cn/").
|
||||||
|
setUserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package us.codecraft.spider.samples;
|
||||||
|
|
||||||
|
import us.codecraft.spider.Site;
|
||||||
|
import us.codecraft.spider.Page;
|
||||||
|
import us.codecraft.spider.processor.PageProcessor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 下午1:48
|
||||||
|
*/
|
||||||
|
public class TianyaPageProcesser implements PageProcessor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(Page page) {
|
||||||
|
List<String> strings = page.getHtml().rs("<a[^<>]*href=[\"']{1}(/post-free.*?\\.shtml)[\"']{1}").toStrings();
|
||||||
|
page.addTargetRequests(strings);
|
||||||
|
page.putField("title", page.getHtml().x("//div[@id='post_head']//span[@class='s_title']//b"));
|
||||||
|
page.putField("body",page.getHtml().sc());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Site getSite() {
|
||||||
|
return Site.me().setDomain("http://bbs.tianya.cn/").setStartUrl("http://bbs.tianya.cn/"); //To change body of implemented methods use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package us.codecraft.spider.selector;
|
||||||
|
|
||||||
|
import org.htmlcleaner.CleanerProperties;
|
||||||
|
import org.htmlcleaner.HtmlCleaner;
|
||||||
|
import org.htmlcleaner.TagNode;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 上午10:35
|
||||||
|
*/
|
||||||
|
public class HtmlCleanerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() throws IOException {
|
||||||
|
HtmlCleaner htmlCleaner = new HtmlCleaner();
|
||||||
|
|
||||||
|
CleanerProperties props = htmlCleaner.getProperties();
|
||||||
|
|
||||||
|
TagNode node = htmlCleaner.clean(new URL("http://www.huanqiu.com"),"UTF-8");
|
||||||
|
System.out.println(node.getAllElementsList(true));
|
||||||
|
System.out.println(node);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package us.codecraft.spider.selector;
|
||||||
|
|
||||||
|
import junit.framework.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: cairne
|
||||||
|
* Date: 13-4-21
|
||||||
|
* Time: 上午7:13
|
||||||
|
*/
|
||||||
|
public class RegexSelectorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidRegex() {
|
||||||
|
String regex = "\\d+(";
|
||||||
|
try {
|
||||||
|
new RegexSelector(regex);
|
||||||
|
Assert.assertNotNull(regex);
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
|
||||||
|
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
|
||||||
|
|
||||||
|
<appender name="stdout" class="org.apache.log4j.ConsoleAppender">
|
||||||
|
<layout class="org.apache.log4j.PatternLayout">
|
||||||
|
<param name="ConversionPattern" value="%d{yy-MM-dd HH:mm:ss,SSS} %-5p %c(%F:%L) ## %m%n" />
|
||||||
|
</layout>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<logger name="org.springframework" additivity="false">
|
||||||
|
<level value="warn" />
|
||||||
|
<appender-ref ref="stdout" />
|
||||||
|
</logger>
|
||||||
|
|
||||||
|
<logger name="org.apache" additivity="false">
|
||||||
|
<level value="warn" />
|
||||||
|
<appender-ref ref="stdout" />
|
||||||
|
</logger>
|
||||||
|
|
||||||
|
<logger name="net.sf.ehcache" additivity="false">
|
||||||
|
<level value="warn" />
|
||||||
|
<appender-ref ref="stdout" />
|
||||||
|
</logger>
|
||||||
|
|
||||||
|
<root>
|
||||||
|
<level value="debug" />
|
||||||
|
<appender-ref ref="stdout" />
|
||||||
|
</root>
|
||||||
|
|
||||||
|
</log4j:configuration>
|
Loading…
Reference in New Issue