龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > 软件开发 > JAVA开发 >

零基础写Java知乎爬虫之进阶篇(2)

时间:2014-11-08 02:58来源:网络整理 作者:网络 点击:
分享到:
首先,对于先设计一个数据结构用来存储TODO表,考虑到需要先进先出所以采用队列,自定义一个Quere类: import java.util.LinkedList; /** * 自定义队列类 保存T

首先,对于先设计一个数据结构用来存储TODO表, 考虑到需要先进先出所以采用队列,自定义一个Quere类:

import java.util.LinkedList;
/**
 * 自定义队列类 保存TODO表
 */
public class Queue {
 /**
  * 定义一个队列,使用LinkedList实现
  */
 private LinkedList<Object> queue = new LinkedList<Object>(); // 入队列
 /**
  * 将t加入到队列中
  */
 public void enQueue(Object t) {
  queue.addLast(t);
 }
 /**
  * 移除队列中的第一项并将其返回
  */
 public Object deQueue() {
  return queue.removeFirst();
 }
 /**
  * 返回队列是否为空
  */
 public boolean isQueueEmpty() {
  return queue.isEmpty();
 }
 /**
  * 判断并返回队列是否包含t
  */
 public boolean contians(Object t) {
  return queue.contains(t);
 }
 /**
  * 判断并返回队列是否为空
  */
 public boolean empty() {
  return queue.isEmpty();
 }
}



还需要一个数据结构来记录已经访问过的 URL,即Visited表。

考虑到这个表的作用,每当要访问一个 URL 的时候,首先在这个数据结构中进行查找,如果当前的 URL 已经存在,则丢弃这个URL任务。

这个数据结构需要不重复并且能快速查找,所以选择HashSet来存储。

综上,我们另建一个SpiderQueue类来保存Visited表和TODO表:

import java.util.HashSet;
import java.util.Set;
/**
 * 自定义类 保存Visited表和unVisited表
 */
public class SpiderQueue {
 /**
  * 已访问的url集合,即Visited表
  */
 private static Set<Object> visitedUrl = new HashSet<>();
 /**
  * 添加到访问过的 URL 队列中
  */
 public static void addVisitedUrl(String url) {
  visitedUrl.add(url);
 }
 /**
  * 移除访问过的 URL
  */
 public static void removeVisitedUrl(String url) {
  visitedUrl.remove(url);
 }
 /**
  * 获得已经访问的 URL 数目
  */
 public static int getVisitedUrlNum() {
  return visitedUrl.size();
 }
 /**
  * 待访问的url集合,即unVisited表
  */
 private static Queue unVisitedUrl = new Queue();
 /**
  * 获得UnVisited队列
  */
 public static Queue getUnVisitedUrl() {
  return unVisitedUrl;
 }
 /**
  * 未访问的unVisitedUrl出队列
  */
 public static Object unVisitedUrlDeQueue() {
  return unVisitedUrl.deQueue();
 }
 /**
  * 保证添加url到unVisitedUrl的时候每个 URL只被访问一次
  */
 public static void addUnvisitedUrl(String url) {
  if (url != null && !url.trim().equals("") && !visitedUrl.contains(url)
    && !unVisitedUrl.contians(url))
   unVisitedUrl.enQueue(url);
 }
 /**
  * 判断未访问的 URL队列中是否为空
  */
 public static boolean unVisitedUrlsEmpty() {
  return unVisitedUrl.empty();
 }
}


上面是一些自定义类的封装,接下来就是一个定义一个用来下载网页的工具类,我们将其定义为DownTool类:

package controller;
import java.io.*;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.*;
import org.apache.commons.httpclient.params.*;
public class DownTool {
 /**
  * 根据 URL 和网页类型生成需要保存的网页的文件名,去除 URL 中的非文件名字符
  */
 private String getFileNameByUrl(String url, String contentType) {
  // 移除 "http://" 这七个字符
  url = url.substring(7);
  // 确认抓取到的页面为 text/html 类型
  if (contentType.indexOf("html") != -1) {
   // 把所有的url中的特殊符号转化成下划线
   url = url.replaceAll("[\\?/:*|<>\"]", "_") + ".html";
  } else {
   url = url.replaceAll("[\\?/:*|<>\"]", "_") + "."
     + contentType.substring(contentType.lastIndexOf("/") + 1);
  }
  return url;
 }
 /**
  * 保存网页字节数组到本地文件,filePath 为要保存的文件的相对地址
  */
 private void saveToLocal(byte[] data, String filePath) {
  try {
   DataOutputStream out = new DataOutputStream(new FileOutputStream(
     new File(filePath)));
   for (int i = 0; i < data.length; i++)
    out.write(data[i]);
   out.flush();
   out.close();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
 // 下载 URL 指向的网页
 public String downloadFile(String url) {
  String filePath = null;
  // 1.生成 HttpClinet对象并设置参数
  HttpClient httpClient = new HttpClient();
  // 设置 HTTP连接超时 5s
  httpClient.getHttpConnectionManager().getParams()
    .setConnectionTimeout(5000);
  // 2.生成 GetMethod对象并设置参数
  GetMethod getMethod = new GetMethod(url);
  // 设置 get请求超时 5s
  getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 5000);
  // 设置请求重试处理
  getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
    new DefaultHttpMethodRetryHandler());
  // 3.执行GET请求
  try {
   int statusCode = httpClient.executeMethod(getMethod);
   // 判断访问的状态码
   if (statusCode != HttpStatus.SC_OK) {
    System.err.println("Method failed: "
      + getMethod.getStatusLine());
    filePath = null;
   }
   // 4.处理 HTTP 响应内容
   byte[] responseBody = getMethod.getResponseBody();// 读取为字节数组
   // 根据网页 url 生成保存时的文件名
   filePath = "temp\\"
     + getFileNameByUrl(url,
       getMethod.getResponseHeader("Content-Type")
         .getValue());
   saveToLocal(responseBody, filePath);
  } catch (HttpException e) {
   // 发生致命的异常,可能是协议不对或者返回的内容有问题
   System.out.println("请检查你的http地址是否正确");
   e.printStackTrace();
  } catch (IOException e) {
   // 发生网络异常
   e.printStackTrace();
  } finally {
   // 释放连接
   getMethod.releaseConnection();
  }
  return filePath;
 }
}
精彩图集

赞助商链接