评论([[comments.sum]])
2.0 多任务(进程,协程,线程)爬虫:验证码识别,返回头储存,ip代理 介绍。 异步是什么,爬虫异步的方式。线程,进程,介绍2020-12-11 4k 69
提取图片人工打码……
对于请求 登录页面等,会在本地设置 cookie 的链接。可以通过
session = requests.Session()
, 创建一个 session 对象(用法 requests 相似)。会自动对返回结果中的 返回头 进行存储,以便于下次请求使用。
服务器可能会通过记录 ip 某一段时间内的访问次数,以此拒绝访问。
可以使用代理服务器减少 单个 ip 的访问次数。突破自身 ip 的访问限制。同时 隐藏自身真实 ip。
代理 ip 相关的网站:
from Package.myrequest import MyRequests
url1 = 'https://www.baidu.com/s?wd=ip'
page_text = MyRequests.get(url1, proxies={"http": '123.55.102.9:9999'}).text
with open('iptest.html', 'w', encoding='utf-8') as fp:
fp.write(page_text)
# 返回的ip还是本机ip
并发: 当有多个线程在操作时,如果系统只有一个 cpu 核心,则他根本不可能真正同时进行一个以上线程.他只能将运行时间划分成若干个时间段 再将时间段分给各个线程执行,其他线程处于挂起状态. 这种方式称为 并发(Concurrent)
并行: 当系统有一个以上核心时,则线程的操作有可能实现并发,一个核心执行时,其他核心可以执行另一个线程. 多个线程不抢占 cpu 资源,可以同时进行.成为 并行(Parallel)
如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间。
windons 下 使用 multiprocessing 模块中 Process 类创建新进程
主进程执行完毕(子进程是分离出去的),随后子进程也会自动关闭.
couse = Process(target=func, name=进程名,args=(函数需要的参数(元组形式))
, 返回创建的子进程进程
子进程对象的调用方法:
.start()
启动进程任务.run()
执行任务,但并没有启动进程.terminate()
结束工作进程,不在处理未处理的任务。如果需要创建多个进程时,可以使用 multiprocessing 中的 Pool 类创建进程池。
初始化 pool 时可以指定一个最大进程数,当有新的请求提交到 pool 中时:
阻塞式:添加一个进程,等进程执行完毕 在添加执行另一个进程.
非阻塞式:全部添加到队列中,立刻返回.并没有等待其他进程完毕. 但是回调函数是等待一个进程完成立即调用
from multiprocessing import Pool
pool = Pool(5)
func = lambda a, b: a + b
# 阻塞式
pool.apply(func, args=('1', '2'))
# 非阻塞式
pool.apply_async(func, args=('1', '2'), callback=lambda val: val)
# 关闭进程池,不再接受新的进程
pool.close()
# 主进程阻塞等待子进程的退出。主进程执行完毕,随后子进程也会马上关闭
pool.join()
线程状态: 新建 就绪 运行 阻塞 结束
如果多个线程共同对某个数据修改,可能出现数据不同步的问题。
使用 Thread 的 lock 和 Rlock 可以实现简单的线程同步,这两个对象都有 acquire 和 release 方法 .
import threading
from time import sleep
TEMP = 0
lock = threading.Lock()
def task1():
global TEMP
lock.acquire() # 阻塞. # 获取线程锁
sleep(0.5)
TEMP += 1
lock.release() # 关闭锁, 只要不释放其他线程无法进入运行状态
thread = threading.Thread(target=task1)
thread1 = threading.Thread(target=task1)
thread.start() # 启动此线程
thread1.start()
python 内置线程池(用法类似于 内置进程池)
from multiprocessing.pool import ThreadPool
在执行函数 A 时,可以随时中断,去执行函数 B ,然后中断继续执行函数 A (可以自由切换)。 但这一过程并不是函数调用(没有调用语句),这一整个过程看似像多线程,然而协程只有一个线程执行.
greenlet 可以实现协程,不过每一次都要人为的去指向下一个该执行的协程,显得太过麻烦。
python 还有一个比 greenlet 更强大的并且能够自动切换任务的模块 gevent
gevent 每次遇到 io 操作,需要耗时等待时,会自动跳到下一个协程继续执行
import gevent
gevent.monkey.patch_all() # *1 猴子补丁
func = lambda val: val
threads = [gevent.spawn(func, i) for i in range(10)]
# grevent_obj.join() # 等待协程结束(主进程不会等待)
gevent.joinall(threads) # 可接多个参数
gevent.sleep() # 模拟一个耗时操作, time.sleep没有让gevent感知到等待
*1 :把标准库中的 thread/socket 等给替换掉.这样我们在后面使用 socket 的时候可以跟平常一样使用,无需修改任何代码,但是它变成非阻塞的了. (将阻塞(等待)的协程自动切换)
优点:可以为相关的阻塞程序单独开启线程或进程,阻塞操作就可以异步执行。
缺点:无法无限制的开启多线程或者多进程。
相关推荐:
来自系列:Python 爬虫学习笔记评论([[comments.sum]])
[[item.name]] [[item.email]] [[item.date_time]]
[[itemSon.name]] [[itemSon.email]] [[itemSon.date_time]]
回复 @[[itemSon.reply_user.name]]:
加载更多([[item.son.length-2]])...