什么是线程,进程
进程:每个进程都有自己独立的内存空间,不同进程之间的内存空间不共享。 密集CPU任务,需要充分使用多核CPU资源(服务器,大量的并行计算)时,用多进程。 进程之间的通信有操作系统传递,导致通讯效率低,切换开销大。
线程:一个进程可以有多个线程,所有线程共享进程的内存空间,通讯效率高,切换开销小。 共享意味着竞争,导致数据不安全,为了保护内存空间的数据安全,引入"互斥锁"。 一个线程在访问内存空间的时候,其他线程不允许访问,必须等待之前的线程访问结束,才能使用这个内存空间。
谈了线程和进程,不得不说线程锁(一种安全有序的让多个线程访问内存空间的机制)
来源:百度百科
曾经我看过一篇文章,对于爬虫有这么一个比喻,爬虫就是去果园摘水果,但是一个人多摘不了这么多,这不叫上了隔壁老,咱们一块去,其实就是就是一个线程,人太多了,不如分两个队伍,其实就是两个进程,可是两个队伍摘水果,万一他们都摘了,重复进入别人的领地,所以需要线程锁来隔开两个进程的地盘
线程典型例子
定义一个生产者,再定义一个消费者
import?threading import?time import?random MONEY?=?0 gLock?=?threading.Lock() def?Procuder(): ????while?True: ????????global?MONEY ????????random_money?=?random.randint(10,100) ????????#?开锁 ????????gLock.acquire() ????????MONEY?+=?random_money ????????gLock.release() ????????print?('生产者%s-生产了%d'?%?(threading.current_thread,random_money)) ????????time.sleep(0.5) def?Customer(): ????while?True: ????????global?MONEY ????????random_money?=?random.randint(10,100) ????????if?MONEY?>?random_money: ????????????print?('消费者%s-消费了:%d'?%?(threading.current_thread,random_money)) ????????????gLock.acquire() ????????????MONEY?-=?random_money ????????????gLock.release() ????????else: ????????????print?('需要消费的钱为:%d,余额为:%d,'?%?(random_money,MONEY)) ????????time.sleep(0.5) def?p_c_test(): ????#?执行3个线程,来当作生产者 ????for?x?in?range(3): ????????th?=?threading.Thread(target=Procuder) ????????th.start() ????#?执行3个线程,来当作消费者 ????for?x?in?range(3): ????????th?=?threading.Thread(target=Customer) ????????th.start() if?__name__?==?"__main__": ????p_c_test()
从下图看出代码运行是不断生产,不断消费。
实战训练
这次爬取的是HdhCmsTestdoutula测试数据的斗图
http://HdhCmsTestdoutula测试数据/photo/list/
导入对应的模块
import?requests from?lxml?import?etree from?queue?import?Queue import?threading import?os #?用于保存图片的下载方式 from?urllib?import?request
这次采用类的继承方式来开多线程和队列的方式
#?定义生产者来生成表情的url class?Producer(threading.Thread): ????headers?=?{ ????????'User-Agent':?'Mozilla/5.0?(Windows?NT?10.0;?Win64;?x64)?AppleWebKit/537.36?(KHTML,?like?Gecko)' ??????????????????????'?Chrome/69.0.3497.100?Safari/537.36', ????} ????def?__init__(self,?page_queue,?img_queue,?*args,?**kwargs): ????????#?继承threading.Thread的__init__方法 ????????super(Producer,?self).__init__(*args,?**kwargs) ????????self.page_queue?=?page_queue ????????self.img_queue?=?img_queue ????????if??not?os.path.exists(r'D:\images\imgs'): ????????????os.makedirs(r'D:\images\imgs')
剩余代码
分析xpath
def?run(self): ????while?True: ?????????if?self.page_queue.empty(): ????????????break ?????????url?=?self.page_queue.get() ?????????self.parse_page(def?parse_page(self,?url): def?parse_page(self,?url): ????response?=?requests.get(url,?headers=self.headers) ????text?=?response.text ????html?=?etree.HTML(text) ????contents?=?html.xpath("//a[@class='col-xs-6?col-sm-3']") ????for?content?in?contents: ????????title?=?content.xpath(".//p[@style='display:?none']/text()")[0] ????????href?=?content.xpath(".//img/@data-original")[0] ????????suffix?=?os.path.splitext(href)[1] ????????filename?=?title?+?suffix ????????self.img_queue.put((href,?filename))
如果队列有东西就下载,没用就break
#?定义消费者 class?Consumer(threading.Thread): ????def?__init__(self,?page_queue,?img_queue,?*args,?**kwargs): ????????super(Consumer,?self).__init__(*args,?**kwargs) ????????self.page_queue?=?page_queue ????????self.img_queue?=?img_queue ????def?run(self): ????????while?True: ????????????if?self.img_queue.empty()?and?self.page_queue.empty(): ????????????????break ????????????img_url,?filename?=?self.img_queue.get() ????????????request.urlretrieve(img_url,?'D:\images\imgs\{}'.format(filename)) ????????????print(filename?+?'----下载完成')
最后开线程,爬呀爬
page_queue?=?Queue(100) img_queue?=?Queue(1000) for?p?in?range(1,?101): ????url?=?'http://HdhCmsTestdoutula测试数据/photo/list/?page={}'.format(p) ????page_queue.put(url) for?x?in?range(5): ????producer?=?Producer(page_queue,?img_queue) ????producer.start() for?x?in?range(5): ????consumer?=?Consumer(page_queue,?img_queue) ????consumer.start()
不到10秒中,就2000张,哇哇,这速度
本节代码已上传,在公众号回复【斗图】
■ Over?■
最后,祝有所学习,有所成长
回复【 1024 】获取学习资料
转发,好看支持一下,感谢
你的转发,就是对我最大的支持
查看更多关于爬虫篇|开多线程,咱们一起来斗图(九)的详细内容...