python编程

pip源

1
2
3
4
5
6
-i https://pypi.tuna.tsinghua.edu.cn/simple
阿里云 http://mirrors.aliyun.com/pypi/simple/
中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/
豆瓣(douban) http://pypi.douban.com/simple/
清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/
中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple/

进程、线程、协程

进程与线程的历史

在早期的操作系统里,计算机只有一个核心,进程执行程序的最小单位,任务调度采用时间片轮转的抢占式方式进行进程调度。每个进程都有各自的一块独立的内存,保证进程彼此间的内存地址空间的隔离。 随着计算机技术的发展,进程出现了很多弊端,一是进程的创建、撤销和切换的开销比较大,二是由于对称多处理机(对称多处理机(SymmetricalMulti-Processing)又叫SMP,是指在一个计算机上汇集了一组处理器(多CPU),各CPU之间共享内存子系统以及总线结构)的出现,可以满足多个运行单位,而多进程并行开销过大。 这个时候就引入了线程的概念。 线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元,由线程ID、程序计数器、寄存器集合 和堆栈共同组成。线程的引入减小了程序并发执行时的开销,提高了操作系统的并发性能。 线程没有自己的系统资源,只拥有在运行时必不可少的资源。但线程可以与同属与同一进程的其他线程共享进程所拥有的其他资源。

进程与线程的关系

线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。线程可与属于同一进程的其它线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和栈)。

多线程编程

基础使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# -*- coding: UTF-8 -*-
import threading
import time

class MyThread(threading.Thread):
def __init__(self,num):
threading.Thread.__init__(self)
self.num = num

def run(self):#定义每个线程要运行的函数
print("running on number:%s" %self.num)

if __name__ == '__main__':
t1 = MyThread(1)
t2 = MyThread(2)
t1.start()
t2.start()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# -*- coding: UTF-8 -*-
import threading
import time

def worker(num):
"""
thread worker function
:return:
"""
time.sleep(1)
print("The num is %d" % num)
return

for i in range(20):
t = threading.Thread(target=worker,args=(i,),name=“t.%d” % i)
t.start()

由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,CPU接着执行其他线程。为了保证数据的准确性,引入了锁的概念。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# -*- coding: UTF-8 -*-
import threading
import time

globals_num = 0

lock = threading.RLock()

def Func():
lock.acquire() # 获得锁
global globals_num
globals_num += 1
time.sleep(1)
print(globals_num)
lock.release() # 释放锁

for i in range(10):
t = threading.Thread(target=Func)
t.start()

线程锁threading.RLock和threading.Lock的区别

RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。 如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的琐。

1
2
3
4
5
6
7
8
9
10
11
12
13
import threading
lock = threading.Lock() #Lock对象
lock.acquire()
lock.acquire() #产生了死琐。
lock.release()
lock.release() 

import threading
rLock = threading.RLock() #RLock对象
rLock.acquire()
rLock.acquire() #在同一线程内,程序不会堵塞。
rLock.release()
rLock.release()

threading.Event

python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。

事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。

  • clear:将“Flag”设置为False
  • set:将“Flag”设置为True
  • Event.isSet() :判断标识位是否为Ture。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# -*- coding: UTF-8 -*-
import threading
import time

def do(event):
print('start')
event.wait()
print('execute')

event_obj = threading.Event()
for i in range(10):
t = threading.Thread(target=do, args=(event_obj,))
t.start()

event_obj.clear()
print "wait"
time.sleep(5)
event_obj.set()
print "wait OK"
爆破md5脚本

其实最后测试发现,最终还是回到了单线程,这里贴一下吧,准备换多进程写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# -*- coding: utf-8 -*-
# @Author: Marte
# @Date: 2018-10-20 19:10:06
# @Last Modified by: Marte
# @Last Modified time: 2019-05-22 13:00:59
from hashlib import md5
import time
import threading
import datetime

hash = ''
def work(cipher,weishu,start,end):
for i in xrange(start,end):
global hash
if hash != '':
break
if md5(str(i)).hexdigest()[:weishu] == cipher:
hash = i
break

if __name__=='__main__':
cipher = raw_input('md5:')
weishu = int(raw_input('len:'))
start = 1
thread_list=[]
for i in range(0,10):
c1 = threading.Thread(target=work, args=(cipher,weishu,start+i*10000000,start+(i+1)*10000000))
thread_list.append(c1)
starttime = datetime.datetime.now()
for i in thread_list:
i.start()
for i in thread_list:
i.join()
print hash
endtime = datetime.datetime.now()
print (endtime - starttime).seconds

threading.Condition

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# -*- coding: UTF-8 -*-
import threading
import time
def consumer(cond):
with cond:
print("consumer before wait")
cond.wait()
print("consumer after wait")

def producer(cond):
with cond:
print("producer before notifyAll")
cond.notifyAll()
print("producer after notifyAll")

condition = threading.Condition()
c1 = threading.Thread(name="c1", target=consumer, args=(condition,))
c2 = threading.Thread(name="c2", target=consumer, args=(condition,))

p = threading.Thread(name="p", target=producer, args=(condition,))

c1.start()
time.sleep(2)
c2.start()
time.sleep(2)
p.start()
爆破md5脚本

其实最后测试发现,最终还是回到了单线程,这里贴一下吧,准备换多进程写。感觉python的多线程好鸡肋阿

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# -*- coding: utf-8 -*-
# @Author: Marte
# @Date: 2018-10-20 19:10:06
# @Last Modified by: Marte
# @Last Modified time: 2019-05-22 13:00:59
from hashlib import md5
import time
import threading
import datetime
# processor_number = 1


# def work(cipher,weishu):
# while True:
# plain = urandom(16).encode('hex')
# if md5(plain).hexdigest()[:weishu] == cipher:
# print plain
# break
hash = ''
def work(condition,cipher,weishu,start,end):
with condition:
for i in xrange(start,end):
condition.acquire()
if md5(str(i)).hexdigest()[:weishu] == cipher:
global hash
print hash
if hash != '':
condition.wait()
condition.release()
break
hash = i
print hash
condition.wait()
break


def success(condition):
with condition:
while 1:
global hash
if hash !='':
condition.notifyAll()
break


if __name__=='__main__':
condition = threading.Condition()
cipher = raw_input('md5:')
weishu = int(raw_input('len:'))
start =1
end = 100000000
thread_list=[]
noti_list=[]
for i in range(0,1):
c1 = threading.Thread(target=work, args=(condition,cipher,weishu,start+i*10000000,start+(i+1)*10000000))
thread_list.append(c1)
for i in range(0,1):
p = threading.Thread(target=success, args=(condition,))
noti_list.append(p)
for open in zip(thread_list,noti_list):
open[0].start()
open[1].start()
for thread in thread_list:
thread.join()

Queue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# -*- coding: UTF-8 -*-
#!/usr/bin/env python
import Queue
import threading

message = Queue.Queue(10)
def producer(i):
message.put(i)
def consumer(i):
msg = message.get()
print msg
for i in range(12):
t = threading.Thread(target=producer, args=(i,))
t.start()

for i in range(10):
t = threading.Thread(target=consumer, args=(i,))
t.start()

Thread其他小方法

1
2
3
4
5
6
7
8
9
10
11
thread.sthreadarthread() : 激活线程,
thread.gethreadName() : 获取线程的名称
thread.sethreadName() : 设置线程的名称
thread.name : 获取或设置线程的名称
thread.is_alive() : 判断线程是否为激活状态
thread.isAlive() : 判断线程是否为激活状态
thread.sethreadDaemon() : 设置为后台线程或前台线程(默认:False);通过一个布尔值设置线程是否为守护线程,必须在执行sthreadarthread()方法之后才可以使用。如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止;如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止
thread.isDaemon() : 判断是否为守护线程
thread.identhread : 获取线程的标识符。线程标识符是一个非零整数,只有在调用了sthreadarthread()方法之后该属性才有效,否则它只返回None。
thread.join() : 逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
thread.run() : 线程被cpu调度后自动执行线程对象的run方法

多进程编程

基础使用

multiprocessing是python的多进程管理包

1
2
3
4
5
6
7
8
9
from multiprocessing import Process

def func(name):
print('hello', name)

if __name__ == "__main__":
p = Process(target=func,args=('zhangyanlin',))
p.start()
p.join() # 等待进程执行完毕

进程间通信

Array,Value

数据可以用Value或Array存储在一个共享内存地图里,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# -*- coding: UTF-8 -*-
#!/usr/bin/env python
from multiprocessing import Array,Value,Process

def func(a,b):
a.value = 3.333333333333333
for i in range(len(b)):
b[i] = -b[i]


if __name__ == "__main__":
num = Value('d',0.0)
arr = Array('i',range(11))
for i in arr:
print i

c = Process(target=func,args=(num,arr))
c.start()
c.join()

print(num.value)
for i in arr:
print(i)
Manager(重点)

由Manager()返回的manager提供list, dict,Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue,Value , Array类型的支持。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# -*- coding: UTF-8 -*-
#!/usr/bin/env python
from multiprocessing import Process,Manager
def f(d,l):
d["name"] = "zhangyanlin"
d["age"] = 18
d["Job"] = "pythoner"
l.reverse()

if __name__ == "__main__":
with Manager() as man:
d = man.dict()
l = man.list(range(10))
p = Process(target=f,args=(d,l))
p.start()
p.join()
print(d)
print(l)
Queue()
1
2
3
4
5
6
7
8
9
10
11
from multiprocessing import Process, Queue

def f(q):
q.put([42, None, 'hello'])

if __name__ == '__main__':
q = Queue()
p = Process(target=f, args=(q,))
p.start()
print(q.get()) #prints "[42, None, 'hello']"
p.join()
Pipe()
1
2
3
4
5
6
7
8
9
10
11
12
from multiprocessing import Process, Pipe

def f(conn):
conn.send([42, None, 'hello'])
conn.close()

if __name__ == '__main__':
parent_conn, child_conn = Pipe()
p = Process(target=f, args=(child_conn,))
p.start()
print(parent_conn.recv()) #prints "[42, None, 'hello']"
p.join()

进程锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# -*- coding: UTF-8 -*-
#!/usr/bin/env python
from multiprocessing import Process,Lock
def f(d,l):
d.acquire()
try:
print 'hello world',l
finally:
d.release()
if __name__ == "__main__":
lock = Lock()
for i in range(10):
p = Process(target=f,args=(lock,i))
p.start()

进程池

进程池内部维护一个进程序列,当使用时,去进程池中获取一个进程,如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止。在上面的程序中产生了10个进程,但是只能有5同时被放入进程池,剩下的都被暂时挂起,并不占用内存空间,等前面的五个进程执行完后,再执行剩下5个进程。

apply_async(非阻塞)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#coding: utf-8
import multiprocessing
import time

def func(msg):
print "msg:", msg
time.sleep(3)
print "end"

if __name__ == "__main__":
pool = multiprocessing.Pool(processes = 3)
for i in xrange(4):
msg = "hello %d" %(i)
pool.apply_async(func, (msg, )) #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去

print "Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~"
pool.close()
pool.join() #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
print "Sub-process(es) done."
apply(阻塞)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#coding: utf-8
import multiprocessing
import time

def func(msg):
print "msg:", msg
time.sleep(3)
print "end"

if __name__ == "__main__":
pool = multiprocessing.Pool(processes = 3)
for i in xrange(4):
msg = "hello %d" %(i)
pool.apply(func, (msg, )) #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去

print "Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~"
pool.close()
pool.join() #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
print "Sub-process(es) done."
Pool的其他小方法
1
2
3
4
5
6
7
8
9
10
11
12
进程池的方法
apply(func[, args[, kwds]]) :使用arg和kwds参数调用func函数,结果返回前会一直阻塞,由于这个原因,apply_async()更适合并发执行,另外,func函数仅被pool中的一个进程运行。
apply_async(func[, args[, kwds[, callback[, error_callback]]]]) : apply()方法的一个变体,会返回一个结果对象。如果callback被指定,那么callback可以接收一个参数然后被调用,当结果准备好回调时会调用callback,调用失败时,则用error_callback替换callback。 Callbacks应被立即完成,否则处理结果的线程会被阻塞。
close() : 阻止更多的任务提交到pool,待任务完成后,工作进程会退出。
terminate() : 不管任务是否完成,立即停止工作进程。在对pool对象进程垃圾回收的时候,会立即调用terminate()。
join() : wait工作线程的退出,在调用join()前,必须调用close() or terminate()。这样是因为被终止的进程需要被父进程调用wait(join等价与wait),否则进程会成为僵尸进程。
map(func, iterable[, chunksize])
map_async(func, iterable[, chunksize[, callback[, error_callback]]])
imap(func, iterable[, chunksize])
imap_unordered(func, iterable[, chunksize])
starmap(func, iterable[, chunksize])
starmap_async(func, iterable[, chunksize[, callback[, error_back]]])

multiprocessing的其他小方法

1
2
3
4
5
apply() 同步执行(串行)
apply_async() 异步执行(并行)
terminate() 立刻关闭进程池
join() 主进程等待所有子进程执行完毕。必须在close或terminate()之后。
close() 等待所有进程结束后,才关闭进程池。
爆破md5脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#coding: utf-8
import multiprocessing
import time
import datetime
from hashlib import md5
from multiprocessing import Value

def work(hash,cipher,weishu,start,end):
for i in xrange(start,end):
if hash.value != 0:
break
if md5(str(i)).hexdigest()[:weishu] == cipher:
hash.value = i
break

if __name__=='__main__':
cipher = raw_input('md5:')
weishu = int(raw_input('len:'))
start = 1
hash = Value('i',0)
pool_list = []
for i in range(0,10):
p = multiprocessing.Process(target=work,args=(hash,cipher,weishu,start+i*1000000,start+(i+1)*1000000))
pool_list.append(p)
for i in pool_list:
i.start()
for i in pool_list:
i.join()
print hash.value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#coding: utf-8
from multiprocessing import Manager,Pool
import time
import datetime
from hashlib import md5

def work(q,cipher,weishu,start,end):
for i in xrange(start,end):
if q.qsize()!=0:
break
if md5(str(i)).hexdigest()[:weishu] == cipher:
q.put(i)
break

if __name__=='__main__':
cipher = raw_input('md5:')
weishu = int(raw_input('len:'))
pool = Pool(processes = 3)
start = 1
q=Manager().Queue()
for i in range(0,10):
pool.apply(work,(q,cipher,weishu,start+i*1000000,start+(i+1)*1000000))
pool.close()
pool.join()
print(q.get())

协程

协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。

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

event loop是协程执行的控制点, 如果你希望执行协程, 就需要用到它们。

event loop提供了如下的特性:

  • 注册、执行、取消延时调用(异步函数)
  • 创建用于通信的client和server协议(工具)
  • 创建和别的程序通信的子进程和协议(工具)
  • 把函数调用送入线程池中

示例

python2协程

python2.x协程应用:

  • yield
  • gevent

python2.x中支持协程的模块不多,gevent算是比较常用的,这里就简单介绍一下gevent的用法。

  • gevent.spawn 启动协程,参数为函数名称,参数名称
  • gevent.joinall 停止协程
1
2
3
4
5
6
7
8
9
10
11
#coding: utf-8
#! -*- coding:utf-8 -*-
import gevent
from gevent import monkey;monkey.patch_all()
import urllib2
def get_body(i):
print "start",i
urllib2.urlopen("http://cn.bing.com")
print "end",i
tasks=[gevent.spawn(get_body,i) for i in range(3)]
gevent.joinall(tasks)

python3协程

1
2
3
4
5
6
7
8
9
10
11
12
13
import asyncio

async def cor1():
print("COR1 start")
await cor2()
print("COR1 end")

async def cor2():
print("COR2")

loop = asyncio.get_event_loop()
loop.run_until_complete(cor1())
loop.close()

协程池

pip3 install -i gevent https://pypi.mirrors.ustc.edu.cn/simple/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#! -*- coding:utf-8 -*-
from bs4 import BeautifulSoup
import requests
import gevent
from gevent import monkey, pool
monkey.patch_all()
jobs = []
p = pool.Pool(10)
def get_sleep():
print("start")
gevent.sleep(1)
print("end")
for i in range(10):
jobs.append(p.spawn(get_sleep))
gevent.joinall(jobs)

异步爬虫(待更新)

深入理解Python的yield from语法

yield && yield from
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import time
import random

def stupid_fib(n):
index = 0
a = 0
b = 1
while index < n:
sleep_cnt = yield b
print('let me think {0} secs'.format(sleep_cnt))
time.sleep(sleep_cnt)
a, b = b, a + b
index += 1
print('-'*10 + 'test yield send' + '-'*10)
N = 20
sfib = stupid_fib(N)
fib_res = next(sfib)
while True:
print(fib_res)
try:
fib_res = sfib.send(random.uniform(0, 0.5))
except StopIteration:
break
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import random
import time
def stupid_fib(n):
index = 0
a = 0
b = 1
while index < n:
sleep_cnt = yield b
print('let me think {0} secs'.format(sleep_cnt))
time.sleep(sleep_cnt)
a, b = b, a + b
index += 1
def copy_stupid_fib(n):
print('I am copy from stupid fib')
yield from stupid_fib(n)
print('Copy end')


print('-'*10 + 'test yield from and send' + '-'*10)
N = 20
csfib = copy_stupid_fib(N)
fib_res = next(csfib)
while True:
print(fib_res)
try:
fib_res = csfib.send(random.uniform(0, 0.5))
except StopIteration:
break

当你还并不是很了解yield from的具体作用时,可以理解为一下: 如果一个生成器内部需要遍历另一个生成器,并将数据返回给调用者,你需要遍历它并处理所遇到的异常;而用了 yield from 后,则可以一行代码解决这些问题。

关心协程的朋友,大部分是用其写爬虫(因为协程能很好的解决IO阻塞问题)

  • grequests (requests模块的异步化)
  • 爬虫模块+gevent(目前挺好用的)
  • aiohttp
  • asyncio内置爬虫功能 (也挺好用的)
  • asyncio 和 multiprocessing (PyCon 2018—待研究)

event_loop:事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,当满足条件发生的时候,就会调用对应的处理方法。

coroutine:中文翻译叫协程,在 Python 中常指代为协程对象类型,我们可以将协程对象注册到时间循环中,它会被事件循环调用。我们可以使用 async 关键字来定义一个方法,这个方法在调用时不会立即被执行,而是返回一个协程对象。

task:任务,它是对协程对象的进一步封装,包含了任务的各个状态。

ensure_future:代表将来执行或没有执行的任务的结果,实际上和 task 没有本质区别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import asyncio
import aiohttp
import time

start = time.time()

async def get(url):
session = aiohttp.ClientSession()
response = await session.get(url)
result = await response.text()
session.close()
return result

async def request():
url = 'http://127.0.0.1:5000'
print('Waiting for', url)
result = await get(url)
print('Get response from', url, 'Result:', result)

tasks = [asyncio.ensure_future(request()) for _ in range(5)]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

end = time.time()
print('Cost time:', end - start)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#coding: utf-8
'''
异步方式爬取当当畅销书的图书信息
'''

import time
import aiohttp
import asyncio
import pandas as pd
from bs4 import BeautifulSoup

# table表格用于储存书本信息
table = []

# 获取网页(文本信息)
async def fetch(session, url):
async with session.get(url) as response:
return await response.text(encoding='gb18030')

# 解析网页
async def parser(html):

# 利用BeautifulSoup将获取到的文本解析成HTML
soup = BeautifulSoup(html, "html.parser")
# 获取网页中的畅销书信息
book_list = soup.find('ul', class_="bang_list clearfix bang_list_mode")('li')

for book in book_list:
info = book.find_all('div')
# 获取每本畅销书的排名,名称,评论数,作者,出版社
rank = info[0].text[0:-1]
name = info[2].text
comments = info[3].text.split('条')[0]
author = info[4].text
date_and_publisher = info[5].text.split()
publisher = date_and_publisher[1] if len(date_and_publisher) >=2 else ''
table.append([rank,name,comments,author,publisher])

# 处理网页
async def download(url):
async with aiohttp.ClientSession() as session:
html = await fetch(session, url)
await parser(html)

# 全部网页
urls = ['http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent7-0-0-1-%d'%i for i in range(1,26)]

# 统计该爬虫的消耗时间
print('#' * 50)
t1 = time.time() # 开始时间

# 利用asyncio模块进行异步IO处理
loop = asyncio.get_event_loop()
tasks = [asyncio.ensure_future(download(url)) for url in urls]
tasks = asyncio.gather(*tasks)
loop.run_until_complete(tasks)

exit()
# df = pd.DataFrame(table, columns=['rank','name','comments','author','publisher'])
# df.to_csv('d://dangdang.csv',index=False,encoding='utf-8')

t2 = time.time() # 结束时间
print('使用aiohttp,总共耗时:%s' % (t2 - t1))
print('#' * 50)

Refer

python进程、线程、协程

搞定python多线程和多进程

Python协程

用Python3的async/await做异步编程

-------------本文结束感谢您的阅读-------------