前言
- 今天接到老师一个任务:让我数下某一个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还是有些差别的。或者等待一段时间再去运行该程序