进程+线程+队列爬取斗图网
- 需求:爬取斗图网数据
- 首先我们使用线程的方式,爬取前4页数据
- 准备工作
- 图片链接存在页面源代码中
- 但是,界面使用了懒加载技术,真正的url在data-original中
- 准备工作
import requests
from lxml import etree
from concurrent.futures import ThreadPoolExecutor
import time
def get_src_url():
for page in range(1, 5):
url = f'https://www.pkdoutu.com/photo/list/?page={page}'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"
}
resp = requests.get(url =url,headers = headers)
tree = etree.HTML(resp.text)
data_original_list = tree.xpath("//li[@class='list-group-item']//a/img/@data-original")
#下载图片
with ThreadPoolExecutor(10) as t:
for src_url in data_original_list:
print(src_url)
# download_img(src_url)
t.submit(download_img,src_url)
print('一张图片下载完成')
def download_img(url):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"
}
img_data = requests.get(url=url,headers=headers).content
img_name = url.split('/')[-1]
with open(f'doutu/{img_name}','wb') as f:
f.write(img_data)
if __name__ == '__main__':
start = time.time()
get_src_url()
end = time.time()
print(f'总共用时:{end - start}')
-
使用上述代码,总共花了16s时间
-
思考:
- 我们在下载图片的时候,使用了多线程,但是获取图片url还是单线程的
- 需要等待前一页数据下载完毕才会获取下一页图片的地址
- 如果,我们使用两个进程,一个专门负责获取图片地址,另外一个专门下载图片,效率是不是会加快呢?
- 但是,进程只能是不能直接通信的
- 一般来讲,进程之间想要相互通信,我们有以下几种办法
- 文件,效率慢,并发不好处理
- 网络,需要写网络传输,麻烦
- 队列--本篇使用方法,注意:不是所有队列都支持进程之间通信,python中使用multiprocess(底层就是网络传输)
- 数据库
import requests
from lxml import etree
from concurrent.futures import ThreadPoolExecutor
import time
from multiprocessing import Queue
from multiprocessing import Process
#专门获取url
def get_src_url(q):
for page in range(1, 5):
url = f'https://www.pkdoutu.com/photo/list/?page={page}'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"
}
resp = requests.get(url =url,headers = headers)
tree = etree.HTML(resp.text)
data_original_list = tree.xpath("//li[@class='list-group-item']//a/img/@data-original")
for src_url in data_original_list:
print(src_url)
# download_img(src_url)
#把拿到的url 塞入队列
q.put(src_url)
q.put("结束")
#第一个进程跑完后,塞入一个结束的标志,不然第二个进程不知道啥时候结束
#注意:不能用判断队列为空结束
#第二个进程,负责下载图片
def img_process(q): #从队列中提取url,下载图片
with ThreadPoolExecutor(10) as t:
while 1: #不确定队列中有多少个,那就一直那,直到取到结束标志
img_url = q.get() #get是一个阻塞操作
if img_url == '结束':
break
t.submit(download_img,img_url)
def download_img(url):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"
}
img_data = requests.get(url=url,headers=headers).content
img_name = url.split('/')[-1]
with open(f'doutu/{img_name}','wb') as f:
f.write(img_data)
if __name__ == '__main__':
#准备队列
start = time.time()
q = Queue() #主进程
p1 = Process(target=get_src_url,args=(q,)) #单独开辟了内存空姐
p2 = Process(target=img_process,args=(q,))
p1.start()
p2.start()
p1.join()
p2.join()
end = time.time()
print(f'总共用时:{end - start}')