python多线程爬取_python爬虫之多线程爬取

一、什么是多进程?

像电脑上同时运行多个软件,比如在打开微信的同时,也打开了QQ与钉钉,这就是多进程。

二、什么是多线程?

一个进程中可以进行多种操作,即在QQ上既可以发送消息也可视频/语音,这就是多线程。

三、主进程/子进程

主进程下面可能会有好多子进程,即不一定一个运行的软件就是一个进程,他下面可能会有很多个子进程。

四、主线程/子线程

一个主线程下面可能会有多个子线程。

五、如何创建线程(Thread)

1、面向过程的创建方式

t = threading.Thread(target=s, name='xxx'xxx argxs=(x,y))

target:线程启动之后需要执行的函数

name:线程的名字

threading.current_thread().name:获取线程的name

args:主线程向子线程传递的参数

t.start():启动线程

t.join():让主线程等待子线程结束

#!/usr/local/bin/python3.7

importthreadingimporttime"""一个主线程,两个子线程。"""

defsend_message(x):for i in range(1,5):print('%s在发%s'%(x,threading.current_thread().name))

time.sleep(1)defvideo(x):for i in range(1,5):print('%s在%s'%(x, threading.current_thread().name))

time.sleep(1)defmain():#主线程传递到子线程的参数

x = '墨子李'

#创建发消息的线程

sthread = threading.Thread(target=send_message, name='发消息', args=(x,))#创建视频的线程

vthread = threading.Thread(target=video, name='视频', args=(x,))#启动线程

sthread.start()

vthread.start()#主线程等待子线程结束之后再结束

sthread.join()

vthread.join()print('我是主线程')if __name__ == "__main__":

main()

2、面向对象的创建方式

#!/usr/local/bin/python3.7

"""定义一个类,继承自threading.Thread"""

importthreadingimporttimeclassSendMessage(threading.Thread):def __init__(self, name, x):

super().__init__()

self.name=name

self.x=xdefrun(self):for i in range(1, self.x):print('%s在发送第%s条消息'%(self.name, i))

time.sleep(1)classVideo(threading.Thread):def __init__(self, name, x):

super().__init__()

self.name=name

self.x=xdefrun(self):for i in range(1, self.x):print('%s视频了%s分钟'%(self.name, i))

time.sleep(1)defmain():#创建线程

sthread = SendMessage('墨子李', 5)

vthread= Video('墨子李', 5)#启动线程

sthread.start()

vthread.start()#主线程等待子线程结束

sthread.join()

vthread.join()if __name__ == "__main__":

main()

3、线程同步

线程之间共享全局变量(进程之间不可以),会出现数据混乱的现象,这个时候要使用线程锁来处理这种情况。

创建锁:s = threading.Lock()

上锁:s.acquire()

释放锁:s.release()

4、队列(queue)

先进先出原则

创建队列:q = Queue(5)

给队列添加数据:q.put('xxx')

q.put('xxx', False) 如果队列满,程序直接报错

q.put('xxx', True, 3) 如果队列满,程序等待3s再报错

q.get() 获取数据,如果队列为空卡在这里等待

q.get(False) 如果队列为空,程序直接报错

q.get(True, 3) 如果队列为空,程序等待3s报错

q.empty() 判断队列是否为空

q.full() 判断队列是否已满

q.qsize() 获取队列长度

#!/usr/local/bin/python3.7

from queue importQueue#创建队列,规定队列长度为5

q = Queue(5)#添加数据

q.put('1')

q.put('2')

q.put('3')

q.put('4')

q.put('5')#判断队列是否已满,返回true

print(q.full())#获取数据

print(q.get())print(q.get())print(q.get())print(q.get())print(q.get())#队列长度 ,数据取完之后长度为0

print(q.qsize())#判断队列是否为空,此时返回True

print(q.empty())

5、多线程爬取

1、分析

两类线程:下载、解析

内容队列:下载线程往队列中put数据,解析线程从队列get数据。

url队列:下载线程从url队列get数据

写数据:上锁

2、实例

#!/usr/local/bin/python3.7

"""@File : duoxiancheng.py

@Time : 2020/06/19

@Author : Mozili"""

importrequestsimportthreadingimportqueueimporttimefrom lxml importetreeimportjson#采集线程列表

crawl_thread_list =[]#解析线程列表

parse_thread_list =[]classCrawlThread(threading.Thread):def __init__(self, name, page_queue, data_queue):

super().__init__()

self.name=name

self.page_queue=page_queue

self.data_queue=data_queue

self.headers= {'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15'}defrun(self):print('%s开始采集数据....'%self.name)#这里需要加循环让线程继续下去,不然执行一次之后就会停止

whileTrue:#获取页码队列中的数据,拼接url

url = 'http://www.ifanjian.net/jianwen-{}'

#判断队列是否为空

if notself.page_queue.empty():

url=url.format(self.page_queue.get())#print(url)

else:#print('数据已取完')

break

#发送请求

r = requests.get(url, headers=self.headers)#print('请求url----------------------------------------', r.url)

#self.fp1.write(r.text)

#将数据添加到数据队列

self.data_queue.put(r.text)#print(self.data_queue.qsize())

#print(self.data_queue.get())

print('%s结束采集数据....'%self.name)classParseThread(threading.Thread):def __init__(self, name, data_queue, lock, fp):

super().__init__()

self.name=name

self.data_queue=data_queue

self.lock=lock

self.fp=fpdefrun(self):

items=[]print('{}开始解析数据...'.format(self.name))while self.data_queue.qsize()!=0:#从队列中获取数据

content =self.data_queue.get()#解析数据

tree =etree.HTML(content)

time.sleep(1)#获取标题

title_list = tree.xpath("//ul[@class='cont-list']/li/h2/a/text()")#print(title_list)

#获取图片链接

src_list = tree.xpath("//ul[@class='cont-list']/li//p/img/@data-src")#print(src_list)

for title intitle_list:for src insrc_list:

data={'标题':title,'链接':src

}

items.append(data)#加锁

self.lock.acquire()

self.fp.write(json.dumps(data, ensure_ascii=False)+'\n')#释放锁

self.lock.release()break

#print(self.data_queue.qsize())

print('{}结束解析数据...'.format(self.name))defcreat_crawl_thread(page_queue, data_queue):

crawl_name_list= ['采集线程1', '采集线程2', '采集线程3']for name incrawl_name_list:

crawl_thread=CrawlThread(name, page_queue, data_queue)

crawl_thread_list.append(crawl_thread)defcreat_parse_thread(data_queue, lock, fp):

parse_name_list= ['解析线程1', '解析线程2', '解析线程3']for name inparse_name_list:

parse_thread=ParseThread(name, data_queue, lock, fp)

parse_thread_list.append(parse_thread)defmain():#创建页码队列

page_queue =queue.Queue()#给队列添加页码

for page in range(1, 51):

page_queue.put(page)#创建数据队列

data_queue =queue.Queue()#创建锁

lock =threading.Lock()#创建文件

fp = open('Reptile/fanjian.json', 'a', encoding='utf8')#fp1 = open('Reptile/fanjian.txt', 'a', encoding='utf8')

#创建采集数据线程

creat_crawl_thread(page_queue, data_queue)#创建解析数据线程

creat_parse_thread(data_queue, lock, fp)#启动采集数据线程

for cthread incrawl_thread_list:

cthread.start()

time.sleep(3)#启动解析数据线程

for pthread inparse_thread_list:

pthread.start()#主线程等待子线程结束

cthread.join()

pthread.join()print('主线程结束....')#关闭文件

#fp.close()

if __name__ == "__main__":

main()

你可能感兴趣的:(python多线程爬取)