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

2023-05-16

爬虫两种方式--宽度优先和带偏好爬虫

先复习下上次学了什么:

  1. URL和URI的结构组成

  2. 根据指定网址爬取网站内容(get方式和post方式)


 上一日记中学到了抓取单个页面内容的方法,但实际项目中则需要爬虫遍历互联网,把互联网中相关的页面都抓取回来。那么爬虫是怎样遍历互联网,把页面抓取下来的呢?首先互联网可以开成是一个"图",每个页面可以看作一个节点,链接可以看作是"有向边"。因此能够通过图的方式对互联网这超级大"图"进行遍历。图的遍历通常可分为宽度优先遍历和深度优先遍历这两种方式。

  1. 宽度优先遍历

    图的宽度优先遍历需要一个队列作为保存当前节点的子节点的数据结构。算法如下:

    wKioL1cdwDSz0LtqAAAefHwTMxg495.png

    1) 顶点V入队列

    2)当队列非空时继续执行,否则算法为空

    3)出队列,获得队头节点V,访问顶点V并标志V已经被访问

    4)查找顶点V的第一个邻接顶点col

    5)若V的邻接顶点col未被访问,则col进队列

    6)继续查找V的其它邻接顶点col,转到步骤5判断,若V的所有邻接顶点都被访问过,则转到步骤2


    执行过程如下:

    wKiom1cdxQ3RWs5eAAArco-EuVE642.png

     



    整个宽度优先爬虫过程就是从一系列种子节点开始,把网页中的"子节点"(超链接)提取出来,然后放入队列中一次进行抓取。被处理过的链接需要放到一张表(通常叫Visited表)中。每次新处理一个链接之前,都会判断是否存在于Visited表中。如果存在,证明该链接已处理过,跳过不再处理,否则进入处理流程。过程如下图:


    wKioL1cdz5GxWeHZAAAWu9A1mzU885.png


    1)解析出来的链接和Visited表中的链接进行比较,若Visited表不存在此链接,表示未被访问过。

    2)把链接放入TODO表中

    3)处理完毕后,再次从TODO表中取出一条链接,直接放入Visited表中

    4)针对这个链接所表示的网页,重复上述流程




    wKiom1cfB7aQSZLeAAAxiCFMhnI749.png

    接下来我们就看一下怎么用java代码实现整个流程抓取逻辑吧!



import java.util.LinkedList;

/**
*URL队列类
*/
public class Queue {
	//使用链表实现队列
	private LinkedList<Object> queue=new LinkedList<Object>();
	//入队列
	public void enQueue(Object t){
		queue.addLast(t);
	}
	//出队列
	public Object deQueue(){
		return queue.removeFirst();
	}
	
	//判断队列是否为空
	public boolean isQueueEmpty(){
		return queue.isEmpty();
	}
	//判断队列是否包含t
	public boolean contains(Object t){
		return queue.contains(t);
	}
	public boolean enpty(){
		return queue.isEmpty();
	}
}
import java.util.HashSet;
import java.util.Set;
/**
 * 记录哪些URL访问过
 * 哪些待访问
 * */
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.contains(url)){
			unVisitedUrl.enQueue(url);
		}
	}
	//获得已经访问过的url数量
	public static int getVisitedUrlNum(){
		return visitedUrl.size();
	}
	//判断未访问的url队列是否为空
	public static boolean unVisitedUrlIsEmpty(){
		return unVisitedUrl.enpty();
	}
}
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;

import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;

/**
 * 下载并存储网页信息
 * */
public class DownLoadFile {
	//生成HttpClient对象并设置参数
	private static HttpClient httpClient = new HttpClient();

	//下载url指定网页
	public String downloadFile(String url) {
		//设置HTTP连接超市时间
		httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(5000);
		//生成GetMethod对象并设置参数
		GetMethod getMethod=new GetMethod(url);
		//设置get请求超时时间
		getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 5000);
		//设置请求重试处理
		getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());
		
		String filePath=null;
		//执行Http GET请求
		try {
			int statusCode = httpClient.executeMethod(getMethod);
			//判断状态码
			if(statusCode!=HttpStatus.SC_OK){
				filePath=null;
			}
			
			//处理Http响应内容
			byte[] responseBody=getMethod.getResponseBody();//读取字节数组
			filePath="temp\\"+getFileNameByUrl(url,getMethod.getResponseHeader("Content-Type").getValue());
			saveToLocal(responseBody,filePath);
		} catch (Exception e) {
			// TODO: handle exception
		}finally{
			getMethod.releaseConnection();
		}
		return filePath;
	}

	/**
	 * 保存网页字节数组到本地文件,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 (Exception e) {
			// TODO: handle exception
		}
	}

	/**
	 * 根据url和网页累生成需要保存的网页的文件名,去除url中非文件名符号
	 * */
	private String getFileNameByUrl(String url, String contentType) {
		//移除http
		url=url.substring(7);
		//text/html类型
		if(contentType.indexOf("html")!=-1){
			url=url.replaceAll("[\\?/:*|<>\"]", "_")+".html";
			return url;
		}else{
			//如果application/pdf类型
			return url.replaceAll("[\\?/:*|<>\"]", "_")+"."
					+contentType.substring(contentType.lastIndexOf("/")+1);
		}
	}

}
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;

/**
 * 提取页面内容
 * */
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(){

				@Override
				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();
					if(filter.accept(linkUrl))
					links.add(linkUrl);
					
				}else{//frame标签
					String frame=tag.getText();
					//提取frame里面src属性的链接,如 frame src='test.html'
					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 (Exception e) {
			e.printStackTrace();
		}
		return links;
	}

}
import java.util.Set;

/**
 * 宽度爬取页面主程序
 * */
public class MyCrawler {

	/**
	 * 抓取过程
	 * */
	public void crawling(String [] seeds){
		//定义过滤器,提取以http://www.baidu.com开头的链接
		LinkFilter filter=new LinkFilter(){

			@Override
			public boolean accept(String url) {
				if(url.startsWith("https://www.baidu.com")){
					return true;
				}else{
					return false;
				}
			}
		};
		//初始化url队列
		initCrawlerWithSeeds(seeds);
		//抓取的链接不为空并且数量不多于1000
		while(!LinkQueue.unVisitedUrlIsEmpty()&&LinkQueue.getVisitedUrlNum()<=1000){
			//队列头URL出队列
			String visitUrl=(String)LinkQueue.unVisitedUrlDeQueue();
			if(visitUrl==null){
				continue;
			}
			
			DownLoadFile downLoadFile=new DownLoadFile();
			//下载网页
			downLoadFile.downloadFile(visitUrl);
			//该url放入已访问队列中
			LinkQueue.addVisitedUrl(visitUrl);
			Set<String> links=HtmlParserTool.extracLinks(visitUrl,filter);
			for(String link:links){
				LinkQueue.addUnvisitedUrl(link);
			}
		}
	}
	/**
	 * 使用种子初始化URL队列
	 * @param seeds 种子url
	 * @return
	 * */
	private void initCrawlerWithSeeds(String[] seeds) {
		for(int i=0;i<seeds.length;i++){
			LinkQueue.addUnvisitedUrl(seeds[i]);
		}
		
	}
	
	
}
/**
 * 过滤提取出来的url,使得您爬取出来的url只会与你所需要的页面相关
 * 这里例子只爬取以https://www.baidu.com/开头的内容
 * */
public interface LinkFilter {
	public boolean accept(String url);
}
public class SpiderWidth {
	public static void main(String[] args) {
		MyCrawler myCrawler=new MyCrawler();
		//网页列表
		myCrawler.crawling(new String[]{"https://www.baidu.com"});
	}
}

以上程序本人以验证过,需要验证的可以将百度换成自己的网站,有什么以为可以多留言交流


深度优先方式敬请期待下节。。。

转载于:https://blog.51cto.com/yiqiuqiuqiu/1767867

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java爬虫学习日记2-宽度优先爬虫代码实现 的相关文章

  • 使用 FreeRTOS 时注意事项总结(基础篇教程完结)

    以下转载自安富莱电子 xff1a http forum armfly com forum php FreeRTOS 的初始化流程 推荐的初始化流程如下 xff0c 本教程配套的所有例子都是采用的这种形式 xff0c 当然 xff0c 不限制
  • 使用Python实现Hadoop MapReduce程序

    为什么80 的码农都做不了架构师 xff1f gt gt gt 笔者的机器运行效果如下 xff08 输入数据是find的帮助手册 xff0c 和笔者预期一样 xff0c the是最多的 xff09 xff1a 以下是原帖 在这个实例中 xf
  • 解决vnc连接Linux出现X形

    编辑vnc配置文件 vnc xstartup如下 xff1a bin sh Uncomment the following two lines for normal desktop unset SESSION MANAGER exec et
  • 交换机 BootROM 下的升级配置

    实验十 交换机 BootROM 下的升级配置 一 实验目的 1 了解什么时候采用 BootROM 升级 xff1b 2 了解怎样使用 BootROM升级交换机 二 应用环境 当交换机的系统文件遭到破坏时 xff0c 已经无法进入正常的CLI
  • 如何利用 Visual Studio 自定义项目或工程模板

    在开发项目的时候 xff0c 由其是商业性质的大型项目时 xff0c 往往需要在每个代码文件上都加上一段关于版权 开发人员的信息 xff0c 并且名称空间上都需要带有公司的标志 这个时候 xff0c 是选择在开发的时候手动添加还是自动生成呢
  • vncserver和Ubuntu Xfce4远程桌面环境的配置,解决不显示图形界面

    vncserver和Ubuntu Xfce4远程桌面环境的配置 参考的http blog 163 com thinki cao blog static 83944875201303014531803 ubuntu用vnc连接后不显示图形界面
  • Windows平台下利用Fastcopy来做数据的定期同步

    FastCopy号称是Windows 平台上最快的文件拷贝 删除软件 xff0c 特别是文件超多 超大的情况下 为此我们在数据备份的时候选择FastCopy 但是 FastCopy如果直接来做计划任务的话会有一个问题 xff0c 因为打开的
  • 善用VS中的Code Snippet来提高开发效率

    前言 在谈谈VS中的模板中 xff0c 我介绍了如何创建项目 项模板 xff0c 这种方式可以在创建项目时省却不少重复性的工作 xff0c 从而提高开发效率 在创建好了项目和文件后 xff0c 就得开始具体的编码了 xff0c 这时又有了新
  • [git]merge和rebase的区别

    前言 我从用git就一直用rebase xff0c 但是新的公司需要用merge命令 xff0c 我不是很明白 xff0c 所以查了一些资料 xff0c 总结了下面的内容 xff0c 如果有什么不妥的地方 xff0c 还望指正 xff0c
  • 正则表达式python_Python的隐藏正则表达式宝石

    正则表达式python There are many terrible modules in the Python standard library but the Python re module is not one of them W
  • IP地址自动封与解封的shell脚本

    本脚本学习与阿铭的脚本课程 用于防止公司网站被DDos攻击时 xff0c 封禁 肉机 的IP地址 共分为以下步骤 xff1a 1 每分钟分析一次访问日志 data logs access log 2 把访问量超过100的IP给封掉 3 将封
  • 应用中抛出SELECT/UPDATE/INSERT/DELETE command denied to user 'XXX'@'XXX.XXX.XXX.XXX' for table 'xxx' 的5种原...

    实为吾之愚见 望诸君酌之 闻过则喜 xff0c 与君共勉 第一章 准备环境 创建数据测试数据 xff0c 以下测试多是基于自建mysql进行 mysql gt create database test1 Query OK 1 row aff
  • 数据标注的作用及行业现状

    在之前的内容中讲过确立一个算法模型需要使用大量标注好的数据去训练机器让机器去学习其中的特征以达到 智能 的目的 而数据标注就是帮助机器去学习去认知数据中的特征 比如我们要让机器学习认知汽车 xff0c 我们直接给机器一个汽车的图片它是无法识
  • 如何解决安卓手机显示google play服务停止运行?

    相信不少的安卓用户都遇到过这种情况 xff1a 很抱歉 xff0c google play服务 已停止运行 这到底是怎么一回事呢 xff1f 接下来就通过本文来给大家介绍一下 xff0c 我们一起往下看 xff01 其实呢 xff0c 这句
  • Failed to stop the server machine ' xxx.xxx'

    2019独角兽企业重金招聘Python工程师标准 gt gt gt arcgis for server 10 2 在部署新站点时抛出异常 xff1a Failed to create the site Failed to stop the
  • vue中使用jquery报错 $ is not defined

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 一 问题 is not defined 在使用Vs Code编写Vue应用的时候 xff0c 从页面中引入jquery后 xff0c 在 vue文件编写使用时 xff0c
  • 即将发布的 ASP.NET Core 2.2 会有哪些新玩意儿?

    今年 6 月份的时候时候 NET 团队就在 GitHub 公布了 ASP NET Core 2 2 版本的 Roadmap xff08 文末有链接 xff09 xff0c 而前两天 ASP NET Core 2 2 预览版 2 已经可以下载
  • 【three.js练习程序】随机生成100个方块

    lt DOCTYPE html gt lt html gt lt head gt lt meta charset 61 34 utf 8 34 gt lt title gt ceshi lt title gt lt script type
  • [转]你应该知道的CSS文字大小单位PX、EM、PT

    这里引用的是Jorux的 95 的中国网站需要重写CSS 的文章 xff0c 题目有点吓人 xff0c 但是确实是现在国内网页制作方面的一些缺陷 我一直也搞不清楚px与em之间的关系和特点 xff0c 看过以后确实收获很大 平时都是用px来
  • 实践这一次,彻底搞懂浏览器缓存机制

    前言 实践系列 主要是让我们通过实践去加深对一些原理的理解 实践系列 前端路由 实践系列 Babel原理 实践系列 Promises A 43 规范 有兴趣的同学可以关注 实践系列 求star求follow 如果觉得自己已经掌握浏览器缓存机

随机推荐

  • lxc lxd_如何在LXC和LXD中使用Linux容器

    lxc lxd In the good old days installing an operating system meant pulling together all the hardware components firing yo
  • (linux)Centos 7 xfsdump文件系统的备份和恢复

    XFS提供了 xfsdump 和 xfsrestore 工具协助备份XFS文件系统中的数据 xfsdump 按inode顺序备份一个XFS文件系统 centos7选择xfs格式作为默认文件系统 xff0c 而且不再使用以前的ext xff0
  • 佳能2900脱机自检_HP打印机脱机自检方法

    HP激光打印机如何在打印机面板上打印脱机自检页 背景 在打印机使用过程中经常需要查看打印机状态 xff0c 此时我们可以打印脱机 自检页来 帮助我们判断 xff0c 下面列出了 HP 激光打印机打印脱机 自检页的 方法 控制面板其他操作请参
  • loglog matlab,MATLAB 函数loglog()

    它使用x和y轴的对数刻度生成向量x和y的图 句法 loglog Y It should plot the columns of Y versus their index if Y includes real numbers If Y inc
  • k30s刷鸿蒙系统,Redmi K30S至尊版喜提MIUI 12.5稳定版

    Redmi K30S至尊版喜提MIUI 12 5稳定版 2021 05 07 13 20 05 16点赞 18收藏 104评论 进入5月 xff0c 第二批升级MIUI 12 5的机型也陆续开启推送 xff0c 在五一小长假期间包括Redm
  • html中有序列表的css样式,CSS 列表样式(ul)

    CSS 列表 CSS 列表属性作用如下 xff1a 设置不同的列表项标记为有序列表 设置不同的列表项标记为无序列表 设置列表项标记为图像 列表 在 HTML 中 xff0c 有两种类型的 HTML列表 xff1a 无序列表 列表项的标记使用
  • 网易邮箱发送失败服务器连接失败,网易邮件发送不出去MI:SFQ错误

    因为业务需要 xff0c 写了一个在线留言发送到邮箱的小程序 xff0c 前几天使得挺好 突然发送不了了 xff0c 报错事务失败 服务器响应为 5 5 0 MI STC 经过百度查询 xff0c 发现是 550 MI STC 短期内发送了
  • ajax部分验证表单数据,简单的Ajax+Servlet表单数据验证

    var XMLHttpReq 创建XMLHttpRequest对象 function createXMLHttpRequest if window XMLHttpRequest Mozilla 浏览器 XMLHttpReq 61 new X
  • 云服务器装操作系统吗,云服务器能装操作系统吗

    云服务器能装操作系统吗 内容精选 换一换 监控是保持云耀云服务器可靠性 可用性和性能的重要部分 xff0c 通过监控 xff0c 用户可以观察云耀云服务器资源 为使用户更好地掌握自己的云耀云服务器运行状态 xff0c 公有云平台提供了云监控
  • 打造性能服务器图片,详解用node-images打造简易图片服务器.pdf

    详详解解用用node images 打打造造简简易易图图片片服服务务器器 Edit 2016 5 11 修正了代码里面一些明显的错误 xff0c 并发布在 aj axj s 之中 xff0c 源码在这里 Edit 2016 5 24 加入
  • 企业应用:浅谈 “数据权限” 和 查询 API 设计

    背景 多数企业应用都需要对数据权限进行控制 xff0c 如 xff1a 某个用户只能看到某个范围的数据 xff08 数据行 xff09 某个用户只能看到某几列数据 xff08 数据列 xff09 本文以数据行级别的权限控制为范例 xff0c
  • 您必须了解的4种OpenStack Neutron网络类型

    如果您托管的OpenStack虚拟实例需要网络连接 xff0c 则必须创建一个网络 有多种类型的网络 xff0c 为了做出正确的选择 xff0c 您至少需要了解两个非常重要的网络属性 xff1a router xff1a external
  • 动态链接库*.so的编译与使用- -

    动态链接库 so的编译与使用 动态库 so在linux 下用c和c 43 43 编程时经常会碰到 xff0c 最近在网站找了几篇文章介绍动态库的编译和链接 xff0c 总算搞懂了这个之前一直不太了解得东东 xff0c 这里做个笔记 xff0
  • Linux系统VNC配置实践总结

    VNC 概述 VNC Virtual Network Computing 是 虚拟网络 计算机 的缩写 VNC 是一款优秀的 远程控制 工具软件 xff0c 由著名的 AT amp T 的欧洲研究实验室开发的 VNC 是在基于 UNIX 和
  • linux不需要杀毒软件

    linux一般不需要杀毒软件 所有在windows下的病毒在linux下都失效 xff0c 即使你下载一堆病毒 xff0c 不论多强的病毒 xff0c 对病毒狂点都没事 既然没中毒 xff0c 切换回windows当然也没事 xff0c 但
  • 使用badblocks命令检测、修复硬盘坏道

    badblocks是Linux下常用的坏道修复工具 当你觉得硬盘上可能有坏道 xff0c 或者是SMART数据显示有坏道的时候 xff0c 都可以用badblocks来检查一下 假设我们要检查的硬盘是 dev sdb sudo badblo
  • python3 如何给装饰器传递参数

    引子 之前写过一篇文章用来讲解装饰器 https www cnblogs com JiangLe p 9309330 html 那篇文章的定位是入门级的 所以也就没有讲过多的高级主题 xff0c 决定在这里讲一下如果为装饰器传递参数 目标
  • VirsualBox 安装Ubuntu上不了网

    VirsualBox 安装Ubuntu上不了网 xff0c 可以ping通 xff0c 但是浏览器上不了网 可以选择双网卡 xff0c 如下 xff1a 桥接网卡为了是上外网 xff0c NAT为了上内网 能够SSH连接 囧囧小先生一直是技
  • martian source packets(ll header)

    原文地址 xff1a http blog chinaunix net space php uid 61 346158 amp do 61 blog amp id 61 2131002 martian source packets ll he
  • java爬虫学习日记2-宽度优先爬虫代码实现

    爬虫两种方式 宽度优先和带偏好爬虫 先复习下上次学了什么 xff1a URL和URI的结构组成根据指定网址爬取网站内容 xff08 get方式和post方式 xff09 上一日记中学到了抓取单个页面内容的方法 xff0c 但实际项目中则需要