Advertisement
Guest User

JCrawler.java

a guest
May 29th, 2010
359
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 14.17 KB | None | 0 0
  1. package jcrawler;
  2.  
  3. import java.io.*;
  4. import java.net.*;
  5. import java.util.Iterator;
  6. import java.util.LinkedList;
  7. import java.util.List;
  8. import java.util.Stack;
  9. import java.util.logging.Level;
  10. import java.util.logging.Logger;
  11.  
  12. /**
  13.  * @author Jan Kuboschek
  14.  *
  15.  * Basic crawler class to easily and quickly interact with one website.
  16.  * Override "doAction(String URL, String content)" to process the content further
  17.  * (e.g. store it, parse it).
  18.  *
  19.  * Concept allows for multi-threading of crawlers. All class instances share
  20.  * processed and queued lists of links.
  21.  *
  22.  * Instead of keeping track of processed links and queued links within the object,
  23.  * a JDBC connection could be established to store links in a database.
  24.  *
  25.  * Currently limited to one website at a time, however, could be expanded upon
  26.  * by adding an externalLinks stack and adding to it as appropriate.
  27.  *
  28.  * JCrawler is intended to be used to quickly generate XML sitemaps or parse websites
  29.  * for your desired information. It's lightweight.
  30.  */
  31. public abstract class JCrawler extends Thread
  32. {
  33.     static private Stack<String> internalLinks;
  34.     static private LinkedList<String> processedLinks;
  35.     static private String currentDomain;
  36.     static private int numberObjects;
  37.  
  38.     static private int connectionRetries;
  39.  
  40.     private boolean pause;
  41.  
  42.  
  43.     /**************************************************
  44.      * Construct a new JCrawler object. Requires an object constructed with URL first.
  45.      **************************************************/
  46.     JCrawler()
  47.     {
  48.         if (internalLinks == null || currentDomain == null)
  49.         {
  50.             System.out.println("Error: First JCrawler object must be called with URL.");
  51.             System.exit(0);
  52.         }
  53.         numberObjects++;
  54.         pause = false;
  55.         crawl();
  56.     }
  57.  
  58.     /**************************************************
  59.      * Construct a new JCrawler object with parameter.
  60.      * If object with parameter has already been constructed,
  61.      * treats this as the default constructor.
  62.      **************************************************/
  63.     JCrawler(String URL)
  64.     {
  65.         if (internalLinks == null)
  66.         {
  67.             internalLinks = new Stack<String>();
  68.             processedLinks= new LinkedList<String>();
  69.             currentDomain = getDomainFromURL(URL);
  70.             numberObjects = 1;
  71.             connectionRetries = 3;
  72.         }
  73.         else
  74.             numberObjects++;
  75.         internalLinks.add(URL);
  76.         pause = false;
  77.         crawl();
  78.     }
  79.  
  80.     /**************************************************
  81.      * Overwrite to customize crawler to i.e. parse contents, build XML site maps, etc.
  82.      **************************************************/
  83.     abstract void doAction(String URL, String content);
  84.  
  85.     /**************************************************
  86.      * Pauses the crawler.
  87.      **************************************************/
  88.     public void pauseCrawler() { pause = true; }
  89.  
  90.     /**************************************************
  91.      * Resumes the crawler.
  92.      **************************************************/
  93.     public void resumeCrawler() { pause = false; }
  94.  
  95.     /**************************************************
  96.      * Returns a list of processed links.
  97.      **************************************************/
  98.     public List<String> getProcessedLinks() { return processedLinks; }
  99.  
  100.     /**************************************************
  101.      * Checks if crawler is paused. If not, check if crawler has been idle for too long.
  102.      * If not, fetch link from stack, read content, push new links onto stack and call doAction (an
  103.      * abstract method to do something with the content).
  104.      *************************************************/
  105.     private void crawl()
  106.     {
  107.         (new Thread() {
  108.             @Override
  109.             public void run() {
  110.                 int runTime = 0;
  111.                 while (true)
  112.                 {
  113.                     if (runTime == 5)
  114.                     {
  115.                         System.out.println("Stopping thread. Queue has been empty for "+runTime+" seconds.");
  116.                         return;
  117.                     }
  118.                     else
  119.                     {
  120.                         if (!pause)
  121.                         {
  122.                             runTime++;
  123.                             if (internalLinks.size() > 0)
  124.                             {
  125.                                 runTime = 0;
  126.                                 String URL = internalLinks.pop();
  127.                                 String content = "";
  128.                                 if (!processedLinks.contains(URL))
  129.                                 {
  130.                                     content = getWebsiteContent(URL);
  131.                                     if (!content.equals(""))
  132.                                     {
  133.                                         doAction(URL, content);
  134.                                         try {
  135.                                             List<String> links = HTMLUtils.extractLinks(content);
  136.                                             for (String link : links)
  137.                                             {
  138.                                                 link = processLink(link, URL);
  139.                                                 if (!link.equals("") && (getDomainFromURL(link).equalsIgnoreCase(currentDomain)) && (!internalLinks.contains(link)))
  140.                                                     internalLinks.push(link);
  141.                                             }
  142.                                         } catch (IOException ex) {
  143.                                             Logger.getLogger(JCrawler.class.getName()).log(Level.SEVERE, null, ex);
  144.                                         }
  145.                                     }
  146.                                 }
  147.                                 processedLinks.push(URL);
  148.                             }
  149.                             else
  150.                             {
  151.                                 try {
  152.                                     Thread.sleep(1000);
  153.                                 } catch (InterruptedException ex) {
  154.                                     Logger.getLogger(JCrawler.class.getName()).log(Level.SEVERE, null, ex);
  155.                                 }
  156.                                 if (numberObjects > 1)
  157.                                     System.out.println("Queue currently empty...waiting...");
  158.                                 else
  159.                                 {
  160.                                     printProcessedLinks();
  161.                                     System.out.println("Queue empty...quitting.");
  162.                                     return;
  163.                                 }
  164.                             }
  165.                         }
  166.                         else
  167.                             System.out.println("Paused...waiting to resume...");
  168.                     }
  169.                 }
  170.             }
  171.         }).start();
  172.      }
  173.  
  174.     /**************************************************
  175.      * Fetches the content of URL and returns it, otherwise returns a blank
  176.      * string and prints an appropriate error message.
  177.      **************************************************/
  178.     private String getWebsiteContent(String URL)
  179.     {
  180.         boolean noError = false;
  181.         int i=0;
  182.         try
  183.         {
  184.             URL myURL = new URL(URL);
  185.             HttpURLConnection myConn = (HttpURLConnection)myURL.openConnection();
  186.             BufferedReader in;
  187.             HttpURLConnection.setFollowRedirects(false);
  188.             myConn.setInstanceFollowRedirects(false);
  189.             System.setProperty("http.agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)");
  190.             while ( (i < connectionRetries) && (noError == false) )
  191.             {
  192.                 noError = true;
  193.                 i++;
  194.                 try
  195.                 {
  196.                     in = new BufferedReader(new InputStreamReader(myConn.getInputStream()));
  197.                     myConn = (HttpURLConnection)myURL.openConnection();
  198.  
  199.                     if ( (myConn.getResponseCode() == 200) && (!myConn.getContentType().startsWith("image")) )
  200.                     {
  201.                         String buffer = "";
  202.                         String inputLine;
  203.  
  204.                         while ((inputLine = in.readLine()) != null)
  205.                         {
  206.                             buffer += inputLine + "\n";
  207.                         }
  208.                         in.close();
  209.                         System.out.println("Opening "+URL+"...Server reponse "+myConn.getResponseCode()+". OK. ");
  210.                         return buffer;
  211.                     }
  212.                     else
  213.                     {
  214.                         System.out.println("Opening "+URL+"... Server reponse "+myConn.getResponseCode()+", Content type "+myConn.getContentType()+". PAGE NOT INDEXED. ");
  215.                         return "";
  216.                     }
  217.                 }
  218.                 catch (SocketTimeoutException e)
  219.                 {
  220.                     System.out.println("Request timed out. Retrying ... "+i+" of "+connectionRetries+".");
  221.                     Thread.sleep(2000);
  222.                     noError = false;
  223.                 }
  224.                 catch (NoRouteToHostException e)
  225.                 {
  226.                     System.out.println("Lost internet connection. Retrying ... "+i+" of "+connectionRetries+".");
  227.                     Thread.sleep(5000);
  228.                     noError = false;
  229.                 }
  230.                 catch (UnknownHostException e)
  231.                 {
  232.                     System.out.println("Host is not available. Retrying ... "+i+" of "+connectionRetries+".");
  233.                     Thread.sleep(2000);
  234.                     noError = false;
  235.                 }
  236.                 catch (ConnectException e)
  237.                 {
  238.                     System.out.println("Unknown host. Retrying ... "+i+" of "+connectionRetries+".");
  239.                     Thread.sleep(2000);
  240.                     noError = false;
  241.                 }
  242.                 catch (java.io.FileNotFoundException e)
  243.                 {
  244.                     System.out.println("File not found... ");
  245.                 }
  246.             }
  247.         }
  248.         catch (MalformedURLException e)
  249.         {
  250.             System.out.println("Malformed URL...");
  251.             noError = false;
  252.         }
  253.         catch (java.io.IOException e)
  254.         {
  255.             System.out.println("Bad input... ");
  256.         }
  257.         catch (InterruptedException e)
  258.         {
  259.             System.out.println("Interrupted exception... ");
  260.         }
  261.        
  262.         System.out.println("Request timed out. Skipping "+URL+" ...");
  263.         return "";
  264.     }
  265.  
  266.     /**************************************************
  267.      * Returns the processed link or a blank string if link is null or an exception.
  268.      * Also normalizes link.
  269.      **************************************************/
  270.     public String processLink(String link, String URL)
  271.     {
  272.         if (link == null)
  273.             return "";
  274.         if (stripExceptions(link))
  275.             return link = normalizeLink(link, URL);
  276.         else
  277.             return "";
  278.     }
  279.  
  280.     /**************************************************
  281.      * Returns normalized link. A relative link is turned into an absolute link.
  282.      **************************************************/
  283.     public String normalizeLink(String link, String URL)
  284.     {
  285.         link = link.replace("www.", "");
  286.         if (link.endsWith("/"))
  287.             link = link.substring(0, (link.length()-1));
  288.         if(link.startsWith("http://") == false)
  289.         {
  290.             String newURL = "";
  291.             while (link.startsWith("../") == true)
  292.             {
  293.                 String temp[] = new String[URL.split("/").length];
  294.                 temp  = URL.split("/");
  295.                 for (int i=0;i<temp.length-2;i++)
  296.                     newURL += temp[i]+"/";
  297.                 link = link.substring(3, link.length());
  298.             }
  299.             if (newURL.equals("")==false)
  300.                 URL = newURL;
  301.  
  302.             URL = getDomainFromURL(URL); // get the base URL
  303.             if (link.startsWith("/") == false)
  304.             {
  305.                 if (link.contains("."))
  306.                     return link = URL+"/"+link;
  307.                 else
  308.                     return link = URL+"/"+link+"/";
  309.             }
  310.             else
  311.             {
  312.                 return link = URL+link;
  313.             }
  314.         }
  315.         else
  316.             return link;
  317.     }
  318.  
  319.     /**************************************************
  320.      * Returns false if link matches an exception, otherwise returns true.
  321.      **************************************************/
  322.     private boolean stripExceptions(String link)
  323.     {
  324.         if (link.contains("mailto:"))
  325.         {
  326.             return false;
  327.         }
  328.         if (link.contains("ads"))
  329.         {
  330.             return false;
  331.         }
  332.         if (link.equals("/") == true)
  333.         {
  334.             return false;
  335.         }
  336.         if (link.contains("#"))
  337.         {
  338.             return false;
  339.         }
  340.         if (link.contains("javascript:"))
  341.         {
  342.             return false;
  343.         }
  344.         return true;
  345.     }
  346.  
  347.     /**************************************************
  348.      * Returns domain from URL.
  349.      * E.g. http://www.foo.com/bar => http://www.foo.com
  350.      **************************************************/
  351.     private String getDomainFromURL(String URL)
  352.     {
  353.         try{
  354.         URL = URL.replace("www.", "");
  355.         String partialURL[] = new String[4];
  356.         partialURL = URL.split("/",4);
  357.         URL = partialURL[0]+"/"+partialURL[1]+"/"+partialURL[2];
  358.         }
  359.         catch (ArrayIndexOutOfBoundsException e)
  360.         {
  361.             System.out.println("Out of bounds exception in URL.");
  362.         }
  363.         return URL;
  364.     }
  365.  
  366.     /**************************************************
  367.      * Prints out list of processed links
  368.      **************************************************/
  369.     public void printProcessedLinks()
  370.     {
  371.         Iterator<String> x = processedLinks.iterator();
  372.         System.out.print("Processed links: ");
  373.         while (x.hasNext())
  374.             System.out.println(x.next()+"|");
  375.     }
  376. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement