网络爬虫——基于JAVA的宽度优先遍历互联网结点

整个的宽度优先爬虫过程就是从一系列的种子节点开始,把这些网页中(种子结点网页)的“子节点” (也就是超链接)提取出来,放入队列中依次进行抓取。被处理过的链接需要放入一张表(通常称 为 Visited 表)中。每次新处理一个链接之前,需要查看这个链接是否已经存在于 Visited 表 中。如果存在,证明链接已经处理过,跳过,不做处理,否则进行下一步处理。实际的过 程如图 1.5 所示。

初始的 URL 地址是爬虫系统中提供的种子 URL(一般在系统的配置文件中指定)。当解析这些种子 URL 所表示的网页时,会产生新的 URL(比如从页面中的<a href= “http://www.admin.com”中提取出 http://www.admin.com 这个链接)。然后,进行以下工作:
(1) 把解析出的链接和 Visited 表中的链接进行比较,若 Visited 表中不存在此链接,表示其未被访问过。
(2) 把链接放入 TODO 表(TODO表用于存放未访问的链接URL)中。
(3) 处理完毕后,再次从 TODO 表中得一条链接,直接放入 Visited 表中。
(4) 针对这个链接所表示的网页,继续上述过程。如此循环往复。
表 1.3 显示了对图 1.3 所示的页面的爬取过程。

JAVA代码实现:

(1)Queue类:
 /** * 队列,保存将要访问的URL */
public class Queue {
//使用链表实现队列
private LinkedList queue = new LinkedList();
//入队列
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。 每当要访问一个 URL 的时候,首先在这个数据结构中进行查找,如果当前的 URL 已经存在,则丢弃它。

(2) LinkQueue类:

public class LinkQueue {
//已访问的 url 集合
 private static Set visitedUrl = new HashSet();
//待访问的 url 集合
private static Queue unVisitedUrl = new Queue();

//获得URL队列
 public static Queue getUnVisitedUrl() {
 return unVisitedUrl;
} 

//添加到访问过的URL队列中
public static void addVisitedUrl(String url) {
visitedUrl.add(url);
 }

 //移除访问过的URL
 public static void removeVisitedUrl(String url) {
 visitedUrl.remove(url);
} 

//未访问的URL出队列
public static Object unVisitedUrlDeQueue() {
 return unVisitedUrl.deQueue();
}

 // 保证每个 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 int getVisitedUrlNum() {
 return visitedUrl.size();
} 

//判断未访问的URL队列中是否为空
 public static boolean unVisitedUrlsEmpty() {
 return unVisitedUrl.empty();
}
}

  下面的代码详细说明了网页下载并处理的过程。比如如何存储网页,设置请求超时策略等。

(3) DownLoadFile类:
 public class DownLoadFile {
 /** * 根据 URL 和网页类型生成需要保存的网页的文件名,去除 URL 中的非文件名字符 */
 public String getFileNameByUrl(String url,String contentType) {
 //移除http:  url=url.substring(7);
//text/html类型
 if(contentType.indexOf("html")!=-1)
{
 url= url.replaceAll("[\\?/:*|<>\"]", "_")+".html";
 return url;
}
//如application/pdf类型
 else { return url.replaceAll("[\\?/:*|<>\"]", "_")+"."+ contentType.substring(contentType.lastIndexOf("/")+1);
       }
}
/** * 保存网页字节数组到本地文件,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().getParam().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.执行 HTTP 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("Please check your provided http address!");
 e.printStackTrace();
} catch (IOException e) {
// 发生网络异常
 e.printStackTrace();
 } finally {
// 释放连接 getMethod.releaseConnection();
              }
           }
       } return filePath;
   }
}

  (4)Java 有一个非常实用的开源工具包 HtmlParser,它专门针对 Html 页面进行处理,不仅能提取 URL,还能提取文本以及你想要的任何内容.下面的HtmlParserTool类将实现其具体的一些功能:

 public class HtmlParserTool {
 // 获取一个网站上的链接,filter 用来过滤链接
 public static Set<String> extracLinks(String url, LinkFilter filter) {
 Set<String> links = new HashSet<String>();
try {
 Parser parser = new Parser(url);
 parser.setEncoding("gb2312");
// 过滤 <frame >标签的 filter,用来提取 frame 标签里的 src 属性
 NodeFilter frameFilter = new NodeFilter() {
 public boolean accept(Node node) {
 if (node.getText().startsWith("frame src=")) {
 return true;
} else {
 return false;
 }
};
// OrFilter 来设置过滤 <a> 标签和 <frame> 标签
 OrFilter linkFilter = new OrFilter(new NodeClassFilter( LinkTag.class), frameFilter);
// 得到所有经过过滤的标签 NodeList list = parser.extractAllNodesThatMatch(linkFilter);
 for (int i = 0; i < list.size(); i++) {
 Node tag = list.elementAt(i);
 if (tag instanceof LinkTag)
// <a> 标签
{
LinkTag link = (LinkTag) tag;
 String linkUrl = link.getLink();
// URL
 if (filter.accept(linkUrl)) links.add(linkUrl);
 } else
// <frame> 标签
{
// 提取 frame 里 src 属性的链接,如 <framesrc="test.html"/>
 String frame = tag.getText();
 int start = frame.indexOf("src=");
frame = frame.substring(start);
 int end = frame.indexOf(" ");
 if (end == -1)
end = frame.indexOf(">");
 String frameUrl = frame.substring(5, end - 1);
 if (filter.accept(frameUrl))
    links.add(frameUrl);
   }
 }
 } catch (ParserException e) {
 e.printStackTrace();
 } return links;
}
}

(5)现在使用MyCrawler类将爬虫爬起来:

public class MyCrawler {
 /** * 使用种子初始化 URL 队列
* @return * @param seeds 种子URL */
 private void initCrawlerWithSeeds(String[] seeds) {
 for(int i=0;i<seeds.length;i++)
 LinkQueue.addUnvisitedUrl(seeds[i]);
 }

/** * 抓取过程 * @return * @param seeds */
 public void crawling(String[] seeds) {
 //定义过滤器,提取以http://www.lietu.com开头的链接
 LinkFilter filter = new LinkFilter(){
public boolean accept(String url) {
 if(url.startsWith("http://www.lietu.com"))
   return true;
 else
  return false;
   }
}

 //初始化 URL 队列
 initCrawlerWithSeeds(seeds);
 //循环条件:待抓取的链接不空且抓取的网页不多于1000
 while(!LinkQueue.unVisitedUrlsEmpty() &&LinkQueue.getVisitedUrlNum()<=1000) {
//队头URL出队列
 String visitUrl=(String)LinkQueue.unVisitedUrlDeQueue();
 if(visitUrl==null) continue;
 DownLoadFile downLoader=new DownLoadFile();
//下载网页 downLoader.downloadFile(visitUrl);
 //该 URL 放入已访问的 URL 中 LinkQueue.addVisitedUrl(visitUrl);
//提取出下载网页中的
URL Set<String> links=HtmlParserTool.extracLinks(visitUrl,filter);
//新的未访问的 URL 入队
 for(String link:links) {
 LinkQueue.addUnvisitedUrl(link);
      }
   }
 }
 //main 方法入口
 public static void main(String[]args) {
 MyCrawler crawler = new MyCrawler();
 crawler.crawling(new String[]{"http://www.lietu.com"});
 }

  

时间: 2024-10-21 23:34:26

网络爬虫——基于JAVA的宽度优先遍历互联网结点的相关文章

《自己动手写网络爬虫》读书笔记——宽度优先爬虫和带偏好的爬虫

前面只是获取了单个网页内容,在实际中,则使用爬虫程序遍历互联网,把网络中相关的网页全部抓取过来,这也体现了爬虫程序“爬”的概念. 互联网可以看成一个超级大的“图',而每个网页则可以看作是一个”节点“.页面中的链接可以看成是图的”有向边“.因此,可以通过图的遍历的方式对互联网这个”图“进行访问.图的遍历分为宽度优先和深度优先,但深度优先可能会在深度上过于”深”的遍历或者陷入“黑洞”,大多数爬虫都不采用这种方式.此外,在爬取的时候,有时候并不会完全按照宽度优先遍历的方式,而是给待遍历的网页赋予一定的

宽度优先遍历爬虫的python实现

爬虫 宽度优先遍历 python 网上很著名的一本爬虫教程<自己手动写网络爬虫>,该书所有源码是用java编写的, 其中提到了宽度优先遍历算法,闲来无事我把他用python实现了一遍.代码量少了将近一半,呵呵. 宽度优先算法介绍 参考:http://book.51cto.com/art/201012/236668.htm 整个的宽度优先爬虫过程就是从一系列的种子节点开始,把这些网页中的"子节点"(也就是超链接)提取出来,放入队列中依次进行抓取.被处理过的链接需要放 入一张表

根据中序和前序序列来构造二叉树,以宽度优先遍历输出

/** * 重建二叉树 :根据给定的前序和中序遍历的结果,构造出树的结构 */ package binaryBree; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; /** * @author Amory.Wang * Question : 重建二叉树 * test example : 普通二叉树 , 特殊二叉树(只有右结点或者只有左结点) ,特殊输入测试 : root=null/输入

网络爬虫(java)

   陆陆续续做了有一个月,期间因为各种技术问题被多次暂停,最关键的一次主要是因为存储容器使用的普通二叉树,在节点权重相同的情况下导致树高增高,在进行遍历的时候效率大大降低,甚至在使用递归的时候导致栈内存溢出.后来取消递归遍历算法,把普通的二叉排序树升级为平衡二叉树这才解决这些问题.着这个过程中把栈.队列.链表.HashMap.HashTable各种数据结构都重新学习了一遍,使用红黑二叉树实现的TreeMap暂时还没有看,后期需要把TreeMap的实现源码学习一下. 为了把项目做成可扩展性的,方

BFS - leetcode [宽度优先遍历]

127. Word Ladder int size = q.size(); for(int k = 0; k < size; k++){//for 次数 找到一个erase一个

实测可用的宽度优先爬虫的实现

参考文献:自己动手写网络爬虫,罗刚,王振东著(我感觉这本书对我还是蛮有用的,爬虫大杂烩啊) 前面写了一篇利用HttpClient来获取单个网页的灌水文,现在希望在此基础之上可以通过一个种子网页能够爬更多的相关网页. 由于互联网的页面上都是相互链接的,可以看成一个超级大的图,每个页面都可以看成是一个节点,而页面中的链接可以看成是图的有向边. 因此能够通过遍历的方式对互联网这个超级大的图进行访问. 突然就把很具体的问题用数据结构抽象的方法给表述出来的了,果然还是抽象牛叉. 图的遍历常可以分为宽度优先

详解java如何使用HttpClient 和 HtmlParser 实现简易网络爬虫

开发环境的搭建,在工程的 Build Path 中导入下载的Commons-httpClient3.1.Jar,htmllexer.jar 以及 htmlparser.jar 文件.图 1. 开发环境搭建 HttpClient 基本类库使用 HttpClinet 提供了几个类来支持 HTTP 访问.下面我们通过一些示例代码来熟悉和说明这些类的功能和使用. HttpClient 提供的 HTTP 的访问主要是通过 GetMethod 类和 PostMethod 类来实现的,他们分别对应了 HTTP

java爬虫学习日记2-宽度优先爬虫代码实现

爬虫两种方式--宽度优先和带偏好爬虫 先复习下上次学了什么: URL和URI的结构组成 根据指定网址爬取网站内容(get方式和post方式) 上一日记中学到了抓取单个页面内容的方法,但实际项目中则需要爬虫遍历互联网,把互联网中相关的页面都抓取回来.那么爬虫是怎样遍历互联网,把页面抓取下来的呢?首先互联网可以开成是一个"图",每个页面可以看作一个节点,链接可以看作是"有向边".因此能够通过图的方式对互联网这超级大"图"进行遍历.图的遍历通常可分为宽

网络爬虫技术

1.爬虫技术概述 网络爬虫(Web crawler),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本,它们被广泛用于互联网搜索引擎或其他类似网站,可以自动采集所有其能够访问到的页面内容,以获取或更新这些网站的内容和检索方式.从功能上来讲,爬虫一般分为数据采集,处理,储存三个部分.传统爬虫从一个或若干初始网页的URL开始,获得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件.聚焦爬虫的工作流程较为复杂,需要根据一定的网页分析算法过