Python爬取网页中的进程、线程、协程时间对比

前言

  • 今天接到老师一个任务:让我数下某一个A类会议上从2000年到2021年每年份接受了多少篇文章(老师可能是要向这个A类期刊“进攻”了),但是我粗略估计了一下,每年大概有上百篇,难道让我数20年(*******)
  • 近期正在学习python爬虫,可以抓取页面上的源代码,分析页面上的内容,简单的写了下
  • 接下来的代码比较简单,俗话说外行看热闹,内行看笑话,不知道下面这些能不能被称为爬虫,但是为了好听我接下来都叫爬虫,各位看官莫取笑
  • 代码如下:
"""
1、导入模块
2、request 获取该期刊网页源代码
3、正则表达式 从内容里得到每年会议网址的网址
4、再进入每年会议的页面爬取网页回应
5、用正则表达式获得该年期刊论文篇数(由于该期刊每年分为多个部分,所以会对很对对应年份的篇数)
"""
# 使用基础的网页回应获取模块
import urllib.request
# 正则表达式模块
import re
# 时间模块 用来计算运行时间
import time

# 输出送进来的地址中的论文篇数
def get_url_context(url):
    url_context = urllib.request.urlopen(url)
    data = url_context.read()
    file_url = re.findall(r"\"name\">(.*?)</span>", data.decode())
    num = len(file_url) - 4
    print("[%s]年:%d 篇" % (url[-9:-5], num))
    pass


def main():
    # 计算开始时间
    start = time.perf_counter()
    # 总网页地址
    url = 'https://dblp.uni-trier.de/db/conf/uss/index.html'
    recv = urllib.request.urlopen(url)
    re_url = recv.read()
    # 得到一个每年地址列表
    file_url = re.findall(r"<a class=\"toc-link\" href=\"(.*?)\">", re_url.decode())
    # 把每年的地址送入函数
    for i in file_url:
        get_url_context(i)

    # 计算结束时间并输出运行时间
    end = time.perf_counter()
    print(end - start)


if __name__ == '__main__':
    main()

输出如下:(总共89条内容,只列出了少数)
[2020]年:159 篇
[2020]年:11 篇
[2020]年:11 篇
[2020]年:29 篇
[2020]年:18 篇
[2019]年:113 篇
[2019]年:19 篇
[2019]年:7 篇
[2019]年:30 篇
[2019]年:19 篇
[2018]年:101 篇
[2018]年:11 篇

[1995]年:23 篇
[1993]年:23 篇
206.05936830000002
最后一行为运行时间:约206秒,即3分钟多点

  • 速度过慢,因为这是并发工作,即一个网页爬取完成再去爬取另外的网页,接下来我们看多进程、多线程以及多协程

进程、线程和协程

  • 进程:一个程序的执行实例就是一个进程。每一个进程提供执行程序所需的所有资源。(进程本质上是资源的集合)
  • 线程:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
  • 协程:可以理解为在一个进程中,当一个函数不需要cup时,可以让cpu去运行其他的函数,从而实现多任务。分片的实现。
区别
  • 进程资源分配的基本单位、线程CPU调度的基本单位、协程单线程执行多任务
  • 切换效率: 协程 > 线程 > 进程
  • 高效率方式: 进程 + 协程
选择问题
  • 多进程:

密集CPU任务,需要充分使用多核CPU资源(服务器,大量的并行计算)的时候,用多进程。
缺陷:多个进程之间通信成本高,切换开销大。

  • 多线程:

密集I/O任务(网络I/O,磁盘I/O,数据库I/O)使用多线程合适。
缺陷:同一个时间切片只能运行一个线程,不能做到高并行,但是可以做到高并发。

  • 协程:

当程序中存在大量不需要CPU的操作时(IO),适用于协程;

  • 注意

并行模式下,多任务是独立进行的,执行的先后顺序时无序的

使用多进程实现本文描述问题所耗费的时间

import urllib.request
import re
import multiprocessing
import time


def get_url_context(url):
    url_context = urllib.request.urlopen(url)
    data = url_context.read()
    file_url = re.findall(r"\"name\">(.*?)</span>", data.decode())
    num = len(file_url) - 4
    print("[%s]年:%d 篇" % (url[-9:-5], num))
    # print(file_url)
    return file_url
    
def main():
    start = time.perf_counter()
    url = 'https://dblp.uni-trier.de/db/conf/uss/index.html'
    recv = urllib.request.urlopen(url)
    re_url = recv.read()
    file_url = re.findall(r"<a class=\"toc-link\" href=\"(.*?)\">", re_url.decode())
    gm = []
    for i in file_url:
        gm.append(multiprocessing.Process(target=get_url_context, args=(i,)))
        gm[-1].daemon = True
        gm[-1].start()
    num = len(file_url)
    for i in range(num):
        gm[i].join()
    end = time.perf_counter()
    print(end - start)
    
if __name__ == '__main__':
    main()

输出如下:
[2019]年:7 篇
[2020]年:11 篇
[2020]年:11 篇
[2013]年:9 篇
[2010]年:8 篇
[2013]年:9 篇

[2014]年:67 篇
[2017]年:85 篇
运行时间为:19.35869611599992

使用多线程实现本文描述问题所耗费的时间

import urllib.request
import re
import threading
import time


def get_url_context(url):
    url_context = urllib.request.urlopen(url)
    data = url_context.read()
    file_url = re.findall(r"\"name\">(.*?)</span>", data.decode())
    num = len(file_url) - 4
    print("[%s]年:%d 篇" % (url[-9:-5], num))
    # print(file_url)
    return file_url

    pass


def main():
    start = time.perf_counter()
    url = 'https://dblp.uni-trier.de/db/conf/uss/index.html'
    recv = urllib.request.urlopen(url)
    re_url = recv.read()
    file_url = re.findall(r"<a class=\"toc-link\" href=\"(.*?)\">", re_url.decode())
    gm = []
    for i in file_url:
        gm.append(threading.Thread(target=get_url_context, args=(i,)))
        gm[-1].setDaemon(True)
        gm[-1].start()
    num = len(file_url)
    for i in range(num):
        gm[i].join()
    end = time.perf_counter()
    print(end - start)


if __name__ == '__main__':
    main()

输出如下
[2019]年:7 篇
[2016]年:6 篇
[2016]年:7 篇
[2018]年:10 篇
[2014]年:7 篇
[2014]年:10 篇

[2004]年:33 篇
[2020]年:159 篇
运行时间为:18.202859115000138

使用多协程实现本文描述问题所耗费的时间

import urllib.request
import re
import gevent
import time
# 打补丁,让gevent能识别所有耗时操作
from gevent import monkey
monkey.patch_all()


def get_url_context(url):
    url_context = urllib.request.urlopen(url)
    data = url_context.read()
    file_url = re.findall(r"\"name\">(.*?)</span>", data.decode())
    # print(file_url)
    num = len(file_url) - 4
    print("[%s]年:%d 篇" % (url[-9:-5], num))
    # return file_url

    pass


def main():
    start = time.perf_counter()
    url = 'https://dblp.uni-trier.de/db/conf/uss/index.html'
    recv = urllib.request.urlopen(url)
    re_url = recv.read()
    file_url = re.findall(r"<a class=\"toc-link\" href=\"(.*?)\">", re_url.decode())
    gm = []
    for i in file_url[0:22]:
        # 协程
        gm.append(gevent.spawn(get_url_context, i))

    # 让主进程等待其他所有协程完成工作再结束
    num = len(file_url)
    for i in range(0, num):
        gm[i].join()
    end = time.perf_counter()
    print(end - start)


if __name__ == '__main__':
    main()

输出如下:
[2013]年:8 篇
[2013]年:9 篇
[2011]年:11 篇
[2017]年:10 篇
[2012]年:10 篇
[2012]年:12 篇
[2014]年:11 篇

[2020]年:159 篇
[2020]年:29 篇
[2013]年:45 篇
运行时间为:19.3128608

结束

至此多进程、多线程、多协程方式进行爬取可以发现都比单任务并发行时快很多
三者之间并没有太大差别

  • 注意

爬虫建立过多进程、线程或者协程访问同意网站会造成网站对ip的察觉,从而出现以下错误:


[WinError 10060] 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败

  • 解决办法:我并不需要太多访问该网站,所以出现这种问题并没有深入了解解决办法,我认为时ip的问题,所以换一个虚拟机运行程序,因为虚拟机ip和主机ip还是有些差别的。或者等待一段时间再去运行该程序
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值