96SEO 2026-02-20 08:34 0
照旧来回顾上一关的知识点上一关我们学习如何将爬虫的结果发送邮件和定时执行爬虫。

我们要用到的模块是smtplib和email前者负责连接服务器、登录、发送和退出的流程。
后者负责填输邮件的标题与正文。
#引入smtplib、MIMEText和Headermailhostsmtp.qq.com
#把qq邮箱的服务器地址赋值到变量mailhost上地址应为字符串格式
#实例化一个smtplib模块里的SMTP类的对象这样就可以调用SMTP对象的方法和属性了
#连接服务器第一个参数是服务器地址第二个参数是SMTP端口号。
#以上皆为登录邮箱。
receiverinput(请输入收件人的邮箱)
#获取收件人的邮箱。
contentinput(请输入邮件正文)
#实例化一个MIMEText邮件对象该对象需要写进三个参数分别是邮件正文文本格式和编码
#在等号的右边是实例化了一个Header邮件头对象该对象需要写入两个参数分别是邮件主题和编码然后赋值给等号左边的变量message[Subject]。
#以上为填写主题和正文。
try:qqmail.sendmail(account,
#以上为发送邮件和退出邮箱。
对于定时我们选取了schedule模块它的用法非常简洁官方文档里是这样讲述
working...schedule.every(10).minutes.do(job)
schedule.every().day.at(10:30).do(job)
schedule.every().monday.do(job)
schedule.every().wednesday.at(13:15).do(job)#部署每周三的1315执行函数的任务while
True:schedule.run_pending()time.sleep(1)
#15-17都是检查部署的情况如果任务准备就绪就开始执行任务。
我们已经做过不少爬虫项目不过我们爬取的数据都不算太大如果我们想要爬取的是成千上万条的数据那么就会遇到一个问题因为程序是一行一行依次执行的缘故要等待很久我们才能拿到想要的数据。
既然一个爬虫爬取大量数据要爬很久那我们能不能让多个爬虫一起爬取
这样无疑能提高爬取的效率就像一个人干不完的活儿组个团队一起干活一下被干完了。
这是一个很好的思路——让多个爬虫帮我们干活。
但具体怎么用Python实现这事呢
相信你肯定会这么做把三部电影都点击下载。
看哪一部先下载好了就先看哪一部让还没有下载完的电影持续保持下载状态。
如果用计算机里的概念来解释这件事的话在一个任务未完成时就可以执行其他多个任务彼此不受影响在看第一部下载好的电影时其他电影继续保持下载状态彼此之间不受影响叫异步。
有异步的概念那应该也有同步的概念吧是的同步就是一个任务结束才能启动下一个类比你看完一部电影才能去看下一部电影。
显然异步执行任务会比同步更加节省时间因为它能减少不必要的等待。
如果你需要对时间做优化异步是一个很值得考虑的方案。
如果我们把同步与异步的概念迁移到网络爬虫的场景中那我们之前学的爬虫方式都是同步的。
爬虫每发起一个请求都要等服务器返回响应后才会执行下一步。
而很多时候由于网络不稳定加上服务器自身也需要响应的时间导致爬虫会浪费大量时间在等待上。
这也是爬取大量数据时爬虫的速度会比较慢的原因。
那我们是不是可以采取异步的爬虫方式让多个爬虫在执行任务时保持相对独立彼此不受干扰这样不就可以免去等待时间显然这样爬虫的效率和速度都会提高。
怎样才能实现异步的爬虫方式提高爬虫的效率呢要回答这个问题的话得了解一点点计算机的历史小知识。
我们知道每台计算机都靠着CPU中央处理器干活。
在过去单核CPU的计算机在处理多任务时会出现一个问题每个任务都要抢占CPU执行完了一个任务才开启下一个任务。
CPU毕竟只有一个这会让计算机处理的效率很低。
为了解决这样的问题一种非抢占式的异步技术被创造了出来这种方式叫多协程在此多是多个的意思。
它的原理是一个任务在执行过程中如果遇到等待就先去执行其他的任务当等待结束再回来继续之前的那个任务。
在计算机的世界这种任务来回切换得非常快速看上去就像多个任务在被同时执行一样。
这就好比当你要做一桌饭菜你可以在等电饭煲蒸饭的时候去炒菜。
而不是等饭做好再去炒菜。
你还是那个你但工作时间就这样被缩短了。
多协程能够缩短工作时间的原理也是如此。
所以要实现异步的爬虫方式的话需要用到多协程。
在它的帮助下我们能实现前面提到的“让多个爬虫替我们干活”。
所以接下来我会带你了解gevent的用法和实操一个多协程案例爬取8个网站包括百度、新浪、搜狐、腾讯、网易、爱奇艺、天猫、凤凰。
我们先用之前同步的爬虫方式爬取这8个网站然后等下再和gevent异步爬取做一个对比。
requests.get(url)#用requests.get()函数爬取网站print(url,r.status_code)#打印网址和抓取请求的状态码end
1.7253923416137695程序运行后你会看到同步的爬虫方式是依次爬取网站并等待服务器响应状态码为200表示正常响应后才爬取下一个网站。
比如第一个先爬取了百度的网址等服务器响应后再去爬取新浪的网址以此类推直至全部爬取完毕。
为了让你能更直观地看到爬虫完成任务所需的时间我导入了time模块记录了程序开始和结束的时间最后打印出来的就是爬虫爬取这8个网站所花费的时间。
requests.get(url)print(url,time.time()-start,r.status_code)tasks_list
gevent.spawn(crawler,url)tasks_list.append(task)
0.7928953170776367程序运行后打印出了网址、每个请求运行的时间、状态码和爬取8个网站最终所用时间。
通过每个请求运行的时间我们能知道爬虫用了异步的方式抓取了8个网站因为每个请求完成的时间并不是按着顺序来的。
比如在我测试运行这个代码的时候最先爬取到的网站是搜狐接着是凤凰并不是百度和新浪。
且每个请求完成时间之间的间隔都非常短你可以看作这些请求几乎是“同时”发起的。
通过对比同步和异步爬取最终所花的时间用多协程异步的爬取方式确实比同步的爬虫方式速度更快。
其实我们案例爬取的数据量还比较小不能直接体现出更大的速度差异。
如果爬的是大量的数据运用多协程会有更显著的速度优势。
比如我做了一个测试把爬取8个网站变成爬取80个网站用同步的爬取方式大概需要花17.3秒但用多协程异步爬取只需大概4.5秒整个爬取效率提升了280%。
提醒导入gevent库前得先安装它。
如果你想要在自己本地电脑操作的话就需要在本地上安装。
安装方法window电脑在终端输入命令pip
#monkey.patch_all()能把程序变成协作式运行就是可以帮助程序实现异步。
requests.get(url)#用requests.get()函数爬取网站。
print(url,time.time()-start,r.status_code)#打印网址、请求运行时间、状态码。
tasks_list
gevent.spawn(crawler,url)#用gevent.spawn()函数创建任务。
tasks_list.append(task)#往任务列表添加任务。
上面代码涉及到gevent的语法有些可能你还看不懂不要慌我跟你一个个具体解释一遍。
第1、3行代码从gevent库里导入了monkey模块这个模块能将程序转换成可异步的程序。
monkey.patch_all()它的作用其实就像你的电脑有时会弹出“是否要用补丁修补漏洞或更新”一样。
它能给程序打上补丁让程序变成是异步模式而不是同步模式。
它也叫“猴子补丁”。
我们要在导入其他库和模块前先把monkey模块导入进来并运行monkey.patch_all()。
这样才能先给程序打上补丁。
你也可以理解成这是一个规范的写法。
第5行代码我们导入了gevent库来帮我们实现多协程导入了time模块来帮我们记录爬取所需时间导入了requests模块帮我们实现爬取8个网站。
第21、23、25行代码我们定义了一个crawler函数只要调用这个函数它就会执行【用requests.get()爬取网站】和【打印网址、请求运行时间、状态码】这两个任务。
第33行代码因为gevent只能处理gevent的任务对象不能直接调用普通函数所以需要借助gevent.spawn()来创建任务对象。
这里需要注意一点gevent.spawn()的参数需为要调用的函数名及该函数的参数。
比如gevent.spawn(crawler,url)就是创建一个执行crawler函数的任务参数为crawler函数名和它自身的参数url。
第35行代码用append函数把任务添加到tasks_list的任务列表里。
第37行代码调用gevent库里的joinall方法能启动执行所有的任务。
gevent.joinall(tasks_list)就是执行tasks_list这个任务列表里的所有任务开始爬取。
到这里用gevent实操抓取8个网站我们已经完成gevent的基础语法我们也大致了解。
用我们刚刚学的gevent语法我们可以用gevent.spawn()创建1000个爬取任务再用gevent.joinall()执行这1000个任务。
但这种方法会有问题执行1000个任务就是一下子发起1000次请求这样子的恶意请求会拖垮网站的服务器。
既然这种直接创建1000个任务的方式不可取那我们能不能只创建成5个任务但每个任务爬取200个网站
假设我们有1000个任务那创建5个任务每个任务爬取200个网站的代码可以写成如下的样子此代码仅做展示并不可运行
requests.get(url)print(url,time.time()-start,r.status_code)tasks_list
gevent.spawn(crawler,url_list[i*200:(i1)*200])#用gevent.spawn()函数创建5个任务。
tasks_list.append(task)#往任务列表添加任务。
gevent.joinall(tasks_list)
print(end-start)遗憾地告诉你这么做也还是会有问题的。
就算我们用gevent.spawn()创建了5个分别执行爬取200个网站的任务这5个任务之间是异步执行的但是每个任务爬取200个网站内部是同步的。
这意味着如果有一个任务在执行的过程中它要爬取的一个网站一直在等待响应哪怕其他任务都完成了200个网站的爬取它也还是不能完成200个网站的爬取。
这时我们可以从实际生活的案例中得到启发。
想想银行是怎么在一天内办理1000个客户的业务的。
银行会开设办理业务的多个窗口让客户取号排队由银行的叫号系统分配客户到不同的窗口去办理业务。
在gevent库中也有一个模块可以实现这种功能——queue模块。
当我们用多协程来爬虫需要创建大量任务时我们可以借助queue模块。
queue翻译成中文是队列的意思。
我们可以用queue模块来存储任务让任务都变成一条整齐的队列就像银行窗口的排号做法。
因为queue其实是一种有序的数据结构可以用来存取数据。
这样协程就可以从队列里把任务提取出来执行直到队列空了任务也就处理完了。
就像银行窗口的工作人员会根据排号系统里的排号处理客人的业务如果已经没有新的排号就意味着客户的业务都已办理完毕。
接下来我们来实操看看可以怎么用queue模块和协程配合依旧以抓取8个网站为例。
url_list:work.put_nowait(url)def
requests.get(url)print(url,work.qsize(),r.status_code)tasks_list
gevent.spawn(crawler)tasks_list.append(task)
0.9640278816223145网址后面的数字指的是队列里还剩的任务数比如第一个网址后面的数字6就是此时队列里还剩6个抓取其他网址的任务。
#monkey.patch_all()能把程序变成协作式运行就是可以帮助程序实现异步。
#从gevent库里导入queue模块因为gevent库里就带有queue所以我们用【from
Queue】就能把queue模块导入。
其他模块和代码我们在讲解gevent时已经讲解过了相信你能懂。
#遍历url_listwork.put_nowait(url)#用put_nowait()函数可以把网址都放进队列里。
用Queue()能创建queue对象相当于创建了一个不限任何存储数量的空队列。
如果我们往Queue()中传入参数比如Queue(10)则表示这个队列只能存储10个任务。
创建了queue对象后我们就能调用这个对象的put_nowait方法把我们的每个网址都存储进我们刚刚建立好的空队列里。
work.put_nowait(url)这行代码就是把遍历的8个网站都存储进队列里。
第3部分是定义爬取函数和如何从队列里提取出刚刚存储进去的网址。
work.empty():#当队列不是空的时候就执行下面的程序。
url
work.get_nowait()#用get_nowait()函数可以把队列里的网址都取出。
r
requests.get(url)#用requests.get()函数抓取网址。
print(url,work.qsize(),r.status_code)#打印网址、队列长度、抓取请求的状态码。
这里定义的crawler函数多了三个你可能看不懂的代码
当然queue对象的方法还不止这几种比如有判断队列是否为空的empty方法对应也有判断队列是否为满的full方法。
你是不是觉得queue对象这么多方法一下子记不住其实这些不需要你死记硬背的附上一张queue对象的方法表你只需要在用到的时候查查表就好。
代码的前3部分我们讲解完了。
如果你能明白队列怎么创建、数据怎么存储进队列以及怎么从队列里提取出的数据就说明queue模块的重点内容你都掌握了。
接在第3部分代码的后面就是让爬虫用多协程执行任务爬取队列里的8个网站的代码重点看有注释的代码。
requests.get(url)print(url,work.qsize(),r.status_code)tasks_list
gevent.spawn(crawler)#用gevent.spawn()函数创建执行crawler()函数的任务。
tasks_list.append(task)#往任务列表添加任务。
#用gevent.joinall方法执行任务列表里的所有任务就是让爬虫开始爬取网站。
我们创建了两只可以异步爬取的爬虫。
它们会从队列里取走网址执行爬取任务。
一旦一个网址被一只爬虫取走另一只爬虫就取不到了另一只爬虫就会取走下一个网址。
直至所有网址都被取走队列为空时爬虫就停止工作。
#monkey.patch_all()能把程序变成协作式运行就是可以帮助程序实现异步。
#遍历url_listwork.put_nowait(url)#用put_nowait()函数可以把网址都放进队列里。
def
work.empty():#当队列不是空的时候就执行下面的程序。
url
work.get_nowait()#用get_nowait()函数可以把队列里的网址都取出。
r
requests.get(url)#用requests.get()函数抓取网址。
print(url,work.qsize(),r.status_code)#打印网址、队列长度、抓取请求的状态码。
tasks_list
gevent.spawn(crawler)#用gevent.spawn()函数创建执行crawler()函数的任务。
tasks_list.append(task)#往任务列表添加任务。
#用gevent.joinall方法执行任务列表里的所有任务就是让爬虫开始爬取网站。
print(end-start)请你动手把上面的代码敲一遍不动手实践的爬虫学习都是耍流氓。
动手总会有收获的。
恭喜你这一关的核心知识实现多协程的gevent库和Queue模块你都学完了
同样是要做饭菜我们已经知道比先做饭再做菜更好的方式是等待做饭的过程中去做菜。
但其实还有更快的方案让一个人负责做饭一个人负责做菜。
继续说我们的计算机历史小知识在后来我们的CPU从单核终于进化到了多核每个核都能够独立运作。
计算机开始能够真正意义上同时执行多个任务术语叫并行执行而不是在多个任务之间来回切换术语叫并发执行。
比如你现在打开浏览器看着爬虫课程的同时可以打开音乐播放器听歌还可以打开Excel。
对于多核CPU而言这些任务就都是同时运行的。
时至今日我们电脑一般都会是多核CPU。
多协程其实只占用了CPU的一个核运行没有充分利用到其他核。
利用CPU的多个核同时执行任务的技术我们把它叫做“多进程”。
所以真正大型的爬虫程序不会单单只靠多协程来提升爬取速度的。
比如百度搜索引擎可以说是超大型的爬虫程序它除了靠多协程一定还会靠多进程甚至是分布式爬虫。
多进程爬虫和分布式爬虫相对来说就比较复杂一些了我这里不会多讲。
需要进阶学习的同学可以自己研究去学。
多协程是一种非抢占式的异步方式。
使用多协程的话就能让多个爬取任务用异步的方式交替执行。
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback