一、添加线程
1.进程和线程
正在的运行的应用程序就是进程,进程和进程之间均运算在其专门的内存空间里面,相互独立不干扰
线程是进程执行任务的基本单元,一个进程默认有一个线程,这个线程叫主线程。
如果进程中需要主线程以外的线程(子线程),需要手动创建
2.threading模块
1)函数
threading是Python标准库中提供的专门处理多线程问题的模块。
threading.current_thread() - 获取当前线程
threading.active_count() - 获取当前进程的活跃线程个数
threading.enumerate() - 获取当前进程中所有的线程对象
2)类
Thread类 - Thread类的对象就是线程对象(程序需要多少个子线程,就需要创建多少个Thread类的对象)
# 获取当前线程
print(threading.current_thread()) # <_MainThread(MainThread, started 140736248673152)>
# 获取当前进程中活跃的线程的数量
print(threading.active_count())
# 获取当前进程中所有的线程对象
print(threading.enumerate())
3.添加线程 - 创建Thread对象
添加线程就是创建Thread的对象
1)创建线程
Thread(target=函数,args=元组) - 创建线程对象
target - 函数,需要在子线程执行的任务对应的函数
args - 元组,元组中的元素就是在子线程中调用target对应的函数传递的参数
2)启动线程
线程对象.start() - 在子线程中调用target对应的函数
def download_image(url: str):
print('=====:', threading.current_thread())
start = time.time()
name = url[url.rindex('/'):]
print(f'{name}开始下载: {datetime.now()}')
response = requests.get(url)
with open('files/'+name, 'wb') as f:
f.write(response.content)
time.sleep(2)
end = time.time()
print(f'{name}下载结束, {datetime.now()},所用时间{end-start}s')
# a.在主线程中下载三张图片
# download_image('https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1040146029,3829256909&fm=115&gp=0.jpg')
# download_image('https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1040146029,3829256909&fm=115&gp=0.jpg')
# download_image('https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2414067779,1864517677&fm=26&gp=0.jpg')
# b.在三个子线程中下载三张图片
t1 = Thread(target=download_image, args=('https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1040146029,3829256909&fm=115&gp=0.jpg',))
t2 = Thread(target=download_image, args=('https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1040146029,3829256909&fm=115&gp=0.jpg',))
t3 = Thread(target=download_image, args=('https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2414067779,1864517677&fm=26&gp=0.jpg',))
t1.start()
t2.start()
t3.start()
print('!!!!!:', threading.current_thread())
二、Thread的子类对象
class DownloadThread(Thread):
def __init__(self, movie_name):
super().__init__()
self.movie_name = movie_name
# 在run方法中实现需要在子线程中执行的任务。实现任务对应的功能需要额外的数据,不能传参,通过添加对象属性来提供数据
def run(self):
print(f'{self.movie_name}开始下载:{datetime.now()}')
print('下载中....')
print(self.movie_name[:])
time.sleep(randint(2, 5))
print(f'{self.movie_name}下载完成:{datetime.now()}')
movies = ['咒怨', 12, '八佰', '釜山行2', '活着']
ts = []
for m in movies:
t = DownloadThread(m)
t.start()
ts.append(t)
# 注意: run不能直接通过对象主动调用,否则任务都会在主线程中执行
# t.run()
for t in ts:
print(t.movie_name)
三、join
1.join
线程对象.join() - join后面的操作会在指定线程结束之后才执行
class DownloadThread(Thread):
def __init__(self, movie_name):
super().__init__()
self.movie_name = movie_name
# 在run方法中实现需要在子线程中执行的任务。实现任务对应的功能需要额外的数据,不能传参,通过添加对象属性来提供数据
def run(self):
print(f'{self.movie_name}开始下载:{datetime.now()}')
print('下载中....')
# print(self.movie_name[:])
time.sleep(randint(2, 5))
print(f'{self.movie_name}下载完成:{datetime.now()}')
t1 = DownloadThread('百变星君')
t2 = DownloadThread('美人鱼')
t3 = DownloadThread('回魂夜')
t1.start()
t2.start()
t3.start()
# =========在主线程中等待(电影下载完成前,主线程什么都不能做)============
# t1.join()
# t2.join()
# t3.join()
# print('电影都下载完成!')
# ==========在子线程中等待(电影下载完成前,主线程可以干其他的事情)
def wait():
t1.join()
t2.join()
t3.join()
print('==========电影下载完成!=============')
t4 = Thread(target=wait)
t4.start()
# =========
while True:
print('=======')
time.sleep(1)
四、queue的使用
注意: 在子线程中调用的函数没有返回值(有返回值也没有办法获取)
Queue提供的是线程安全的序列,专门用来保存不同线程中产生的数据,同时提供线程相关操作
计算nums中所有的数字的阶乘的和
nums = [6, 8, 4]
def factorial(num, result: Queue):
re = reduce(lambda x, y: x*y, range(1, num+1), 1)
# print(result)
# return result
time.sleep(2)
# result.append(re)
result.put({num: re})
# result = []
# 创建空队列
result = Queue()
t1 = Thread(target=factorial, args=(6, result))
t1.start()
t2 = Thread(target=factorial, args=(8, result))
t2.start()
t3 = Thread(target=factorial, args=(4, result))
t3.start()
t1.join()
t2.join()
t3.join()
# 取出结果
print(result.get())
print(result.get())
print(result.get())
五、51job数据爬取
from selenium import webdriver
from selenium.webdriver.common import keys
from pyquery import PyQuery as pq
import requests
import time
from threading import Thread
# 创建浏览器对象
browser = webdriver.Chrome()
# 指定浏览器打开的页面
browser.get('https://www.51job.com/')
def create_web_page():
# 获取输入框
input = browser.find_element_by_id('kwdselectid')
# 在输入框中输入Python
input.send_keys('Python')
# 在输入框中按回车
input.send_keys(keys.Keys.ENTER)
# input.click() # 点击指定标签
# 通过浏览器获取页面内容
doc = pq(browser.page_source)
jobs_box = doc.find('.j_joblist>.e>.el')
# 开始获取每个职位的详情页,并且获取职位信息
start = time.time()
all_thread = []
for job in jobs_box:
job_url = pq(job).attr('href')
t = Thread(target=get_url, args=(job_url,))
t.start()
all_thread.append(t)
# get_url(job_url)
for t in all_thread:
t.join()
end = time.time()
print(f'耗时:{end-start}')
# 通过requests获取指定页面的数据
def get_url(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36'
}
re = requests.get(url, headers=headers)
# print(re.encoding)
re.encoding = 'gbk'
print('=================================================')
# print(re.text)
doc = pq(re.text)
message_box = doc.find('.tCompany_main .tBorderTop_box:first-child')
print(message_box.text())
def main():
create_web_page()
if __name__ == '__main__':
main()
# headers = {
# 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36'
# }
# re = requests.get('https://jobs.51job.com/chengdu-qyq/119797173.html?s=01&t=0', headers=headers)
# print(re.encoding)
# re.encoding = 'gbk'
# print(re.text)