- 浏览: 14591 次
- 性别:
- 来自: 武汉
-
最新评论
-
san586:
谢谢十二郎,@Aspect作用于action,致使action ...
对Struts1/2 Action应用Spring AOP问题小结 -
yangpi2008:
如何从索引中读出内容呢,我怎么读不到哦555555555555 ...
基于Spindle的增强HTTP Spider -
bruce_luo:
长期关注。。。。学习中。。。
基于Spindle的增强HTTP Spider -
akululu:
长期关注,并测试。。希望楼主不断更新!
基于Spindle的增强HTTP Spider -
brunoplum:
今天把进一步完善的版本放上来,主要变动是添加了将页面抓取保存到 ...
基于Spindle的增强HTTP Spider
构建于lucene之上的可用的Java开源Spider少之又少,spindle长期没有更新且功能不够完善,故而自己参考其源代码重新编写了一个可扩展的WebCrawler,本着开源共享,共同进步的想法发布于此,期冀得到大家的批评指正,有任何意见及建议均可Email联系我
(kaninebruno@hotmail.com)
以下代码基于lucene-2.3.1,htmlparser-1.6,je-analysis-1.5.3,以及自己修改过的cpdetector-1.0.5;
下载地址分别为
htmlparser:http://sourceforge.net/project/showfiles.php?group_id=24399
je-analysis:http://www.jesoft.cn/je-analysis-1.5.3.jar
lucene就不用说了,cpdetector-1.0.5见附件.
(应部分网友要求,把所用到的工具打包成一个spider.rar,方便下载测试)
spindle的官方站点:http://www.bitmechanic.com/projects/spindle/
主类SiteCapturer代码如下:
工具类ParserUtils代码如下:
程序运行可选择控制台或新建一JSP页面,加入以下代码即可
(另,示例代码中log4j的配置文件须放在项目所在磁盘的根目录下;可在capture()
方法的PropertyConfigurator.configure("/log4j.properties")处自由修改)

以下代码基于lucene-2.3.1,htmlparser-1.6,je-analysis-1.5.3,以及自己修改过的cpdetector-1.0.5;
下载地址分别为
htmlparser:http://sourceforge.net/project/showfiles.php?group_id=24399
je-analysis:http://www.jesoft.cn/je-analysis-1.5.3.jar
lucene就不用说了,cpdetector-1.0.5见附件.
(应部分网友要求,把所用到的工具打包成一个spider.rar,方便下载测试)
spindle的官方站点:http://www.bitmechanic.com/projects/spindle/
主类SiteCapturer代码如下:
package com.huizhi.kanine.util; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.URL; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.HashSet; import jeasy.analysis.MMAnalyzer; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.document.DateTools; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.Term; import org.apache.lucene.search.Hits; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.TermQuery; import org.apache.lucene.store.Directory; import org.apache.lucene.store.LockObtainFailedException; import org.apache.lucene.store.RAMDirectory; import org.htmlparser.Parser; import org.htmlparser.PrototypicalNodeFactory; import org.htmlparser.filters.AndFilter; import org.htmlparser.filters.HasAttributeFilter; import org.htmlparser.filters.NodeClassFilter; import org.htmlparser.tags.BaseHrefTag; import org.htmlparser.tags.FrameTag; import org.htmlparser.tags.LinkTag; import org.htmlparser.tags.MetaTag; import org.htmlparser.util.EncodingChangeException; import org.htmlparser.util.NodeIterator; import org.htmlparser.util.NodeList; import org.htmlparser.util.ParserException; /** * @author 张波 * E-mail:kaninebruno@hotmail.com * Created On : 2008-03-30 * Updated On : 2008-04-06 */ public class SiteCapturer implements Runnable { /* 基准(初始)URL */ protected URL baseURL = null; /* 索引文件或抓取页面的存放位置 */ protected String indexDir = null; /** * 待解析的URL地址集合,所有新检测到的链接均存放于此; * 解析时按照先入先出(First-In First-Out)法则线性取出 */ protected ArrayList URLs = new ArrayList(); /* 已索引的URL地址集合,避免链接的重复抓取 */ protected HashSet indexedURLs = new HashSet(); protected Parser parser = new Parser();; /* 程序运行线程数,默认2个线程 */ protected int threads = 2; /* 存储于磁盘的IndexWriter */ protected IndexWriter FSDWriter; /* 存储于内存的IndexWriter */ protected IndexWriter RAMWriter; protected IndexSearcher indexSearcher; protected RAMDirectory ramDirectory = new RAMDirectory(); /* 筛选页面内容的分词器 */ protected Analyzer luceneAnalyzer = new MMAnalyzer(); /* 解析页面时的字符编码 */ protected String charset; /* 基准端口 */ protected int basePort; /* 基准主机 */ protected String baseHost; /* 是否索引,默认true */ protected boolean justIndex = true; /* 是否保存,默认false */ protected boolean justCopy = false; /* 检测索引中是否存在当前URL信息,避免重复抓取 */ protected boolean isRepeatedCheck = false; /* 索引操作的写入线程锁 */ public static final Object indexLock = new Object(); public static Logger logger = Logger .getLogger(SiteCapturer.class.getName()); public SiteCapturer() { PrototypicalNodeFactory factory = new PrototypicalNodeFactory(); factory.registerTag(new LocalLinkTag()); factory.registerTag(new LocalFrameTag()); factory.registerTag(new LocalBaseHrefTag()); parser.setNodeFactory(factory); } /** * 程序入口,在此初始化mPages、IndexWriter * 通过协调各线程间的活动完成website的抓取工作 * 任务完成后将所有的索引片段合并为一个以优化检索 */ public void capture() { URLs.clear(); URLs.add(getBaseURL()); int responseCode = 0; String contentType = ""; PropertyConfigurator.configure("/log4j.properties"); try { HttpURLConnection uc = (HttpURLConnection) baseURL.openConnection(); responseCode = uc.getResponseCode(); contentType = uc.getContentType(); } catch (MalformedURLException mue) { logger.error("Invalid URL : " + getBaseURL()); } catch (UnknownHostException uhe) { logger.error("UnknowHost : " + getBaseURL()); } catch (SocketException se) { logger.error("Socket Error : " + se.getMessage() + " " + getBaseURL()); } catch (IOException ie) { logger.error("IOException : " + ie); } if (responseCode == HttpURLConnection.HTTP_OK && contentType.startsWith("text/html")) { charset = ParserUtils.autoDetectCharset(baseURL); basePort = baseURL.getPort(); baseHost = baseURL.getHost(); if (charset.equals("windows-1252")) charset = "GBK"; /* 存放索引文件的位置 */ File indexDirectory = new File(indexDir); /* 标记是否重新建立索引,true为重新建立索引 */ boolean flag = true; if (!indexDirectory.exists()) { /* 如果文件夹不存在则创建 */ indexDirectory.mkdir(); } else if (IndexReader.indexExists(indexDirectory)) { /* 如果已存在索引,则追加索引 */ flag = false; File lockfile = new File(indexDirectory + File.separator + "write.lock"); if (lockfile.exists()) lockfile.delete(); } try { if (justIndex) { FSDWriter = new IndexWriter(indexDirectory, luceneAnalyzer, flag); RAMWriter = new IndexWriter(ramDirectory, luceneAnalyzer, true); if (isRepeatedCheck) { IndexReader indexReader = IndexReader.open(indexDir); indexSearcher = new IndexSearcher(indexReader); } } long start = System.currentTimeMillis(); ArrayList threadList = new ArrayList(); for (int i = 0; i < threads; i++) { Thread t = new Thread(this, "K-9 Spider Thread #" + (i + 1)); t.start(); threadList.add(t); } while (threadList.size() > 0) { Thread child = (Thread) threadList.remove(0); try { child.join(); } catch (InterruptedException ie) { logger.error("InterruptedException : " + ie); } } long elapsed = System.currentTimeMillis() - start; if (justIndex) { RAMWriter.close(); FSDWriter.addIndexes(new Directory[] { ramDirectory }); FSDWriter.optimize(); FSDWriter.close(); } logger.info("Finished in " + (elapsed / 1000) + " seconds"); logger.info("The Count of the Links Captured is " + indexedURLs.size()); } catch (CorruptIndexException cie) { logger.error("CorruptIndexException : " + cie); } catch (LockObtainFailedException lofe) { logger.error("LockObtainFailedException : " + lofe); } catch (IOException ie) { logger.error("IOException : " + ie); } } } public void run() { String url; while ((url = dequeueURL()) != null) { if (justIndex) process(url); } threads--; } /** * 判断提取到的链接是否符合解析条件;标准为Port及Host与基准URL相同且类型为text/html或text/plain */ public boolean isToBeCaptured(String url) { boolean flag = false; HttpURLConnection uc = null; int responseCode = 0; String contentType = ""; String host = ""; int port = 0; try { URL source = new URL(url); String protocol = source.getProtocol(); if (protocol != null && protocol.equals("http")) { host = source.getHost(); port = source.getPort(); uc = (HttpURLConnection) source.openConnection(); uc.setConnectTimeout(8000); responseCode = uc.getResponseCode(); contentType = uc.getContentType(); } } catch (MalformedURLException mue) { logger.error("Invalid URL : " + url); } catch (UnknownHostException uhe) { logger.error("UnknowHost : " + url); } catch (SocketException se) { logger.error("Socket Error : " + se.getMessage() + " " + url); } catch (SocketTimeoutException ste) { logger.error("Socket Connection Time Out : " + url); } catch (FileNotFoundException fnfe) { logger.error("broken link " + url + " ignored"); } catch (IOException ie) { logger.error("IOException : " + ie); } if (port == basePort && responseCode == HttpURLConnection.HTTP_OK && host.equals(baseHost) && (contentType.startsWith("text/html") || contentType .startsWith("text/plain"))) flag = true; return flag; } /* 从URL队列mPages里取出单个的URL */ public synchronized String dequeueURL() { while (true) if (URLs.size() > 0) { String url = (String) URLs.remove(0); indexedURLs.add(url); if (isToBeCaptured(url)) { NodeList list; try { int bookmark = URLs.size(); /* 获取页面所有节点 */ parser.setURL(url); try { list = new NodeList(); for (NodeIterator e = parser.elements(); e .hasMoreNodes();) list.add(e.nextNode()); } catch (EncodingChangeException ece) { /* 解码出错的异常处理 */ parser.reset(); list = new NodeList(); for (NodeIterator e = parser.elements(); e .hasMoreNodes();) list.add(e.nextNode()); } /* 抓取静态页面 */ if (-1 == url.indexOf("?") && justCopy) copy(url, list); /** * 依据 http://www.robotstxt.org/wc/meta-user.html 处理 * Robots <META> tag */ NodeList robots = list .extractAllNodesThatMatch( new AndFilter(new NodeClassFilter( MetaTag.class), new HasAttributeFilter("name", "robots")), true); if (0 != robots.size()) { MetaTag robot = (MetaTag) robots.elementAt(0); String content = robot.getAttribute("content") .toLowerCase(); if ((-1 != content.indexOf("none")) || (-1 != content.indexOf("nofollow"))) for (int i = bookmark; i < URLs.size(); i++) URLs.remove(i); } } catch (ParserException pe) { logger.error("ParserException : " + pe); } return url; } } else { threads--; if (threads > 0) { try { wait(); threads++; } catch (InterruptedException ie) { logger.error("InterruptedException : " + ie); } } else { notifyAll(); return null; } } } /** * 处理单独的URL地址,解析页面并加入到lucene索引中;通过自动探测页面编码保证抓取工作的顺利执行 */ protected void process(String url) { String result[]; String content = null; String title = null; /* 此项操作较耗性能,故默认不予检测 */ if (isRepeatedCheck) { try { TermQuery query = new TermQuery(new Term("url", url)); Hits hits = indexSearcher.search(query); if (hits.length() > 0) { logger.info("The URL : " + url + " has already been captured"); } else { result = ParserUtils.parseHtml(url, charset); content = result[0]; title = result[1]; } } catch (IOException ie) { logger.error("IOException : " + ie); } } else { result = ParserUtils.parseHtml(url, charset); content = result[0]; title = result[1]; } if (content != null && content.trim().length() > 0) { Document document = new Document(); document.add(new Field("content", content, Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.WITH_POSITIONS_OFFSETS)); document.add(new Field("url", url, Field.Store.YES, Field.Index.UN_TOKENIZED)); document.add(new Field("title", title, Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.WITH_POSITIONS_OFFSETS)); document.add(new Field("date", DateTools.timeToString(System .currentTimeMillis(), DateTools.Resolution.DAY), Field.Store.YES, Field.Index.UN_TOKENIZED)); synchronized (indexLock) { try { RAMWriter.addDocument(document); /** * 当存放索引的内存使用大于指定值时将其写入硬盘;采用此方法的目的是通过内存缓冲避免频繁的 * IO操作,提高索引创建性能;合并索引时一定要调用被合并一方的IndexWriter的close()方法 */ if (RAMWriter.ramSizeInBytes() > 512 * 1024) { RAMWriter.close(); FSDWriter.addIndexes(new Directory[] { ramDirectory }); RAMWriter = new IndexWriter(ramDirectory, luceneAnalyzer, true); } logger.info("Indexed link : " + url); } catch (CorruptIndexException cie) { logger.error("CorruptIndexException : " + cie); } catch (IOException ie) { logger.error("IOException : " + ie); } } } } /* 将URL链接转换为本地目录的形式 */ protected String makeLocalLink(String link, String current) { String localLink; if (link.equals(getBaseURL())) localLink = "index.html"; else if (link.startsWith(getBaseURL()) && (link.length() > getBaseURL().length())) { localLink = link.substring(getBaseURL().length() + 1); if (-1 == localLink.indexOf(".")) localLink += "/" + "index.html"; } else localLink = link; if ((null != current) && link.startsWith(getBaseURL()) && (current.length() > getBaseURL().length())) { current = current.substring(getBaseURL().length() + 1); int i = 0, j; while (-1 != (j = current.indexOf('/', i))) { localLink = "../" + localLink; i = j + 1; } } return localLink; } /* 将页面按结构层次保存到本地硬盘 */ protected void copy(String url, NodeList list) { File file = new File(indexDir, makeLocalLink(url, "")); File dir = file.getParentFile(); if (!dir.exists()) dir.mkdirs(); else if (!dir.isDirectory()) { dir = new File(dir.getParentFile(), dir.getName() + ".content"); if (!dir.exists()) dir.mkdirs(); file = new File(dir, file.getName()); } try { PrintWriter out = new PrintWriter(new OutputStreamWriter( new FileOutputStream(file), charset)); for (int i = 0; i < list.size(); i++) out.print(list.elementAt(i).toHtml()); out.close(); logger.info("Captured link : " + url); } catch (FileNotFoundException fnfe) { logger.error("FileNotFoundException : " + fnfe); } catch (UnsupportedEncodingException uee) { logger.error("UnsupportedEncodingException : " + uee); } } /** * Link tag that rewrites the HREF. * The HREF is changed to a local target if it matches the source. */ class LocalLinkTag extends LinkTag { public void doSemanticAction() { String link = getLink(); if (link.endsWith("/")) link = link.substring(0, link.length() - 1); int pos = link.indexOf("#"); if (pos != -1) link = link.substring(0, pos); /* 将链接加入到处理队列中 */ if (!(indexedURLs.contains(link) || URLs.contains(link))) URLs.add(link); setLink(link); } } /** * Frame tag that rewrites the SRC URLs. The SRC URLs are mapped to local * targets if they match the source. */ class LocalFrameTag extends FrameTag { public void doSemanticAction() { String link = getFrameLocation(); if (link.endsWith("/")) link = link.substring(0, link.length() - 1); int pos = link.indexOf("#"); if (pos != -1) link = link.substring(0, pos); /* 将链接加入到处理队列中 */ if (!(indexedURLs.contains(link) || URLs.contains(link))) URLs.add(link); setFrameLocation(link); } } /** * Base tag that doesn't show. The toHtml() method is overridden to return * an empty string, effectively shutting off the base reference. */ class LocalBaseHrefTag extends BaseHrefTag { public String toHtml() { return (""); } } public static void main(String[] args) { SiteCapturer worker = new SiteCapturer(); if (args.length < 6) { System.out .println("Usage: -u <start url> -d <index dir> -t <threads> [-r] [-c] [-i]"); return; } for (int i = 0; i < args.length; i++) { if (args[i].equals("-u")) worker.setBaseURL(args[++i]); else if (args[i].equals("-d")) worker.setIndexDir(args[++i]); else if (args[i].equals("-t")) worker.setThreads(Integer.parseInt(args[++i])); else if (args[i].equals("-r")) worker.setIsRepeatedCheck(true); else if (args[i].equals("-c")) worker.setJustCopy(true); else if (args[i].equals("-i")) worker.setJustIndex(false); } if (worker.getThreads() < 1) throw new IllegalArgumentException("Invalid number of threads: " + worker.getThreads()); worker.capture(); System.exit(0); } public String getBaseURL() { return baseURL.toString(); } public void setBaseURL(String source) { if (source.endsWith("/")) source = source.substring(0, source.length() - 1); try { baseURL = new URL(source); } catch (MalformedURLException e) { logger.error("Invalid URL : " + getBaseURL()); } } public void setIndexDir(String indexDirectory) { indexDir = indexDirectory; } public int getThreads() { return threads; } public void setThreads(int threadCount) { threads = threadCount; } public void setIsRepeatedCheck(boolean check) { isRepeatedCheck = check; } public void setJustIndex(boolean justIndex) { this.justIndex = justIndex; } public void setJustCopy(boolean justCopy) { this.justCopy = justCopy; } }
工具类ParserUtils代码如下:
package com.huizhi.kanine.util; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.URL; import java.net.UnknownHostException; import java.nio.charset.Charset; import org.htmlparser.Parser; import org.htmlparser.util.NodeList; import org.htmlparser.util.ParserException; import org.htmlparser.visitors.HtmlPage; import cpdetector.io.ASCIIDetector; import cpdetector.io.CodepageDetectorProxy; import cpdetector.io.JChardetFacade; import cpdetector.io.ParsingDetector; import cpdetector.io.UnicodeDetector; public class ParserUtils { /* StringBuffer的缓冲区大小 */ public static int TRANSFER_SIZE = 4096; /* 当前平台的行分隔符 */ public static String lineSep = System.getProperty("line.separator"); /* 自动探测页面编码,避免中文乱码的出现 */ public static String autoDetectCharset(URL url) { CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance(); /** * ParsingDetector可用于检查HTML、XML等文件或字符流的编码 * 构造方法中的参数用于指示是否显示探测过程的详细信息 * 为false则不显示 */ detector.add(new ParsingDetector(false)); detector.add(JChardetFacade.getInstance()); detector.add(ASCIIDetector.getInstance()); detector.add(UnicodeDetector.getInstance()); Charset charset = null; try { charset = detector.detectCodepage(url); } catch (MalformedURLException mue) { mue.printStackTrace(); } catch (IOException ie) { ie.printStackTrace(); } if (charset == null) charset = Charset.defaultCharset(); return charset.name(); } /* 按照指定编码解析标准的html页面,为建立索引做准备*/ public static String[] parseHtml(String url, String charset) { String result[] = null; String content = null; try { URL source = new URL(url); InputStream in = source.openStream(); BufferedReader reader = new BufferedReader(new InputStreamReader( in, charset)); String line = new String(); StringBuffer temp = new StringBuffer(TRANSFER_SIZE); while ((line = reader.readLine()) != null) { temp.append(line); temp.append(lineSep); } reader.close(); in.close(); content = temp.toString(); } catch (UnsupportedEncodingException uee) { uee.printStackTrace(); } catch (MalformedURLException mue) { System.err.println("Invalid URL : " + url); } catch (UnknownHostException uhe) { System.err.println("UnknowHost : " + url); } catch (SocketException se) { System.err.println("Socket Error : " + se.getMessage() + " " + url); } catch (SocketTimeoutException ste) { System.err.println("Socket Connection Time Out : " + url); } catch (FileNotFoundException fnfe) { System.err.println("broken link " + ((FileNotFoundException) fnfe.getCause()).getMessage() + " ignored"); } catch (IOException ie) { ie.printStackTrace(); } if (content != null) { Parser myParser = Parser.createParser(content, charset); HtmlPage visitor = new HtmlPage(myParser); try { myParser.visitAllNodesWith(visitor); String body = null; String title = "Untitled"; if (visitor.getBody() != null) { NodeList nodelist = visitor.getBody(); body = nodelist.asString().trim(); } if (visitor.getTitle() != null) title = visitor.getTitle(); result = new String[] { body, title }; } catch (ParserException pe) { pe.printStackTrace(); } } return result; } }
程序运行可选择控制台或新建一JSP页面,加入以下代码即可
(另,示例代码中log4j的配置文件须放在项目所在磁盘的根目录下;可在capture()
方法的PropertyConfigurator.configure("/log4j.properties")处自由修改)
<%@ page contentType="text/html; charset=UTF-8"%> <%@ page import="com.huizhi.kanine.util.*"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Lucene</title> </head> <body> <% SiteCapturer worker= new SiteCapturer(); worker.setBaseURL("http://www.blabla.cn"); worker.setIndexDir("c:\\luceneIndex"); //worker.setIsRepeatedCheck(true);//可选,检测链接是否和索引重复 //worker.setJustCopy(true);//可选,将链接保存到本地 worker.setThreads(20); worker.capture(); %> </body> </html>
- spider.rar (3.9 MB)
- 下载次数: 1268
评论
11 楼
yangpi2008
2009-01-13
如何从索引中读出内容呢,我怎么读不到哦5555555555555
是不是方法不对哦
import java.io.IOException;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
public class TestQuery {
public static void main(String[] args) throws IOException, ParseException {
Hits hits = null;
String queryString = "茶馆";
Query query = null;
IndexSearcher searcher = new IndexSearcher("C:\\luceneIndex");
Analyzer analyzer = new StandardAnalyzer();
try {
QueryParser qp = new QueryParser("content", analyzer);
query = qp.parse(queryString);
} catch (ParseException e) {
}
if (searcher != null) {
hits = searcher.search(query);
if (hits.length() > 0) {
System.out.println("找到:" + hits.length() + " 个结果!");
}
}
}
}
是不是方法不对哦
import java.io.IOException;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
public class TestQuery {
public static void main(String[] args) throws IOException, ParseException {
Hits hits = null;
String queryString = "茶馆";
Query query = null;
IndexSearcher searcher = new IndexSearcher("C:\\luceneIndex");
Analyzer analyzer = new StandardAnalyzer();
try {
QueryParser qp = new QueryParser("content", analyzer);
query = qp.parse(queryString);
} catch (ParseException e) {
}
if (searcher != null) {
hits = searcher.search(query);
if (hits.length() > 0) {
System.out.println("找到:" + hits.length() + " 个结果!");
}
}
}
}
10 楼
bruce_luo
2008-06-19
长期关注。。。。学习中。。。
9 楼
akululu
2008-04-07
长期关注,并测试。。希望楼主不断更新!
8 楼
brunoplum
2008-04-06
今天把进一步完善的版本放上来,主要变动是添加了将页面抓取保存到本地的功能,另外加入了日志记录,修正了解析url时一处不妥的地方;欢迎测试!
7 楼
chencang
2008-04-02
lib.rar我下不下来


6 楼
chencang
2008-04-02
楼主辛苦了,期待很久了。
我也在学习这方面的内容,我要仔细拜读下
我也在学习这方面的内容,我要仔细拜读下

5 楼
sovolee
2008-04-02
(1)不能抓获https协议的网站;
(2)缺少异常日志;
(3)SiteCapturer类过长,不方便作业,不符合“建筑美”。
(2)缺少异常日志;
(3)SiteCapturer类过长,不方便作业,不符合“建筑美”。
4 楼
sovolee
2008-04-01
很好,期待很久了。
3 楼
D04540214
2008-03-31
我来做庄啦
2 楼
brunoplum
2008-03-31
关于性能,在用spindle的时候即使是只有几十个页面的简单站点都会在抓取完成后进入线程的死锁,我的这个改版不会出现这样的情况,而且加入了很多处理异常的操作,尽可能地保证了页面解析与建立索引的速度及准确性
1 楼
betafox
2008-03-31
楼主辛苦了啊,能否把它的优点和性能测试一起放上来?
相关推荐
刘嘉怡.中期检查.doc
内容概要:本文详细介绍了如何使用COMSOL Multiphysics进行热电效应仿真的全过程。首先解释了热电效应的基本概念及其应用场景,如手机充电发烫、吹风机温度升高等。接着,通过具体实例展示了如何在COMSOL中建立热电模型,包括选择合适的物理场(焦耳热和热电效应)、设定材料属性(电导率、导热系数、塞贝克系数)、绘制几何形状以及设置边界条件。文中还提供了详细的MATLAB代码片段用于自动化建模流程,涵盖求解器配置、网格划分、后处理等方面的技术细节。此外,作者分享了一些常见问题的解决方案,如求解器不收敛、网格畸变等。 适合人群:对热电效应感兴趣的科研人员、工程技术人员及高校学生,尤其适用于有一定COMSOL和MATLAB基础的学习者。 使用场景及目标:帮助读者掌握热电效应的基本原理和COMSOL仿真技能,能够独立完成从模型构建到结果分析的完整流程。目标是提高热电转换系统的效率,优化设计参数,探索新材料的应用潜力。 其他说明:文章不仅提供了理论指导,还包括大量实战经验和技术技巧,有助于解决实际建模过程中遇到的问题。
内容概要:本文深入探讨了汽车内外饰模具设计的关键要素,涵盖分型面设计、斜顶和滑块的应用、模架选择以及顶出系统的配置。针对每个部分,不仅提供了理论指导,还辅以Python、MATLAB等编程语言的实际代码示例,帮助理解和实施具体设计方案。例如,分型面设计强调了如何根据产品结构和外观要求确定最佳分型面位置;斜顶和滑块部分讨论了不同类型及其应用场景;模架和顶出系统则关注于结构稳定性和顶出效果的优化。 适合人群:从事汽车模具设计的专业人士,尤其是希望深入了解内外饰模具设计细节的新手设计师和技术人员。 使用场景及目标:适用于汽车内外饰模具设计项目,旨在提高模具设计的精度和效率,减少试错成本,确保产品质量。通过学习本文提供的技术和实践经验,能够更好地应对实际工作中遇到的各种挑战。 其他说明:文中提到的代码示例和经验公式均来源于实际工程案例,具有较高的参考价值。同时,作者还分享了许多宝贵的行业经验和技巧,有助于读者快速掌握模具设计的核心技能。
python3.10以上 可安装pyside6(类似pyqt),具体安装操作步骤
内容概要:DeepSeek AI是由杭州深度求索人工智能基础技术研究有限公司于2025年1月20日发布的深度探索AI技术。它具有多模态能力、多语言支持、长上下文理解、领域垂直优化、开源特性等多项技术突破,支
IIS配置phpweb服务器所需VC_redist.x64.rar
云南移动5G-A网业战略发展探讨 -创新领航,千帆竞发,共同迈入5G-A新时代.pptx
本文描述了如何使用C#基于OpenCvSharpe实现模版匹配功能,其中实现了下功能: 1、图像加载; 2、模版加载、绘制、保存功能; 3、模版匹配功能。
内容概要:本文档汇集了CSci 235软件设计与分析II课程中关于数据结构的面试题,由Stewart Weiss教授整理。文档涵盖了广泛的数据结构主题,包括但不限于链表(如单链表、双向链表、循环链表)、二叉树(如二叉搜索树、最小高度二叉搜索树)、栈、队列等。每个问题都旨在考察求职者对不同数据结构的理解及其应用场景。例如,选择合适的数据结构实现手机通讯录功能,或设计支持撤销功能的文本编辑器。此外,文档还探讨了复杂度分析(Big-O表示法),以及如何优化特定操作的时间复杂度。最后,文档提供了额外的学习资源链接,帮助求职者进一步准备面试。 适合人群:计算机科学专业的学生或有志于从事软件开发工作的求职者,特别是那些希望在技术面试中表现优异的人士。 使用场景及目标:①理解并掌握常见数据结构的基本概念和特性;②学会根据不同场景选择最合适的数据结构;③掌握常见数据结构操作的时间复杂度分析;④为技术面试做充分准备,提高面试成功率。 其他说明:文档中的问题不仅限于理论知识,还包括实际编码练习,建议读者在学习过程中动手实践,以加深理解和记忆。同时,文档提供的额外资源链接可以作为扩展阅读材料,帮助读者更全面地掌握相关知识。
Matlab领域上传的视频是由对应的完整代码运行得来的,完整代码皆可运行,亲测可用,适合小白; 1、从视频里可见完整代码的内容 主函数:main.m; 调用函数:其他m文件;无需运行 运行结果效果图; 2、代码运行版本 Matlab 2019b;若运行有误,根据提示修改;若不会,私信博主; 3、运行操作步骤 步骤一:将所有文件放到Matlab的当前文件夹中; 步骤二:双击打开main.m文件; 步骤三:点击运行,等程序运行完得到结果; 4、仿真咨询 如需其他服务,可私信博主; 4.1 博客或资源的完整代码提供 4.2 期刊或参考文献复现 4.3 Matlab程序定制 4.4 科研合作
帆软本地打印插件FinePrint 8.0版本,适用于FineReport8
内容概要:本文介绍了密歇根大学EECS 461课程——嵌入式控制系统的核心内容及其发展背景。课程旨在教授学生嵌入式控制系统的理论与实践,包括传感器和执行器接口、实时性能和安全要求、混合行为系统、分布式控制网络等方面的知识。文中特别强调了现代汽车作为嵌入式控制系统的典型应用,从1977年到2019年间,汽车技术经历了从模拟控制到微处理器控制的巨大变革,如今的汽车具备了更高效、更环保、更安全的特点。课程还涵盖了S32K144微控制器的开发环境、实验室练习(如数字I/O、PWM信号生成、虚拟墙模拟等)以及自动代码生成工具的使用。 适合人群:具备一定编程基础,特别是对嵌入式系统感兴趣的本科生和研究生,尤其是电气工程、计算机科学专业的高年级学生或硕士生。 使用场景及目标:①了解嵌入式控制系统的基本概念和发展历程;②掌握嵌入式控制系统的设计方法和技术手段,如实时操作系统、中断处理、网络通信协议(CAN)等;③通过实际项目操作,熟悉嵌入式硬件平台和开发工具链的应用。 其他说明:随着汽车行业向智能化、自动化方向发展,对于能够开发复杂嵌入式软件的人才需求日益增长。EECS 461不仅为学生提供了扎实的技术训练,也为他们未来的职业发展打下了坚实的基础。此外,课程还反映了跨学科教育的重要性,鼓励学生打破传统学术界限,培养解决实际问题的能力。
内容概要:本文详细介绍了如何利用C#与Halcon联合编程构建高效的视觉几何定位与测量框架。主要内容涵盖模板创建与匹配、圆测量、数据持久化以及图像采集等方面的技术细节。首先,通过创建形状模板并进行匹配,实现了工件的精确定位。接着,针对圆形物体的测量,提出了动态ROI绘制、亚像素边缘提取和稳健圆拟合的方法。此外,还讨论了模板管理和图像采集的最佳实践,确保系统的稳定性和高效性。最后,强调了Halcon对象的内存管理和错误处理机制,提供了实用的优化建议。 适合人群:具备一定编程基础,尤其是对C#和Halcon有一定了解的研发人员和技术爱好者。 使用场景及目标:适用于工业生产线上的自动化检测设备开发,旨在提高工件定位和尺寸测量的精度与效率。主要目标是帮助开发者掌握C#与Halcon联合编程的具体实现方法,从而构建稳定可靠的视觉检测系统。 其他说明:文中提供了大量实战代码片段和调试技巧,有助于读者快速理解和应用相关技术。同时,作者分享了许多实际项目中的经验和教训,使读者能够避开常见陷阱,提升开发效率。
内容概要:本文深入探讨了DeepSeek AI的独特优势及其在全球AI领域的影响力。DeepSeek由中国深度求索公司开发,自2025年1月20日发布以来,凭借其卓越的性能和独特优势迅速吸引了全球关注。其核心优势包括:1) 极致成本效率,如低成本训练和高效推理;2) 强大的推理能力,涵盖多领域表现优异
php连接sqlserver之VC_redist.x64.exe
内容概要:本文详细介绍了利用Matlab/Simulink进行异步电动机交流调速系统的仿真实验,主要探讨了两种控制方式:恒压频比(V/F)开环控制和转差频率闭环控制。文中不仅提供了具体的数学模型和代码片段,还展示了不同控制方式下的仿真结果对比,包括转速响应、电流波形和谐波含量等方面的表现。此外,文章深入讲解了SVPWM(空间矢量脉宽调制)的应用,强调了其相对于传统SPWM的优势,并给出了详细的参数调整技巧和注意事项。 适合人群:从事电机控制系统设计的研究人员和技术人员,尤其是对Matlab/Simulink有一定基础并希望深入了解异步电动机调速系统的人群。 使用场景及目标:适用于需要进行电机控制算法开发和优化的场合,旨在帮助读者掌握异步电动机调速的基本原理和具体实现方法,提高仿真的准确性和效率。 其他说明:文章通过丰富的实例和图表,生动地展示了各种控制策略的特点和效果,有助于读者更好地理解和应用相关理论。同时,文中提供的调试技巧对于解决实际工程中的常见问题非常有帮助。
内容概要:本文详细介绍了如何利用Matlab进行电动汽车等速工况续驶里程的仿真。首先解释了等速工况的概念及其重要性,接着展示了具体的参数设定,如车辆质量、风阻系数、电池容量等。然后深入探讨了核心算法,包括阻力计算、功率需求、能量消耗以及SOC(剩余电量)的变化过程。文中特别强调了一些常见的陷阱和注意事项,如单位换算错误、电机效率的动态变化等。最后,通过可视化工具展示了仿真结果,并讨论了可能的改进方向,如引入NEDC工况循环和其他动态因素。 适合人群:新能源汽车专业的学生、研究人员以及对电动汽车仿真感兴趣的工程师。 使用场景及目标:①帮助理解和掌握电动汽车等速工况续驶里程仿真的原理和方法;②提供详细的代码实现和注释,便于学习和修改;③用于课程设计、毕业设计或其他研究项目。 其他说明:本文不仅提供了完整的Matlab代码,还包括详细的参数说明和常见问题解析,确保使用者能够顺利运行并理解整个仿真过程。同时,作者还分享了许多实践经验,有助于提高仿真的准确性和实用性。
【定稿】桂林电子科技大学第七届大学生思政课社会实践优秀成果展示活动实施方案 (1).zip
内容概要:本文详细介绍了使用Maxwell 16.0和ANSYS 2020进行直线感应电机瞬态磁场仿真的方法和技术要点。首先强调了建模前的准备工作,包括初级线圈布置、次级导体材料选择、气隙宽度等参数的确定。然后针对Maxwell 16.0用户,讲解了坐标系的选择(笛卡尔坐标系)、初级绕组绘制、运动参数设置、网格剖分优化以及边界条件的正确配置。对于ANSYS 2020用户,则着重讲述了如何利用Maxwell模块建立模型并在Mechanical中进行电磁力耦合分析,包括参数化扫描设置、气隙厚度扫描、磁密云图动态更新等技巧。此外,文中还分享了许多实用的经验和注意事项,如避免常见的参数设置错误、提高仿真精度的方法、处理推力波动等问题的具体措施。 适合人群:从事电机设计与仿真的工程师、研究人员,尤其是有一定Maxwell和ANSYS使用基础的技术人员。 使用场景及目标:帮助用户掌握直线感应电机瞬态磁场仿真的全流程,确保仿真结果的准确性,提升工作效率。具体应用场景包括但不限于新电机设计验证、现有电机性能优化、故障诊断等。 其他说明:文中提供了大量具体的命令和脚本示例,便于读者直接应用到实际工作中。同时,作者结合自身丰富的实践经验,给出了许多宝贵的建议和警示,有助于读者避开常见陷阱,顺利完成仿真任务。
内容概要:本文详细介绍了如何在Matlab Simulink中构建交流异步电机的矢量控制模型及其SVPWM调制方法。首先解释了坐标变换(如Clarke和Park变换)的基本原理,并提供了具体的实现代码。接着讨论了双闭环控制策略,即电流环和速度环的设计与参数整定,强调了PI控制器的抗饱和处理以及速度环带宽的选择。对于SVPWM部分,文章对比了几种不同的调制算法,推荐了一种改进的七段式算法,提高了电压利用率并降低了谐波含量。此外,文中还分享了许多实际调试过程中遇到的问题及解决方案,如启动电流冲击、低频振荡等。 适合人群:从事电力电子、电机驱动系统设计的研究人员和技术工程师,尤其是对矢量控制和SVPWM感兴趣的初学者。 使用场景及目标:适用于需要深入了解交流异步电机矢量控制原理及其实现方法的人群。目标是在掌握理论基础上,能够独立搭建并优化Simulink仿真模型,从而提高实际应用中的性能表现。 其他说明:随文提供的工程文件包含了完整的模型和详细的参数整定表格,便于读者进行实践操作。同时,作者还提供了一些实用的小贴士,帮助避免常见的错误和陷阱。