spider简单的爬虫程序

spider简单的爬虫程序

1、基础准备
htmlparser
首页:http://sourceforge.net/projects/htmlparser/
下载:http://sourceforge.net/project/showfiles.php?group_id=24399
文件:htmlparser1_6_20060610.zip
<dependency>
<groupId>org.htmlparser</groupId>
<artifactId>htmlparser</artifactId>
<version>1.6</version>
</dependency>

cpdetector
首页:http://cpdetector.sourceforge.net/
下载:http://sourceforge.net/project/showfiles.php?group_id=114421
文件:cpdetector_eclipse_project_1.0.7.zip

<dependency>
<groupId>cpdetector</groupId>
<artifactId>cpdetector</artifactId>
<version>1.0.5</version>
</dependency>

spindle
首页:http://www.bitmechanic.com/projects/spindle/ (但是已经无法访问)

2 修改spindle代码得到的spider
简单的将URL打印出来了,解析的内容等等都没有处理

解析HTML的基类HtmlParserUtil.java

  1. package com.sillycat.api.commons.utils.html;
  2. import java.io.BufferedReader;
  3. import java.io.FileNotFoundException;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.InputStreamReader;
  7. import java.io.UnsupportedEncodingException;
  8. import java.net.MalformedURLException;
  9. import java.net.SocketException;
  10. import java.net.SocketTimeoutException;
  11. import java.net.URL;
  12. import java.net.UnknownHostException;
  13. import java.nio.charset.Charset;
  14. import org.htmlparser.Parser;
  15. import org.htmlparser.util.NodeList;
  16. import org.htmlparser.util.ParserException;
  17. import org.htmlparser.visitors.HtmlPage;
  18. import cpdetector.io.ASCIIDetector;
  19. import cpdetector.io.CodepageDetectorProxy;
  20. import cpdetector.io.JChardetFacade;
  21. import cpdetector.io.ParsingDetector;
  22. import cpdetector.io.UnicodeDetector;
  23. public class HtmlParserUtil {
  24. /* StringBuffer的缓冲区大小 */
  25. public static int TRANSFER_SIZE = 4096;
  26. /* 当前平台的行分隔符 */
  27. public static String lineSep = System.getProperty("line.separator");
  28. /* 自动探测页面编码,避免中文乱码的出现 */
  29. public static String autoDetectCharset(URL url) {
  30.    CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
  31.    /**
  32.    * ParsingDetector可用于检查HTML、XML等文件或字符流的编码 构造方法中的参数用于指示是否显示探测过程的详细信息
  33.    * 为false则不显示
  34.    */
  35.    detector.add(new ParsingDetector(false));
  36.    detector.add(JChardetFacade.getInstance());
  37.    detector.add(ASCIIDetector.getInstance());
  38.    detector.add(UnicodeDetector.getInstance());
  39.    Charset charset = null;
  40.    try {
  41.     charset = detector.detectCodepage(url);
  42.    } catch (MalformedURLException mue) {
  43.     mue.printStackTrace();
  44.    } catch (IOException ie) {
  45.     ie.printStackTrace();
  46.    }
  47.    if (charset == null)
  48.     charset = Charset.defaultCharset();
  49.    return charset.name();
  50. }
  51. /* 按照指定编码解析标准的html页面,为建立索引做准备 */
  52. public static String[] parseHtml(String url, String charset) {
  53.    String result[] = null;
  54.    String content = null;
  55.    try {
  56.     URL source = new URL(url);
  57.     InputStream in = source.openStream();
  58.     BufferedReader reader = new BufferedReader(new InputStreamReader(
  59.       in, charset));
  60.     String line = new String();
  61.     StringBuffer temp = new StringBuffer(TRANSFER_SIZE);
  62.     while ((line = reader.readLine()) != null) {
  63.      temp.append(line);
  64.      temp.append(lineSep);
  65.     }
  66.     reader.close();
  67.     in.close();
  68.     content = temp.toString();
  69.    } catch (UnsupportedEncodingException uee) {
  70.     uee.printStackTrace();
  71.    } catch (MalformedURLException mue) {
  72.     System.err.println("Invalid URL : " + url);
  73.    } catch (UnknownHostException uhe) {
  74.     System.err.println("UnknowHost : " + url);
  75.    } catch (SocketException se) {
  76.     System.err.println("Socket Error : " + se.getMessage() + " " + url);
  77.    } catch (SocketTimeoutException ste) {
  78.     System.err.println("Socket Connection Time Out : " + url);
  79.    } catch (FileNotFoundException fnfe) {
  80.     System.err.println("broken link "
  81.       + ((FileNotFoundException) fnfe.getCause()).getMessage()
  82.       + " ignored");
  83.    } catch (IOException ie) {
  84.     ie.printStackTrace();
  85.    }
  86.    if (content != null) {
  87.     Parser myParser = Parser.createParser(content, charset);
  88.     HtmlPage visitor = new HtmlPage(myParser);
  89.     try {
  90.      myParser.visitAllNodesWith(visitor);
  91.      String body = null;
  92.      String title = "Untitled";
  93.      if (visitor.getBody() != null) {
  94.       NodeList nodelist = visitor.getBody();
  95.       body = nodelist.asString().trim();
  96.      }
  97.      if (visitor.getTitle() != null){
  98.       title = visitor.getTitle();
  99.      }
  100.      result = new String[] { body, title };
  101.     } catch (ParserException pe) {
  102.      pe.printStackTrace();
  103.     }
  104.    }
  105.    return result;
  106. }
  107. }
  108. 多线程爬虫类    HtmlCaptureRunner.java
  109. package com.sillycat.api.thread.runner;
  110. import java.io.FileNotFoundException;
  111. import java.io.IOException;
  112. import java.net.HttpURLConnection;
  113. import java.net.MalformedURLException;
  114. import java.net.SocketException;
  115. import java.net.SocketTimeoutException;
  116. import java.net.URL;
  117. import java.net.UnknownHostException;
  118. import java.util.ArrayList;
  119. import java.util.HashSet;
  120. import org.apache.commons.logging.Log;
  121. import org.apache.commons.logging.LogFactory;
  122. import org.htmlparser.Parser;
  123. import org.htmlparser.PrototypicalNodeFactory;
  124. import org.htmlparser.filters.AndFilter;
  125. import org.htmlparser.filters.HasAttributeFilter;
  126. import org.htmlparser.filters.NodeClassFilter;
  127. import org.htmlparser.tags.BaseHrefTag;
  128. import org.htmlparser.tags.FrameTag;
  129. import org.htmlparser.tags.LinkTag;
  130. import org.htmlparser.tags.MetaTag;
  131. import org.htmlparser.util.EncodingChangeException;
  132. import org.htmlparser.util.NodeIterator;
  133. import org.htmlparser.util.NodeList;
  134. import org.htmlparser.util.ParserException;
  135. import com.sillycat.api.commons.utils.StringUtil;
  136. import com.sillycat.api.commons.utils.html.HtmlParserUtil;
  137. public class HtmlCaptureRunner implements Runnable {
  138. public Log logger = LogFactory.getLog(getClass());
  139. /* 基准(初始)URL */
  140. protected String baseURL = null;
  141. private String contentPath = null;
  142. /**
  143. * 待解析的URL地址集合,所有新检测到的链接均存放于此; 解析时按照先入先出(First-In First-Out)法则线性取出
  144. */
  145. protected ArrayList URLs = new ArrayList();
  146. /* 已存储的URL地址集合,避免链接的重复抓取 */
  147. protected HashSet indexedURLs = new HashSet();
  148. protected Parser parser = new Parser();;
  149. /* 程序运行线程数,默认2个线程 */
  150. protected int threads = 2;
  151. /* 解析页面时的字符编码 */
  152. protected String charset;
  153. /* 基准端口 */
  154. protected int basePort;
  155. /* 基准主机 */
  156. protected String baseHost;
  157. /* 是否存储,默认true */
  158. protected boolean justDatabase = true;
  159. /* 检测索引中是否存在当前URL信息,避免重复抓取 */
  160. protected boolean isRepeatedCheck = false;
  161. public HtmlCaptureRunner() {
  162.    PrototypicalNodeFactory factory = new PrototypicalNodeFactory();
  163.    factory.registerTag(new LocalLinkTag());
  164.    factory.registerTag(new LocalFrameTag());
  165.    factory.registerTag(new LocalBaseHrefTag());
  166.    parser.setNodeFactory(factory);
  167. }
  168. public void capture() {
  169.    URLs.clear();
  170.    URLs.add(getBaseURL());
  171.    int responseCode = 0;
  172.    String contentType = "";
  173.    try {
  174.     HttpURLConnection uc = (HttpURLConnection) new URL(baseURL)
  175.       .openConnection();
  176.     responseCode = uc.getResponseCode();
  177.     contentType = uc.getContentType();
  178.    } catch (MalformedURLException mue) {
  179.     logger.error("Invalid URL : " + getBaseURL());
  180.    } catch (UnknownHostException uhe) {
  181.     logger.error("UnknowHost : " + getBaseURL());
  182.    } catch (SocketException se) {
  183.     logger.error("Socket Error : " + se.getMessage() + " "
  184.       + getBaseURL());
  185.    } catch (IOException ie) {
  186.     logger.error("IOException : " + ie);
  187.    }
  188.    if (responseCode == HttpURLConnection.HTTP_OK
  189.      && contentType.startsWith("text/html")) {
  190.     try {
  191.      charset = HtmlParserUtil.autoDetectCharset(new URL(baseURL));
  192.      basePort = new URL(baseURL).getPort();
  193.      baseHost = new URL(baseURL).getHost();
  194.      if (charset.equals("windows-1252"))
  195.       charset = "GBK";
  196.      long start = System.currentTimeMillis();
  197.      ArrayList threadList = new ArrayList();
  198.      for (int i = 0; i < threads; i++) {
  199.       Thread t = new Thread(this"Spider Thread #" + (i + 1));
  200.       t.start();
  201.       threadList.add(t);
  202.      }
  203.      while (threadList.size() > 0) {
  204.       Thread child = (Thread) threadList.remove(0);
  205.       try {
  206.        child.join();
  207.       } catch (InterruptedException ie) {
  208.        logger.error("InterruptedException : " + ie);
  209.       }
  210.      }
  211.      // for (int i = 0; i < threads; i++) {
  212.      // threadPool.getThreadPoolExcutor().execute(new
  213.      // Thread(this,"Spider Thread #" + (i + 1)));
  214.      // }
  215.      long elapsed = System.currentTimeMillis() - start;
  216.      logger.info("Finished in " + (elapsed / 1000) + " seconds");
  217.      logger.info("The Count of the Links Captured is "
  218.        + indexedURLs.size());
  219.     } catch (MalformedURLException e) {
  220.      e.printStackTrace();
  221.     }
  222.    }
  223. }
  224. public void run() {
  225.    String url;
  226.    while ((url = dequeueURL()) != null) {
  227.     if (justDatabase) {
  228.      process(url);
  229.     }
  230.    }
  231.    threads--;
  232. }
  233. /**
  234. * 处理单独的URL地址,解析页面并加入到lucene索引中;通过自动探测页面编码保证抓取工作的顺利执行
  235. */
  236. protected void process(String url) {
  237.    String result[];
  238.    String content = null;
  239.    String title = null;
  240.    result = HtmlParserUtil.parseHtml(url, charset);
  241.    content = result[0];
  242.    title = result[1];
  243.    if (content != null && content.trim().length() > 0) {
  244.     // content
  245.     System.out.println(url);
  246.     // title
  247.     // DateTools.timeToString(System.currentTimeMillis()
  248.    }
  249. }
  250. /* 从URL队列mPages里取出单个的URL */
  251. public synchronized String dequeueURL() {
  252.    while (true)
  253.     if (URLs.size() > 0) {
  254.      String url = (String) URLs.remove(0);
  255.      indexedURLs.add(url);
  256.      if (isToBeCaptured(url)) {
  257.       NodeList list;
  258.       try {
  259.        int bookmark = URLs.size();
  260.        /* 获取页面所有节点 */
  261.        parser.setURL(url);
  262.        try {
  263.         list = new NodeList();
  264.         for (NodeIterator e = parser.elements(); e
  265.           .hasMoreNodes();)
  266.          list.add(e.nextNode());
  267.        } catch (EncodingChangeException ece) {
  268.         /* 解码出错的异常处理 */
  269.         parser.reset();
  270.         list = new NodeList();
  271.         for (NodeIterator e = parser.elements(); e
  272.           .hasMoreNodes();)
  273.          list.add(e.nextNode());
  274.        }
  275.        /**
  276.        * 依据 http://www.robotstxt.org/wc/meta-user.html 处理
  277.        * Robots <META> tag
  278.        */
  279.        NodeList robots = list
  280.          .extractAllNodesThatMatch(
  281.            new AndFilter(new NodeClassFilter(
  282.              MetaTag.class),
  283.              new HasAttributeFilter("name",
  284.                "robots")), true);
  285.        if (0 != robots.size()) {
  286.         MetaTag robot = (MetaTag) robots.elementAt(0);
  287.         String content = robot.getAttribute("content")
  288.           .toLowerCase();
  289.         if ((-1 != content.indexOf("none"))
  290.           || (-1 != content.indexOf("nofollow")))
  291.          for (int i = bookmark; i < URLs.size(); i++)
  292.           URLs.remove(i);
  293.        }
  294.       } catch (ParserException pe) {
  295.        logger.error("ParserException : " + pe);
  296.       }
  297.       return url;
  298.      }
  299.     } else {
  300.      threads--;
  301.      if (threads > 0) {
  302.       try {
  303.        wait();
  304.        threads++;
  305.       } catch (InterruptedException ie) {
  306.        logger.error("InterruptedException : " + ie);
  307.       }
  308.      } else {
  309.       notifyAll();
  310.       return null;
  311.      }
  312.     }
  313. }
  314. private boolean isHTML(String url) {
  315.    if (!url.endsWith(".html")) {
  316.     return false;
  317.    }
  318.    if (StringUtil.isNotBlank(contentPath)) {
  319.     if (!url.startsWith(baseURL + "/" + contentPath)) {
  320.      return false;
  321.     }
  322.    }
  323.    return true;
  324. }
  325. /**
  326. * 判断提取到的链接是否符合解析条件;标准为Port及Host与基准URL相同且类型为text/html或text/plain
  327. */
  328. public boolean isToBeCaptured(String url) {
  329.    boolean flag = false;
  330.    HttpURLConnection uc = null;
  331.    int responseCode = 0;
  332.    String contentType = "";
  333.    String host = "";
  334.    int port = 0;
  335.    try {
  336.     URL source = new URL(url);
  337.     String protocol = source.getProtocol();
  338.     if (protocol != null && protocol.equals("http")) {
  339.      host = source.getHost();
  340.      port = source.getPort();
  341.      uc = (HttpURLConnection) source.openConnection();
  342.      uc.setConnectTimeout(8000);
  343.      responseCode = uc.getResponseCode();
  344.      contentType = uc.getContentType();
  345.     }
  346.    } catch (MalformedURLException mue) {
  347.     logger.error("Invalid URL : " + url);
  348.    } catch (UnknownHostException uhe) {
  349.     logger.error("UnknowHost : " + url);
  350.    } catch (SocketException se) {
  351.     logger.error("Socket Error : " + se.getMessage() + " " + url);
  352.    } catch (SocketTimeoutException ste) {
  353.     logger.error("Socket Connection Time Out : " + url);
  354.    } catch (FileNotFoundException fnfe) {
  355.     logger.error("broken link " + url + " ignored");
  356.    } catch (IOException ie) {
  357.     logger.error("IOException : " + ie);
  358.    }
  359.    if (port == basePort
  360.      && responseCode == HttpURLConnection.HTTP_OK
  361.      && host.equals(baseHost)
  362.      && (contentType.startsWith("text/html") || contentType
  363.        .startsWith("text/plain")))
  364.     flag = true;
  365.    return flag;
  366. }
  367. class LocalLinkTag extends LinkTag {
  368.    public void doSemanticAction() {
  369.     String link = getLink();
  370.     if (link.endsWith("/"))
  371.      link = link.substring(0, link.length() - 1);
  372.     int pos = link.indexOf("#");
  373.     if (pos != -1)
  374.      link = link.substring(0, pos);
  375.     /* 将链接加入到处理队列中 */
  376.     if (!(indexedURLs.contains(link) || URLs.contains(link))) {
  377.      if (isHTML(link)) {
  378.       URLs.add(link);
  379.      }
  380.     }
  381.     setLink(link);
  382.    }
  383. }
  384. /**
  385. * Frame tag that rewrites the SRC URLs. The SRC URLs are mapped to local
  386. * targets if they match the source.
  387. */
  388. class LocalFrameTag extends FrameTag {
  389.    public void doSemanticAction() {
  390.     String link = getFrameLocation();
  391.     if (link.endsWith("/"))
  392.      link = link.substring(0, link.length() - 1);
  393.     int pos = link.indexOf("#");
  394.     if (pos != -1)
  395.      link = link.substring(0, pos);
  396.     /* 将链接加入到处理队列中 */
  397.     if (!(indexedURLs.contains(link) || URLs.contains(link))) {
  398.      if (isHTML(link)) {
  399.       URLs.add(link);
  400.      }
  401.     }
  402.     setFrameLocation(link);
  403.    }
  404. }
  405. /**
  406. * Base tag that doesn't show. The toHtml() method is overridden to return
  407. * an empty string, effectively shutting off the base reference.
  408. */
  409. class LocalBaseHrefTag extends BaseHrefTag {
  410.    public String toHtml() {
  411.     return ("");
  412.    }
  413. }
  414. public String getBaseURL() {
  415.    return baseURL;
  416. }
  417. public void setBaseURL(String baseURL) {
  418.    this.baseURL = baseURL;
  419. }
  420. public int getThreads() {
  421.    return threads;
  422. }
  423. public void setThreads(int threads) {
  424.    this.threads = threads;
  425. }
  426. public String getCharset() {
  427.    return charset;
  428. }
  429. public void setCharset(String charset) {
  430.    this.charset = charset;
  431. }
  432. public int getBasePort() {
  433.    return basePort;
  434. }
  435. public void setBasePort(int basePort) {
  436.    this.basePort = basePort;
  437. }
  438. public String getBaseHost() {
  439.    return baseHost;
  440. }
  441. public void setBaseHost(String baseHost) {
  442.    this.baseHost = baseHost;
  443. }
  444. public boolean isJustDatabase() {
  445.    return justDatabase;
  446. }
  447. public void setJustDatabase(boolean justDatabase) {
  448.    this.justDatabase = justDatabase;
  449. }
  450. public String getContentPath() {
  451.    return contentPath;
  452. }
  453. public void setContentPath(String contentPath) {
  454.    this.contentPath = contentPath;
  455. }
  456. }
  457. spring上的配置文件applicationContext-bean.xml:
  458. <bean id="productCapture"
  459.    class="com.sillycat.api.thread.runner.HtmlCaptureRunner" >
  460.    <property name="contentPath" value="${product.contentPath}" />
  461.    <property name="basePort" value="${product.base.port}" />
  462.    <property name="baseURL" value="${product.base.url}" />
  463.    <property name="charset" value="${product.base.code}" />
  464.    <property name="threads" value="${product.base.threads}"/>
  465. </bean>
  466. <bean id="messageCapture"
  467.    class="com.sillycat.api.thread.runner.HtmlCaptureRunner" >
  468.    <property name="contentPath" value="${message.contentPath}" />
  469.    <property name="basePort" value="${message.base.port}" />
  470.    <property name="baseURL" value="${message.base.url}" />
  471.    <property name="charset" value="${message.base.code}" />
  472.    <property name="threads" value="${message.base.threads}"/>
  473. </bean>
  474. easySearch.properties配置文件:
  475. #==========================================
  476. # spider configration
  477. #=========================================
  478. product.contentPath=product
  479. product.base.port=80
  480. product.base.url=http://www.safedv.com
  481. product.base.code=UTF-8
  482. product.base.threads=3
  483. message.contentPath=message
  484. message.base.port=80
  485. message.base.url=http://www.safedv.com
  486. message.base.code=UTF-8
  487. message.base.threads=3
  488. 单元测试类HtmlRunnerTest.java文件:
  489. package com.sillycat.api.thread;
  490. import com.sillycat.api.commons.base.BaseManagerTest;
  491. import com.sillycat.api.thread.runner.HtmlCaptureRunner;
  492. public class HtmlRunnerTest extends BaseManagerTest {
  493. private HtmlCaptureRunner productCapture;
  494. private HtmlCaptureRunner messageCapture;
  495. protected void setUp() throws Exception {
  496.    super.setUp();
  497.    productCapture = (HtmlCaptureRunner) appContext.getBean("productCapture");
  498.    messageCapture = (HtmlCaptureRunner) appContext.getBean("messageCapture");
  499. }
  500. protected void tearDown() throws Exception {
  501.    super.tearDown();
  502. }
  503. public void testDumy() {
  504.    assertTrue(true);
  505. }
  506. public void ntestProductCapture() {
  507.    productCapture.capture();
  508. }
  509. public void testMessageCapture(){
  510.    messageCapture.capture();
  511. }
  512. }
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值