教你如何编写简单的网络爬虫_java

一、网络爬虫的基本知识

网络爬虫通过遍历互联网络,把网络中的相关网页全部抓取过来,这体现了爬的概念。爬虫如何遍历网络呢,互联网可以看做是一张大图,每个页面看做其中的一个节点,页面的连接看做是有向边。图的遍历方式分为宽度遍历和深度遍历,但是深度遍历可能会在深度上过深的遍历或者陷入黑洞。所以,大多数爬虫不采用这种形式。另一方面,爬虫在按照宽度优先遍历的方式时候,会给待遍历的网页赋予一定优先级,这种叫做带偏好的遍历。

实际的爬虫是从一系列的种子链接开始。种子链接是起始节点,种子页面的超链接指向的页面是子节点(中间节点),对于非html文档,如excel等,不能从中提取超链接,看做图的终端节点。整个遍历过程中维护一张visited表,记录哪些节点(链接)已经处理过了,跳过不作处理。

使用宽度优先搜索策略,主要原因有:

a、重要的网页一般离种子比较近,例如我们打开的新闻网站时候,往往是最热门的新闻,随着深入冲浪,网页的重要性越来越低。

b、万维网实际深度最多达17层,但到达某个网页总存在一条很短路径,而宽度优先遍历可以最快的速度找到这个网页

c、宽度优先有利于多爬虫合作抓取。

二、网络爬虫的简单实现

1、定义已访问队列,待访问队列和爬取得URL的哈希表,包括出队列,入队列,判断队列是否空等操作

复制代码 代码如下:

package webspider;
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.Queue;

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

 // 获得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.poll();
 }

 // 保证每个 url 只被访问一次
 public static void addUnvisitedUrl(String url) {
  if (url != null && !url.trim().equals("") && !visitedUrl.contains(url)
    && !unVisitedUrl.contains(url))
   unVisitedUrl.add(url);
 }

 // 获得已经访问的URL数目
 public static int getVisitedUrlNum() {
  return visitedUrl.size();
 }

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

}

2、定义DownLoadFile类,根据得到的url,爬取网页内容,下载到本地保存。此处需要引用commons-httpclient.jar,commons-codec.jar,commons-logging.jar。

复制代码 代码如下:

package webspider;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;

public class DownLoadFile {
 /**
  * 根据 url 和网页类型生成需要保存的网页的文件名 去除掉 url 中非文件名字符
  */
 public String getFileNameByUrl(String url, String contentType) {
  // remove 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().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.执行 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 = "f:\\spider\\"
     + 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;
 }
}

3、定义HtmlParserTool类,用来获得网页中的超链接(包括a标签,frame中的src等等),即为了得到子节点的URL。需要引入htmlparser.jar

复制代码 代码如下:

package webspider;

import java.util.HashSet;
import java.util.Set;
import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.filters.OrFilter;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;

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("utf-8");
   // 过滤 <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 属性的链接如 <frame src="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;
 }
}

4、编写测试类MyCrawler,用来测试爬取效果

复制代码 代码如下:

package webspider;

import java.util.Set;

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.baidu.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.baidu.com" });
 }

}

至此,可以看到f:\spider文件夹下面已经出现了很多html文件,都是关于百度的,以“www.baidu.com”为开头。

时间: 2024-10-20 07:27:34

教你如何编写简单的网络爬虫_java的相关文章

Android编写简单的网络爬虫

一.网络爬虫的基本知识 网络爬虫通过遍历互联网络,把网络中的相关网页全部抓取过来,这体现了爬的概念.爬虫如何遍历网络呢,互联网可以看做是一张大图,每个页面看做其中的一个节点,页面的连接看做是有向边.图的遍历方式分为宽度遍历和深度遍历,但是深度遍历可能会在深度上过深的遍历或者陷入黑洞.所以,大多数爬虫不采用这种形式.另一方面,爬虫在按照宽度优先遍历的方式时候,会给待遍历的网页赋予一定优先级,这种叫做带偏好的遍历. 实际的爬虫是从一系列的种子链接开始.种子链接是起始节点,种子页面的超链接指向的页面是

《用Python写网络爬虫》——1.4 编写第一个网络爬虫

1.4 编写第一个网络爬虫 为了抓取网站,我们首先需要下载包含有感兴趣数据的网页,该过程一般被称为爬取(crawling).爬取一个网站有很多种方法,而选用哪种方法更加合适,则取决于目标网站的结构.本章中,首先会探讨如何安全地下载网页,然后会介绍如下3种爬取网站的常见方法: 爬取网站地图: 遍历每个网页的数据库ID: 跟踪网页链接. 1.4.1 下载网页 要想爬取网页,我们首先需要将其下载下来.下面的示例脚本使用Python的urllib2模块下载URL. import urllib2 def

使用Python的Twisted框架编写简单的网络客户端_python

Protocol  和服务器一样,也是通过该类来实现.先看一个简短的例程: from twisted.internet.protocol import Protocol from sys import stdout class Echo(Protocol): def dataReceived(self, data): stdout.write(data) 在本程序中,只是简单的将获得的数据输出到标准输出中来显示,还有很多其他的事件没有作出任何响应,下面 有一个回应其他事件的例子: from tw

java编写简单的ATM存取系统_java

新手练手必备~ 密码账户为: 先创建账户类: 复制代码 代码如下: package cn.Atm; /** * @author 偶my耶 */ import java.io.*; import com.project.project; public class Account {   private String number=null;   private String name=null;   private String password=null;   private double mo

《用Python写网络爬虫》——导读

前 言 互联网包含了迄今为止最有用的数据集,并且大部分可以免费公开访问.但是,这些数据难以复用.它们被嵌入在网站的结构和样式当中,需要抽取出来才能使用.从网页中抽取数据的过程又被称为网络爬虫.随着越来越多的信息被发布到网络上,网络爬虫也变得越来越有用. 目 录 第1章 网络爬虫简介 1.1 网络爬虫何时有用1.2 网络爬虫是否合法1.3 背景调研 1.3.1 检查robots.txt 1.3.2 检查网站地图 1.3.3 估算网站大小 1.3.4 识别网站所用技术 1.3.5 寻找网站所有者1.

斯诺登借“网络爬虫”技术获取大量机密文件

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 新华网华盛顿2月9日电(记者孙浩 穆东)美国<纽约时报>9日刊文报道,正在调查"棱镜门"事件的美国情报官员透露,前防务承包商雇员爱德华·斯诺登只凭借比较简单的"网络爬虫"技术就获取了大量机密文件,由此曝光国家安全局对内安保存在一定漏洞. <纽约时报>援引了多名不愿公开身份情报官员

一个使用 asyncio 协程的网络爬虫(一)

介绍 经典的计算机科学强调高效的算法,尽可能快地完成计算.但是很多网络程序的时间并不是消耗在计算上,而是在等待许多慢速的连接或者低频事件的发生.这些程序暴露出一个新的挑战:如何高效的等待大量网络事件.一个现代的解决方案是异步 I/O. 这一章我们将实现一个简单的网络爬虫.这个爬虫只是一个原型式的异步应用,因为它等待许多响应而只做少量的计算.一次爬的网页越多,它就能越快的完成任务.如果它为每个动态的请求启动一个线程的话,随着并发请求数量的增加,它会在耗尽套接字之前,耗尽内存或者线程相关的资源.使用

用Python编写网络爬虫(六):一个简单的百度贴吧的小爬虫

# -*- coding: utf-8 -*- #--------------------------------------- # 程序:百度贴吧爬虫 # 版本:0.1 # 作者:why # 日期:2013-05-14 # 语言:Python 2.7 # 操作:输入带分页的地址,去掉最后面的数字,设置一下起始页数和终点页数. # 功能:下载对应页码内的所有页面并存储为html文件. #--------------------------------------- import string,

[Python学习] 简单网络爬虫抓取博客文章及思想介绍

        前面一直强调Python运用到网络爬虫方面非常有效,这篇文章也是结合学习的Python视频知识及我研究生数据挖掘方向的知识.从而简单介绍下Python是如何爬去网络数据的,文章知识非常简单,但是也分享给大家,就当简单入门吧!同时只分享知识,希望大家不要去做破坏网络的知识或侵犯别人的原创型文章.主要包括:         1.介绍爬取CSDN自己博客文章的简单思想及过程         2.实现Python源码爬取新浪韩寒博客的316篇文章 一.爬虫的简单思想      最近看刘兵