package com.jinzhi.spider;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.stream.events.StartDocument;

//多线程类,爬虫程序要用多线程来处理网页内容的爬取,效率更高
public class SearchCrawler implements Runnable {
 /**
  *
  * disallowListCache缓存robot不允许搜索的URL。
  * Robot协议在Web站点的根目录下设置一个robots.txt文件, 
     * 规定站点上的哪些页面是限制搜索的。 搜索程序应该在搜索过程中跳过这些区域
  *
  **/
 //不允许搜索的URL缓存
 private HashMap<String, ArrayList<String>> disallowListCache = new HashMap<String, ArrayList<String>>();//设置一存储限制搜索的区域
 ArrayList<String> errorList = new ArrayList<String>();//错误链接集合
 public ArrayList<String> result = new ArrayList<>();//结果集合
 String startUrl;//开始搜索的起点
 int maxUrl;//最大处理的url数;
 String searchString;//要搜索的字符串(英文)
 boolean caseSensitive = false;//大小写是否敏感
 boolean limitHost = false;//是否在限制的主机内搜索
 
 //请求任务URL,根据得到的URL下载相应的HTML代码,利用HTML代码调用其他模块完成相关处理。
 public SearchCrawler(String startUrl, int maxUrl, String searchString) {
  this.startUrl = startUrl;
  this.maxUrl = maxUrl;
  this.searchString = searchString;
 }
 //得到结果——结果肯定不是一个字符串,所以选择用字符串集合,
 public ArrayList<String> getResult() {
  
  return result;
 }
 //启动搜索线程
 public void run() {
  //真正查询操作
  crawl(startUrl, maxUrl, searchString, limitHost, caseSensitive);
  
 }
 //把url包装为URL类
 private URL verifyUrl(String url) {
  //不是以Http://开头则返回null
  if(!url.toLowerCase().startsWith("http://"))
   //方法直接返回
   return null;
   //声明一个新的url
   URL verifiedUrl = null;
  try {
   //用传递进来的url包装一个URL
   verifiedUrl = new URL(url);
  } catch (Exception e) {
   //否则
   return null;
  }
  //把包装好的URL对象返回
  return verifiedUrl;
 }
 //检测URL是否允许被使用
 private boolean isRobotAllowed(URL urlToCheck) {
  //通过URL获取给出对应的主机
  String host = urlToCheck.getHost().toLowerCase();
  //通过主机获取url集合,通过Map键值对的形式,获取主机不允许搜索的URL(是以ArrayList的形式标识)缓存
  ArrayList<String> disallowList = disallowListCache.get(host);
  //如果没有缓存,下载并缓存
  if(disallowList == null) {
   disallowList  = new ArrayList<>();
   try {
    //准备记录不允许搜索的URL
    URL robotsFileUrl = new URL("http://" + host + "/robots.txt");
    //把不允许搜索的URL实例获取的字节流转换为字符流    //打开到此 URL 的连接并返回一个用于从该连接读入的 InputStream。
    BufferedReader reader = new BufferedReader(new InputStreamReader(robotsFileUrl.openStream()));
    //读取robot文件,创建不允许访问的路径列表
    
    String line;//设置字符串缓冲区
    //不等于空就继续读取
    while((line=reader.readLine())!=null) {
     //判断是否包含字符串Disallow
     if(line.indexOf("Disallow:") == 0) {
      //获取不允许访问路径——   1.去除Disallow:
      String disallowPath = line.substring("Disallow:".length());
      //检测是否有注释
      int commenIndex = disallowPath.indexOf("#");
      if(commenIndex != -1) {
       //   2.去掉注释
       disallowPath = disallowPath.substring(0, commenIndex);
      }
      //  3. 去掉空格
      disallowPath = disallowPath.trim();
      //把不允许访问路径添加到不允许访问集合记录起来
      disallowList.add(disallowPath);
     }
    }
    //缓存此主机不允许访问的路径  
    disallowListCache.put(host, disallowList);    
   
   } catch (Exception e) {
  //web站点根目录下没有robots.txt文件,返回真
    return true;
   }
  }
  //从参数URL中得到文件
  String file = urlToCheck.getFile();
  //遍历不允许访问路径记录的集合
  for(int i = 0; i < disallowList.size(); i++) {
   //得到每一个不允许访问的路径
   String disallow = disallowList.get(i);
   //判断从给出的URL得到的文件如果为不被允许访问的文件
   if(file.startsWith(disallow)) {
    //返回false;
    return false;
   }
  }
  //否则,返回真
  return true;
   
 }
 //下载页面
 public String downloadPage(URL pageUrl) {
  try {
   //根据URL初始化缓冲字符流
   BufferedReader reader = new BufferedReader(new InputStreamReader(pageUrl.openStream()));
   String line;//设置字符串缓冲区
   StringBuffer pageBuffer = new StringBuffer();
   while((line = reader.readLine()) != null) {
    //把页面数据添加到字符串缓冲区
    pageBuffer.append(line);
   }
   //返回下载的数据
   return pageBuffer.toString();
   
  } catch (Exception e) {
   // TODO: handle exception
  }
  //否则,返回null
  return null;
  
 }
 //去除url中的"www"
 private String removeWwwFromUrl(String url) {
  //获取"://www."的位置
  int index = url.indexOf("://www.");
  if(index != -1) {
   //如果存在,刚好截取出:www
   return url.substring(0, index + 3) + url.substring(index + 7);
  }
  return (url);
 }
 //查找目标链接
 private ArrayList<String> retrieveLinks(URL pageUrl, String pageContents, HashSet crawledList, boolean limitHost) {
  //用正则表达式编译链接的匹配模式
  Pattern p = Pattern.compile("<a\\s+href\\s*=\\s*\"?(.*?)[\"|>]",Pattern.CASE_INSENSITIVE);
  //看页面内容是否和正则表达式匹配
  Matcher m = p.matcher(pageContents);
  //准备好链接集合
  ArrayList<String> linkList = new ArrayList<>();
  while(m.find()) {
   //得到匹配正则分组的第1组
   String link = m.group(1).trim();
   if(link.length() < 1) {
    //证明没有目标链接
    continue;
   }
   //跳过链到本页面的内链接
   if(link.charAt(0) == '#') {
    //敏感资源链接
    continue;
   }
   if(link.indexOf("mailto:") != -1) {
    //如果包含mailto:也跳过
    continue;
   }
   if(link.toLowerCase().indexOf("javascript") != -1) {
    //如果包含javascript也跳过
    continue;
   }
   //如果存在"://"字符串,则证明找到目标链接
   if(link.indexOf("://") == -1) {
    //处理绝对地址——如果是:以/开头的地址链接
    if(link.charAt(0) == '/') {
     //拼成完整路径
     link = "http://" + pageUrl.getHost() + ":" + pageUrl.getPort() + link;
    } else {
     String file = pageUrl.getFile();
     //处理相对地址——如果存在:/字符
     if(file.indexOf('/') == -1) {
      link = "http://" + pageUrl.getHost() + ":"+ pageUrl.getPort() + "/" +link;
     }
    }
   }
   int index = link.indexOf("#");
   //如果连接URL存在#——存在注解
   if(index != -1) {
    link = link.substring(0, index);
   }
   //去除www
   link = removeWwwFromUrl(link);
   //封装为已证实的url
   URL verifiedLink = verifyUrl(link);
   if(verifiedLink == null) {
    //不是目标链接
    continue;
   }
   //如果限定主机,排除那些不合条件的URL为未证实(非目标URL)
   if(limitHost && !pageUrl.getHost().toLowerCase().equals(verifiedLink.getHost().toLowerCase())) {
    
    continue;
   }
   //跳过已处理的连接
   if(crawledList.contains(link)) {
    //处理过的链接集合中包括正在处理的连接,直接跳过
    continue;
   }
   //把符合标准的链接放入集合中
   linkList.add(link);
  }
  //返回集合
  return (linkList);
 }
 //判断有无目标字符串
 private boolean searchStringMatches(String pageContents, String searchString, boolean caseSensitive) {
  String searchContents = pageContents;
  //大小写不敏感
  if(!caseSensitive) {
   searchContents = pageContents.toLowerCase();
  }
  Pattern p = Pattern.compile("[\\s]+");//这里查询s为核武
  String[] terms = p.split(searchString);
  for(int i = 0; i < terms.length; i++) {
   if(caseSensitive) {
    if(searchContents.indexOf(terms[i]) == -1) {
     return false;
    }
   }
  }
  return true;
 }
 
  //执行实际的搜索操作  
    public ArrayList<String> crawl(String startUrl, int maxUrls,  
            String searchString, boolean limithost, boolean caseSensitive) {  
     //初始化一个已处理连接的集合
        HashSet<String> crawledList = new HashSet<String>();  
        LinkedHashSet<String> toCrawlList = new LinkedHashSet<String>();  
        //最大URL处理数<1——这里链接的url里没有有用信息
        if (maxUrls < 1) {  
         //判断用户给定要获取的URL的处理数——如果输入错误数据,则记录在集合里
            errorList.add("Invalid Max URLs value.");  
            System.out.println("Invalid Max URLs value.");  
        }  
        //用户给定的目标字符串长度小于1
        if (searchString.length() < 1) { 
         //记录入错误链接集合
            errorList.add("Missing Search String.");  
            System.out.println("Missing search String");  
        }  
        //错误链接集合有元素,则返回该集合
        if (errorList.size() > 0) {  
            System.out.println("err!!!");  
            return errorList;  
        }  
 
        // 从开始URL中移出www  
        startUrl = removeWwwFromUrl(startUrl);  
        //把移除"www"的url添加到url等待队列
        toCrawlList.add(startUrl);  
        //等待队列有元素
        while (toCrawlList.size() > 0) { 
         //用户给定获取的URL数量
            if (maxUrls != -1) {  
             //当已获取的有效链接数和用户想要得到的链接数相同时——达到目标数目
                if (crawledList.size() == maxUrls) {
                 //终止循环
                    break;  
                }  
            }  
 
            // 从等待队列中获取url
            String url = toCrawlList.iterator().next();  
 
            //从等待队列中去除该url
            toCrawlList.remove(url);
           
            //url——>URL
            URL verifiedUrl = verifyUrl(url);  
 
            //查看是否被允许访问
            if (!isRobotAllowed(verifiedUrl)) {
             //如果是不允许被访问的URL
                continue;  
            }  
 
            // 增加已处理的URL到crawledList  
            crawledList.add(url);  
            //根据URL下载页面得到目的字符串
            String pageContents = downloadPage(verifiedUrl);  
 
            if (pageContents != null && pageContents.length() > 0) {  
                // 从页面中获取目的链接  
                ArrayList<String> links = retrieveLinks(verifiedUrl,  
                        pageContents, crawledList, limitHost);  
                //目的链接加入等待队列
                toCrawlList.addAll(links);  
 
                if (searchStringMatches(pageContents, searchString,  
                        caseSensitive)) {  
                 //如果有目标字符串,则把URL记录到结果集里边
                    result.add(url);  
                    System.out.println(url);  
                }  
            }  
 
        }  
        return result;  
    }

// 主函数  
    public static void main(String[] args) {  
       
        SearchCrawler crawler = new SearchCrawler(
        "http://www.sohu.com",20,"N");  
        //创建线程    
        Thread search = new Thread(crawler);  
        System.out.println("Start searching...");  
        System.out.println("result:");  
        //启动线程
        search.start();  
        try {  
         //等待线程执行完毕后执行——执行完毕的标志
            search.join();  
        } catch (InterruptedException e) {  
            
            e.printStackTrace();  
        }  
    }  
 
}

运行截图如下:

虽然达到了预期的效果,但是还有很多不足的地方,也没有友好的界面,我将继续改进。

spider爬虫练习的更多相关文章

  1. spider 爬虫文件基本参数(3)

    一 代码 # -*- coding: utf-8 -*- import scrapy class ZhihuSpider(scrapy.Spider): # 爬虫名字,名字唯一,允许自定义 name ...

  2. Spider爬虫 の 事

      初识Spider_Man(爬爬虫) Spider_Man_2 の requests模块   Spider_Man_3 の selenium   Spider_Man_4 の BeautifulSo ...

  3. Spider爬虫-get、post请求

    1:概念: 爬虫就是通过编写程序,模拟浏览器上网,然后让其去互联网上抓取数据的过程. 2:python爬虫与其他语言的比较: (1)php爬虫弊端:多进程多线程支持的不好 (2)java:代码臃肿,重 ...

  4. burp suite之spider(爬虫)

    spider (蜘蛛,这里的意思指爬行) 像蜘蛛一样在网站上爬行出网站的个个目录信息,并发送至Target. 1.Control(控制) Spider is paused :停止蜘蛛爬行 Clear ...

  5. Spider爬虫基础

    get获取某个网站的html代码,post访问网站获取网站返回的信息 import urllib.request import urllib.parse #使用get请求 def start1(): ...

  6. Spider爬虫清洗数据(re方法)

    import re s0 = 'BOY and GIRL' s1 = re.sub(r'BOY|GIRL', 'HUMAN', s0) print s1 # HUMAN and HUMAN 替换方法.

  7. 第十六节:Scrapy爬虫框架之项目创建spider文件数据爬取

    Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架. 其可以应用在数据挖掘,信息处理或存储历史数据等一系列的程序中.其最初是为了页面抓取所设计的, 也可以应用在获取API所返回的数据或 ...

  8. Golang tool:include spider library,image library and some other db library such as mysql,redis,mogodb,hbase,cassandra

    一.Go_tool This is a tool library for Golang.Dont't worry about not understant it! All comment writes ...

  9. python爬虫学习--防盗链

    一 首先要了解什么是盗链 盗链是指服务提供商自己不提供服务的内容,通过技术手段绕过其它有利益的最终用户界面(如广告),直接在自己的网站上向最终用户提供其它服务商的服务内容,骗取最终用户的浏览和点击率. ...

随机推荐

  1. php基础--来自网页转载

    注意:1.网页文件放在wamp中的www文件下:2.www文件下不能出现中文:网页浏览的方法:1.没有建立站点:localhost/文件所在位置2.建立站点:(1)站点-新建站点-打开对话框 (2)修 ...

  2. reference to 'map' is ambiguous|

    reference to 'map' is ambiguous| c++编译出现此错误    表明定义的变量名字map和库函数map冲突而产生歧义

  3. 一个LinkedBlockingQueue线程安全的例子

    一个LinkedBlockingQueue线程安全的例子 package llj.mf.ace; import java.util.ArrayList; import java.util.HashSe ...

  4. 前端面试题汇总(主要为 Vue)

    前端面试题汇总 1. 谈谈你对MVVM开发模式的理解 MVVM分为Model.View.ViewModel三者. 1)Model:代表数据模型,数据和业务逻辑都在Model层中定义: 2)View:代 ...

  5. Window Server配置Flask

    1.安装了Chrome 2.安装git 3.创建SSH key:ssh-keygen -t rsa -C "youremail@example.com" 4.安装notepad++ ...

  6. windows下安装php reids扩展

    1.使用phpinfo()函数查看PHP的版本信息,这会决定扩展文件版本. 2.下载php_igbinary-1.2.1-5.5-ts-vc11-x64.zip,php_redis-2.2.5-5.6 ...

  7. [LeetCode] 132. Palindrome Partitioning II_ Hard tag: Dynamic Programming

    Given a string s, partition s such that every substring of the partition is a palindrome. Return the ...

  8. django js引入失效问题

    今天将项目中html文件下的自定义scrept代码单独独立,结果js引入无效,没有任何时间效果,在浏览器查看引入文件也正常. 后来发现自己引入的位置不对,js的引入文件应该放在body体内,而我把他们 ...

  9. Windows Java安装

    jdk安装与配置jdk for windows1.下载官网地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html2. ...

  10. CF830A Office Keys(贪心)

    CF830A Office Keys [题目链接]CF830A Office Keys [题目类型]贪心 &题意: 有n个人,k个钥匙,一个目的地,求让n个人都回到目的地的最短时间,每个人都要 ...