当前位置: 首页 > news >正文

用C跑爬虫

爬虫自指定的URL地址开始下载网络资源,直到该地址和所有子地址的指定资源都下载完毕为止。

下面开始逐步分析爬虫的实现。

  1. 待下载集合与已下载集合

为了保存需要下载的URL,同时防止重复下载,我们需要分别用了两个集合来存放将要下载的URL和已经下载的URL。

因为在保存URL的同时需要保存与URL相关的一些其他信息,如深度,所以这里我采用了Dictionary来存放这些URL。

具体类型是Dictionary<string, int> 其中string是Url字符串,int是该Url相对于基URL的深度。

每次开始时都检查未下载的集合,如果已经为空,说明已经下载完毕;如果还有URL,那么就取出第一个URL加入到已下载的集合中,并且下载这个URL的资源。

  1. HTTP请求和响应

C#已经有封装好的HTTP请求和响应的类HttpWebRequest和HttpWebResponse,所以实现起来方便不少。

为了提高下载的效率,我们可以用多个请求并发的方式同时下载多个URL的资源,一种简单的做法是采用异步请求的方法。

控制并发的数量可以用如下方法实现

private void DispatchWork()
{if (_stop) //判断是否中止下载{return;}for (int i = 0; i < _reqCount; i++){if (!_reqsBusy[i]) //判断此编号的工作实例是否空闲{RequestResource(i); //让此工作实例请求资源}}
}

由于没有显式开新线程,所以用一个工作实例来表示一个逻辑工作线程

private bool[] _reqsBusy = null; //每个元素代表一个工作实例是否正在工作
private int _reqCount = 4; //工作实例的数量

每次一个工作实例完成工作,相应的_reqsBusy就设为false,并调用DispatchWork,那么DispatchWork就能给空闲的实例分配新任务了。

接下来是发送请求 每次一个工作实例完成工作,相应的_reqsBusy就设为false,并调用DispatchWork,那么DispatchWork就能给空闲的实例分配新任务了。

接下来是发送请求

private void RequestResource(int index){int depth;string url = "";try{lock (_locker){if (_urlsUnload.Count <= 0) //判断是否还有未下载的URL{_workingSignals.FinishWorking(index); //设置工作实例的状态为Finishedreturn;}_reqsBusy[index] = true;_workingSignals.StartWorking(index); //设置工作状态为Workingdepth = _urlsUnload.First().Value; //取出第一个未下载的URLurl = _urlsUnload.First().Key;_urlsLoaded.Add(url, depth); //把该URL加入到已下载里_urlsUnload.Remove(url); //把该URL从未下载中移除}HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);req.Method = _method; //请求方法req.Accept = _accept; //接受的内容req.UserAgent = _userAgent; //用户代理RequestState rs = new RequestState(req, url, depth, index); //回调方法的参数var result = req.BeginGetResponse(new AsyncCallback(ReceivedResource), rs); //异步请求ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, //注册超时处理方法TimeoutCallback, rs, _maxTime, true);}catch (WebException we){MessageBox.Show("RequestResource " + we.Message + url + we.Status);}}
private void RequestResource(int index){int depth;string url = "";try{lock (_locker){if (_urlsUnload.Count <= 0) //判断是否还有未下载的URL{_workingSignals.FinishWorking(index); //设置工作实例的状态为Finishedreturn;}_reqsBusy[index] = true;_workingSignals.StartWorking(index); //设置工作状态为Workingdepth = _urlsUnload.First().Value; //取出第一个未下载的URLurl = _urlsUnload.First().Key;_urlsLoaded.Add(url, depth); //把该URL加入到已下载里_urlsUnload.Remove(url); //把该URL从未下载中移除}HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);req.Method = _method; //请求方法req.Accept = _accept; //接受的内容req.UserAgent = _userAgent; //用户代理RequestState rs = new RequestState(req, url, depth, index); //回调方法的参数var result = req.BeginGetResponse(new AsyncCallback(ReceivedResource), rs); //异步请求ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, //注册超时处理方法TimeoutCallback, rs, _maxTime, true);}catch (WebException we){MessageBox.Show("RequestResource " + we.Message + url + we.Status);}}

第7行为了保证多个任务并发时的同步,加上了互斥锁。_locker是一个Object类型的成员变量。

第9行判断未下载集合是否为空,如果为空就把当前工作实例状态设为Finished;如果非空则设为Working并取出一个URL开始下载。当所有工作实例都为Finished的时候,说明下载已经完成。由于每次下载完一个URL后都调用DispatchWork,所以可能激活其他的Finished工作实例重新开始工作。

第26行的请求的额外信息在异步请求的回调方法作为参数传入,之后还会提到。

第27行开始异步请求,这里需要传入一个回调方法作为响应请求时的处理,同时传入回调方法的参数。

第28行给该异步请求注册一个超时处理方法TimeoutCallback,最大等待时间是_maxTime,且只处理一次超时,并传入请求的额外信息作为回调方法的参数。

RequestState的定义是

class RequestState
{private const int BUFFER_SIZE = 131072; //接收数据包的空间大小private byte[] _data = new byte[BUFFER_SIZE]; //接收数据包的bufferprivate StringBuilder _sb = new StringBuilder(); //存放所有接收到的字符public HttpWebRequest Req { get; private set; } //请求public string Url { get; private set; } //请求的URLpublic int Depth { get; private set; } //此次请求的相对深度public int Index { get; private set; } //工作实例的编号public Stream ResStream { get; set; } //接收数据流public StringBuilder Html{get{return _sb;}}public byte[] Data{get{return _data;}}public int BufferSize{get{return BUFFER_SIZE;}}public RequestState(HttpWebRequest req, string url, int depth, int index){Req = req;Url = url;Depth = depth;Index = index;}
}

相关文章:

用C跑爬虫

爬虫自指定的URL地址开始下载网络资源&#xff0c;直到该地址和所有子地址的指定资源都下载完毕为止。 下面开始逐步分析爬虫的实现。 待下载集合与已下载集合 为了保存需要下载的URL&#xff0c;同时防止重复下载&#xff0c;我们需要分别用了两个集合来存放将要下载的URL和…...

【C语言】你真的了解结构体吗

引言✨我们知道C语言中存在着整形(int、short...)&#xff0c;字符型(char)&#xff0c;浮点型(float、double)等等内置类型&#xff0c;但是有时候&#xff0c;这些内置类型并不能解决我们的需求&#xff0c;因为我们无法用这些单一的内置类型来描述一些复杂的对象&#xff0c…...

血氧仪是如何得出血氧饱和度值的?

目录 一、血氧饱和度概念 二、血氧饱和度监测意义 三、血氧饱和度的监测方式 四、容积脉搏波计算血氧饱和度原理 五、容积脉搏波波形的测量电路方案 1&#xff09;光源和光电探测器的集成测量模块&#xff1a;SFH7050—反射式 2&#xff09;模拟前端 六、市面上血氧仪类型…...

Java全栈知识(3)接口和抽象类

1、抽象类 抽象类就是由abstract修饰的类,其中没有只声明没有实现的方法就是抽象方法&#xff0c;抽象类中可以有0个或者多个抽象方法。 1.1、抽象类的语法 抽象类不能被final修饰 因为抽象类是一种类似于工程中未完成的中间件。需要有子类进行继承完善其功能&#xff0c;所…...

JavaScript == === Object.is()

文章目录JavaScript & & Object.is() 相等运算符 全等运算符Object.is() 值比较JavaScript & & Object.is() 相等运算符 相等运算符&#xff0c;会先进行类型转换&#xff0c;将2个操作数转为相同的类型&#xff0c;再比较2个值。 console.log("10&…...

GPT4论文翻译 by GPT4 and Human

GPT-4技术报告解读 文章目录GPT-4技术报告解读前言&#xff1a;摘要1 引言2 技术报告的范围和局限性3 可预测的扩展性3.1 损失预测3.2 人类评估能力的扩展4 能力评估4.1 视觉输入 !!!5 限制6 风险与缓解&#xff1a;7 结论前言&#xff1a; 这篇报告内容太多了&#xff01;&am…...

inode和软硬链接

文章目录&#xff1a;一、理解文件系统1.1 什么是inode1.2 磁盘了解1.2.1磁盘的硬件结构1.2.2 磁盘的分区1.2.3 EXT2文件系统二、软硬链接2.1 软链接2.2 硬链接一、理解文件系统 1.1 什么是inode inodes 是文件系统中存储文件元数据的数据结构。每个文件或目录都有一个唯一的 …...

简单分析Linux内核基础篇——initcall

写过Linux驱动的人都知道module_init宏&#xff0c;因为它声明了一个驱动的入口函数。 除了module_init宏&#xff0c;你会发现在Linux内核中有许多的驱动并没有使用module_init宏来声明入口函数&#xff0c;而是看到了许多诸如以下的声明&#xff1a; static int __init qco…...

硬件速攻-AT24CXX存储器

AT24C02是什么&#xff1f; AT24CXX是存储芯片&#xff0c;驱动方式为IIC协议 实物图&#xff1f; 引脚介绍&#xff1f; A0 地址设置角 可连接高电平或低电平 A1 地址设置角 可连接高电平或低电平 A2 地址设置角 可连接高电平或低电平 1010是设备前四位固定地址 &#xf…...

C# tuple元组详解

概念 本质就是个数据结构&#xff0c;它是将多个数据元素分组成一个轻型数据结构。 如何声明元组变量&#xff08;针对.net framework 4.7 和 .net core 2.0) 不带字段名称元组 ## t1就是个变量 它的类型是元组类型 ## 左侧括号定义的是参数列表 等于号右侧就是个t1赋值 #…...

1、Linux初级——linux命令

下载镜像&#xff1a;http://cn.ubuntu.com/dowload 一、基本命令 1、alias&#xff08;给命令取别名&#xff09; 例如&#xff1a;alias clls -la&#xff08;只是临时的&#xff09; 2、配置文件$ vim ~/.bashrc $ vim ~/.bashrc // 使用vim打开配置文件 (1)在配置文件…...

ChatGPT助力校招----面试问题分享(四)

1 ChatGPT每日一题&#xff1a;电阻如何选型 问题&#xff1a;电阻如何选型 ChatGPT&#xff1a;电阻的选型通常需要考虑以下几个方面&#xff1a; 额定功率&#xff1a;电阻的额定功率是指电阻能够承受的最大功率。在选型时&#xff0c;需要根据电路中所需要的功率确定所选…...

【设计模式】创建型设计模式

文章目录1. 基础①如何学习设计模式② 类模型③ 类关系2. 设计原则3. 模板方法① 定义②背景③ 要点④ 本质⑤ 结构图⑥ 样例代码4. 观察者模式① 定义②背景③ 要点④ 本质⑤ 结构图⑥ 样例代码5. 策略模式① 定义②背景③ 要点④ 本质⑤ 结构图⑥ 样例代码1. 基础 ①如何学习…...

Linux 信号(signal):信号的理解

目录一、理解信号1.信号是什么2.信号的种类二、简单理解信号的生命周期一、理解信号 1.信号是什么 Linux中的信号其实和日常生活中的信号还是挺像的&#xff0c;LInux中的信号是一种事件通知机制&#xff0c;通知进程发生了某个事件。进程接收到信号后&#xff0c;就会中断当前…...

Vulnhub项目:Web Machine(N7)

靶机地址&#xff1a;Web Machine(N7)渗透过程&#xff1a;kali ip&#xff1a;192.168.56.104&#xff0c;靶机ip&#xff0c;使用arp-scan进行查看靶机地址&#xff1a;192.168.56.128收集靶机开放端口&#xff1a;nmap -sS -sV -T5 -A 192.168.56.128开放了80端口&#xff0…...

Qt基础之三十三:海量网络数据实时显示

开发中我们可能会遇到接收的网络数据来不及显示的问题。最基础的做法是限制UI中加载的数据行数,这样一来可以防止内存一直涨,二来数据刷新非常快,加载再多也来不及看。此时UI能看到数据当前处理到什么阶段就行,实时性更加重要,要做数据分析的话还得查看日志文件。 这里给出…...

linux console快捷键

Ctrl C&#xff1a;终止当前正在运行的程序。Ctrl D&#xff1a;关闭当前终端会话。Ctrl Z&#xff1a;将当前程序放入后台运行。Ctrl L&#xff1a;清除当前屏幕并重新显示命令提示符。Ctrl R&#xff1a;在历史命令中进行逆向搜索。Ctrl A&#xff1a;将光标移动到行首…...

弗洛伊德龟兔赛跑算法(弗洛伊德判圈算法)

弗洛伊德( 罗伯特・弗洛伊德)判圈算法(Floyd Cycle Detection Algorithm)&#xff0c;又称龟兔赛跑算法(Tortoise and Hare Algorithm)&#xff0c;是一个可以在有限状态机、迭代函数或者链表上判断是否存在环&#xff0c;以及判断环的起点与长度的算法。昨晚刷到一个视频&…...

nodejs篇 express(1)

文章目录前言express介绍安装RESTful接口规范express的简单使用一个最简单的服务器&#xff0c;仅仅只需要几行代码便可以实现。restful规范的五种接口类型请求信息req的获取响应信息res的设置中间件的使用自定义中间件解决跨域nodejs相关其它内容前言 express作为nodejs必学的…...

Java实习生------Redis常见面试题汇总(AOF持久化、RDB快照、分布式锁、缓存一致性)⭐⭐⭐

“年轻人&#xff0c;就要勇敢追梦”&#x1f339; 参考资料&#xff1a;图解redis 目录 谈谈你对AOF持久化的理解&#xff1f; redis的三种写回策略是什么&#xff1f; 谈谈你对AOF重写机制的理解&#xff1f;AOF重写机制的具体过程&#xff1f; 谈谈你对RDB快照的理解&a…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

短视频矩阵系统文案创作功能开发实践,定制化开发

在短视频行业迅猛发展的当下&#xff0c;企业和个人创作者为了扩大影响力、提升传播效果&#xff0c;纷纷采用短视频矩阵运营策略&#xff0c;同时管理多个平台、多个账号的内容发布。然而&#xff0c;频繁的文案创作需求让运营者疲于应对&#xff0c;如何高效产出高质量文案成…...

快刀集(1): 一刀斩断视频片头广告

一刀流&#xff1a;用一个简单脚本&#xff0c;秒杀视频片头广告&#xff0c;还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农&#xff0c;平时写代码之余看看电影、补补片&#xff0c;是再正常不过的事。 电影嘛&#xff0c;要沉浸&#xff0c;…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践

作者&#xff1a;吴岐诗&#xff0c;杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言&#xff1a;融合数据湖与数仓的创新之路 在数字金融时代&#xff0c;数据已成为金融机构的核心竞争力。杭银消费金…...

day36-多路IO复用

一、基本概念 &#xff08;服务器多客户端模型&#xff09; 定义&#xff1a;单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用&#xff1a;应用程序通常需要处理来自多条事件流中的事件&#xff0c;比如我现在用的电脑&#xff0c;需要同时处理键盘鼠标…...