一个简单的蜘蛛程序,没有实现多线程,只考虑了一些特定的情况。
一句话:一切从简!
下载提供测试页面。
源码:
package com.wans.spider;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.sun.java_cup.internal.internal_error;
import com.sun.org.apache.bcel.internal.generic.NEW;
public class MySpider1 implements Runnable {
static ArrayList<String> waitUrlList = new ArrayList<String>(); //等待处理的urls
static ArrayList<String> disposeUrlList = new ArrayList<String>(); //要处理的urls
static ArrayList<String> errorUrlList = new ArrayList<String>(); //处理出错的urls
static ArrayList<String> overUrlList = new ArrayList<String>(); //处理完成的urls
//static ArrayList<String> outUrlList = new ArrayList<String>(); //发现外链urls
static ArrayList<String> Allhost=new ArrayList<String>(); //存放主机
static String webUrl = "http://192.168.0.25:8080/test_net_for_spider/";
//static String webUrl = "http://127.0.0.1:8080/test_net_for_spider/"; //对某个网站进行爬取
static long outtime = 1000; //连接超时时间
static int ceng = 5; //查询的层数
static int timeout = 10000; //连接超时
static int sheng = 4; //爬取深度
static int threads = 1; //抓取线程数为1
public static void main(String[] args) {
MySpider1 spider1 = new MySpider1();
for (int i = 1; i <= threads; i++) {
Thread thread = new Thread(spider1);
thread.start();
}
}
//使用多线程提高效率
public void run() {
//调用主体方法
body();
}
//主体方法
private static void body() {
//输出爬取开始基本信息
info();
//将爬取网站加入等待处理的urls
waitUrlList.add(webUrl);
System.out.println("webUrl:"+webUrl+" 已添加到 "+waitUrlList);
//检查等待处理的urls是否为空,不为空,将urls传到disposeUrlList。为空,停止爬取。
//处理urls,发现链接,判断是否是外链。是,加入outUrlList。不是,加入等待队列。
for(int i=1;i<=sheng;i++) {
if(waitUrlList.isEmpty() || waitUrlList.size() == 0) {
System.out.println("\r爬取结束。。。。");
overinfo();//统计爬取结束信息
break;
} else {
disposeUrlList.addAll(waitUrlList);//将等待urls加入处理urls
System.out.println("waitUrlList 加入 disposeUrlList 中 进行处理");
waitUrlList.clear(); //清空urls,为下次使用做准备。
for (String url : disposeUrlList) {
//获取url中的超链接,保证无重复。
ArrayList<String> urlsList = getUrls(url);
for (String urlt : urlsList) {
if(!disposeUrlList.contains(urlt) && !overUrlList.contains(urlt)) {
waitUrlList.add(urlt);
}
}
}
overUrlList.addAll(disposeUrlList); //完成urls
System.out.println("处理连接结束,写入 overUrlList");
System.out.println("处理过连接的个数: "+overUrlList.size());
disposeUrlList.clear();
}
}
}
//获取网页里的超链接,加入等待urls
private static ArrayList<String> getUrls(String url) {
ArrayList<String> urlsArrayList = new ArrayList<String>(); //存放链接
//获得网页内容
String urlConent = getHtml(url);
if(urlConent != null) {
//分析文本内容,获取链接
String regex="<a href.*</a>";
String output=null; //第一次提取的<a href * </a>
String outputUrl=null; //对第一次提取的Url再次过滤
Pattern pa=Pattern.compile(regex, Pattern.DOTALL);
Matcher ma=pa.matcher(urlConent); //初次过滤
while(ma.find()){
output=ma.group().trim();
regex="<a\\s+href\\s*=\\s*\"?(.*?)[\"|>]"; //再次过滤
// regex="http\\s*\"?(.*?)[\"|>]";
Pattern paa=Pattern.compile(regex,Pattern.DOTALL);
Matcher maa=paa.matcher(output);
while(maa.find()){
outputUrl=maa.group().trim();
// System.out.println();
// System.out.println(outputUrl);
if (outputUrl.length() < 1) {
continue;
}
//********************************************
//根据实际情况 过渡网页的某些垃圾信息
outputUrl=outputUrl.replace("<a href=", "");
outputUrl=outputUrl.replace("\"", "");
outputUrl=outputUrl.replace(">", "");
outputUrl=outputUrl.replace("/>", "");
outputUrl=outputUrl.replace("class=","");
outputUrl=outputUrl.replace("target=_blank", "");
outputUrl=outputUrl.replace("'target=_blank'", "");
outputUrl=outputUrl.replace("'_blank'", "");
outputUrl=outputUrl.replace("target=_self", "");
outputUrl=outputUrl.replace("target=", "");
outputUrl=outputUrl.replace("style=", "");
outputUrl=outputUrl.replace("../", "");
outputUrl=outputUrl.replace("#", "");
outputUrl=outputUrl.replace("title=", "");
outputUrl=outputUrl.replace("<a href=", "");
outputUrl=outputUrl.replace("'", "");
//*********************
// int endUrl=outputUrl.indexOf(" "); //处理url有空格问题
// if(endUrl>0){ //要是有空格才截断
// outputUrl=outputUrl.substring(0,endUrl); //以空格为结束
// }
//*************************
outputUrl=outputUrl.trim(); //过滤空间
if(outputUrl.length()>0){
if(outputUrl.indexOf("://")==-1){ //处理相对地址
int length=webUrl.length();
int find=webUrl.lastIndexOf("/")+1;
if(length==find){
outputUrl=webUrl+outputUrl; // 如果以/结尾
}else{
outputUrl=webUrl+"/"+outputUrl; // 如果不以 /结尾
}
}
int begin2=outputUrl.lastIndexOf("//")+2; //检查是否是以 //结尾
if(begin2==outputUrl.length()){
outputUrl=outputUrl.substring(0, outputUrl.length()-1); // 把 //变成/
}
if(!urlsArrayList.contains(outputUrl)){ //去掉重复的url
urlsArrayList.add(outputUrl);
}
}
}
}
}
return urlsArrayList;
}
//获取网页内容
private static String getHtml(String url) {
StringBuffer urlConent = new StringBuffer(); //装url中内容
try {
URL urlpath = new URL(url); //对应连接地址
String urlHost = urlpath.getHost(); //获取地址主机
if("".equals(urlHost) && !Allhost.contains(urlHost)) { //将主机加入
Allhost.add(urlHost);
}
HttpURLConnection urlcon = (HttpURLConnection)urlpath.openConnection();
HttpURLConnection.setFollowRedirects(true); //设置此类是否应该自动执行 HTTP 重定向(响应代码为 3xx 的请求)。
urlcon.setInstanceFollowRedirects(false); //返回此 HttpURLConnection 的 instanceFollowRedirects 字段的值。
urlcon.setConnectTimeout(timeout); //设置超时,这里直接调用系统设置的
//将读超时设置为指定的超时,以毫秒为单位。用一个非零值指定在建立到资源的连接后从 Input 流读入时的超时时间。
//如果在数据可读取之前超时期满,则会引发一个 java.net.SocketTimeoutException。超时时间为零表示无穷大超时。
//此方法的一些非标准实现会忽略指定的超时。要查看读入超时设置,请调用 getReadTimeout()。
urlcon.setReadTimeout(timeout);
urlcon.connect(); //打开到此 URL 引用的资源的通信链接(如果尚未建立这样的连接)。
System.out.println("\r正在连接 ...");
System.out.println("地址是: "+url);
String contentType=urlcon.getContentType();
System.out.println("方式为: "+contentType+" 状态: "+urlcon.getResponseMessage());
System.out.println("正在读取HTML流 ......");
String s="";
BufferedReader br = new BufferedReader(new InputStreamReader(urlcon.getInputStream()));
while((s=br.readLine()) != null) {
urlConent.append(s);
}
double size = urlConent.length();
System.out.println("ok 读取了 "+size/1024+"KB");
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return urlConent.toString();
}
//爬取结束信息
private static void overinfo() {
String info = null;
info += "本次爬取网页总数为:"+overUrlList.size()+"\r";
info += "错误网页总数为:"+errorUrlList.size()+"\r";
info += "爬取结束时间:" + getTime();
System.out.println(info);
}
//输出爬取开始基本信息
private static void info() {
String info = null;
info += "爬取开始时间:" + getTime() + "\r";
info += "主机地址:" + webUrl + "\r";
info += "超时时间是:" + outtime + "\r";
info += "查询的层数:" + ceng + "\r";
info += "限定主机搜索!" + "\r\r";
info += "启动爬取程序。。。。。\r";
System.out.println(info);
}
//获取时间
private static String getTime() {
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
return format.format(new Date());
}
}