[CP_12] Python多线程爬虫应用实践(社招职位名称获取
2019-04-07 本文已影响0人
Fighting_001
目录结构
一、多线程爬虫的使用
1. 多线程实现
2. 自定义创建线程类
3. Python队列
二、多线程爬虫应用案例
案例:爬取指定页码的招聘职位名称
一、多线程爬虫的使用
1. 多线程实现
<关联> [Pt_04] Python进程|多进程|线程|多线程|线程锁
multiThread-001.py
import threading
import time
# 定义执行线程的方法
def run(name):
print(name,"线程执行")
time.sleep(3)
# 创建2个线程;线程执行run方法
t1=threading.Thread(target=run,args=("t001",))
t2=threading.Thread(target=run,args=("t002",))
# 启动线程
t1.start()
t2.start()
# 等待子线程(t1,t2)执行完毕,再执行主线程(print输出的内容)
t1.join()
t2.join()
print("执行完毕!")
2. 自定义创建线程类
使用threading模块创建线程,创建一个新的子类继承父类threading.Thread,实例化后即可调用start()方法,该方法调用了线程的run()方法来启动新线程
threadClass.py
import threading
import time
# 创建一个线程锁
lock=threading.Lock()
# 创建线程类(继承threading.Thread)
class myThread(threading.Thread):
def __init__(self,name):
threading.Thread.__init__(self)
self.name=name
def run(self):
lock.acquire() # 设置锁
print(self.name,"线程开始执行")
print("线程执行中---1")
time.sleep(1)
print("线程执行中---2")
time.sleep(1)
print("线程执行中---3")
time.sleep(1)
print("线程执行中---4")
time.sleep(1)
print("线程执行中---5")
time.sleep(1)
print(self.name,"线程结束")
lock.release() # 释放锁
# 创建线程
t1=myThread("t001")
t2=myThread("t002")
t3=myThread("t003")
# 开启线程
t1.start()
t2.start()
t3.start()
# 等待子线程执行完毕,再执行主线程
t1.join()
t2.join()
t3.join()
print("执行完毕!")
执行结果:
3. Python队列
队列(Queue):保障Python标准库中的线程安全的实现,提供了一个适用于多线程的"先进先出"(First-In-First-Out,FIFO)的数据结构。对于排队的一序列任务,通过队列进行维护管理,保障先来先处理,处理完成一个再处理另一个,从而有序的执行线程而不错乱
queueTask.py
import queue
# 创建队列(maxsize限定队列存放的数据个数上限)
q=queue.Queue(maxsize=10)
# 向队列中放入值or对象
for i in range(1,11):
q.put(i)
# 队列不为空时,循环取出所有值
while not q.empty():
print(q.get())
执行结果:
二、多线程爬虫应用案例
案例:爬取指定页码的招聘职位名称
分析:
2个线程,其中一个爬取列表的网页,将爬取的数据放入到队列中;另一个线程从刚才队列中拿到html页面进行解析数据,获取每一个职位的标题
页码链接:
https://hr.tencent.com/position.php?keywords=&lid=0&start=0#a
https://hr.tencent.com/position.php?keywords=&lid=0&start=10#a
https://hr.tencent.com/position.php?keywords=&lid=0&start=20#a
==> start=(页码-1)*10
列表页-职位标题:
代码实现:
multiThread-002.py
import threading
import queue
import requests
import time
from lxml import etree
# 采集网页的线程:爬取列表所在的网页,放入队列
class Thread1(threading.Thread):
def __init__(self,threadName,pageQueue,dataQueue):
threading.Thread.__init__(self)
self.threadName=threadName # 线程名
self.pageQueue=pageQueue # 页码队列
self.dataQueue=dataQueue # 数据队列
self.header={"User-Agent":"Mozilla/5.0 (Windows NT 6.1; rv:65.0) Gecko/20100101 Firefox/65.0"}
def run(self):
print("启动线程"+self.threadName)
while not flag1:
try:
page=self.pageQueue.get()
url="https://hr.tencent.com/position.php?"+"start="+str((page-1)*10)+"#a"
# 获取列表信息转换为文本
resp=requests.get(url,headers=self.header).text
time.sleep(1) # 拿到数据和放入数据的过渡时间
self.dataQueue.put(resp) # 将数据放入dataQueue队列
except Exception as e:
pass
print("结束线程"+self.threadName)
# 解析网页的线程:从队列中获取网页进行解析,存储到本地
class Thread2(threading.Thread):
def __init__(self,threadName,dataQueue,filename):
threading.Thread.__init__(self)
self.threadName=threadName
self.dataQueue=dataQueue
self.filename=filename
def run(self):
print("启动线程"+self.threadName)
while not flag2:
try:
dataQ=self.dataQueue.get()
html=etree.HTML(dataQ)
datalist=html.xpath('//td/a[@target="_blank"]')
for i in datalist:
data=i.text
self.filename.write(data+"\n")
except Exception as e:
pass
print("结束线程"+self.threadName)
flag1=False # 判断页码队列是否为空
flag2=False # 判断数据队列是否为空
def main():
# 页码队列
pageQueue=queue.Queue(10)
for i in range(1,11):
pageQueue.put(i)
# 采集线程获取的数据放入队列
dataQueue=queue.Queue()
# 爬取数据的结果保存到本地的路径
filename=open(r"D:\CI_Env\Python_Test\file\004.txt","a")
# 启动线程
t1=Thread1("t001",pageQueue,dataQueue)
t1.start()
t2=Thread2("t002",dataQueue,filename)
t2.start()
# pageQueue为空时,结束采集线程[run()方法中的while循环]
while not pageQueue.empty():
pass
global flag1 # 定义为全局变量
flag1=True
# dataQueue为空时,结束解析线程
while not dataQueue.empty():
pass
global flag2
flag2=True
# 等待子线程执行完毕,再执行主线程
t1.join()
t2.join()
filename.close()
print("执行完毕!")
if __name__ == '__main__':
main()
执行结果: