spider爬虫练习
Posted StephenChowcai
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spider爬虫练习相关的知识,希望对你有一定的参考价值。
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爬虫练习的主要内容,如果未能解决你的问题,请参考以下文章