源码网商城,靠谱的源码在线交易网站 我的订单 购物车 帮助

源码网商城

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

  • 时间:2021-09-12 10:17 编辑: 来源: 阅读:
  • 扫一扫,手机访问
摘要:教你如何编写简单的网络爬虫
[b]一、网络爬虫的基本知识[/b] 网络爬虫通过遍历互联网络,把网络中的相关网页全部抓取过来,这体现了爬的概念。爬虫如何遍历网络呢,互联网可以看做是一张大图,每个页面看做其中的一个节点,页面的连接看做是有向边。图的遍历方式分为宽度遍历和深度遍历,但是深度遍历可能会在深度上过深的遍历或者陷入黑洞。所以,大多数爬虫不采用这种形式。另一方面,爬虫在按照宽度优先遍历的方式时候,会给待遍历的网页赋予一定优先级,这种叫做带偏好的遍历。 实际的爬虫是从一系列的种子链接开始。种子链接是起始节点,种子页面的超链接指向的页面是子节点(中间节点),对于非html文档,如excel等,不能从中提取超链接,看做图的终端节点。整个遍历过程中维护一张visited表,记录哪些节点(链接)已经处理过了,跳过不作处理。 [b]使用宽度优先搜索策略,主要原因有:[/b] [b]a、[/b]重要的网页一般离种子比较近,例如我们打开的新闻网站时候,往往是最热门的新闻,随着深入冲浪,网页的重要性越来越低。 [b]b、[/b]万维网实际深度最多达17层,但到达某个网页总存在一条很短路径,而宽度优先遍历可以最快的速度找到这个网页 [b]c、[/b]宽度优先有利于多爬虫合作抓取。 [b]二、网络爬虫的简单实现 [/b] [b]1、[/b]定义已访问队列,待访问队列和爬取得URL的哈希表,包括出队列,入队列,判断队列是否空等操作
[u]复制代码[/u] 代码如下:
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();  } }
[b]2、[/b]定义DownLoadFile类,根据得到的url,爬取网页内容,下载到本地保存。此处需要引用commons-httpclient.jar,commons-codec.jar,commons-logging.jar。
[u]复制代码[/u] 代码如下:
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;  } }
[b]3、[/b]定义HtmlParserTool类,用来获得网页中的超链接(包括a标签,frame中的src等等),即为了得到子节点的URL。需要引入htmlparser.jar
[u]复制代码[/u] 代码如下:
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;  } }
[b]4、[/b]编写测试类MyCrawler,用来测试爬取效果
[u]复制代码[/u] 代码如下:
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”为开头。
  • 全部评论(0)
联系客服
客服电话:
400-000-3129
微信版

扫一扫进微信版
返回顶部