python 复习
import requests
import time
from multiprocessing import Process, Queue
def download(urls, queue): # urls = images
for image_url in urls:
response = requests.get(image_url)
image_data = response.content
queue.put(image_data)
def save_file(queue):
count = 0
while True:
try:
data = queue.get(timeout=5)
# 保存到本地
filename = 'img' + str(count) + '.jpg'
with open('images/' + filename, 'wb') as ws:
ws.write(data)
count += 1
print('保存{}完毕!'.format(filename))
except Exception as err:
print('没有更多数据啦!')
break
if __name__ == '__main__':
q1 = Queue(2)
images = [
'https://gss0.bdstatic.com/94o3dSag_xI4khGkpoWK1HF6hhy/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=7bf3ea52d40735fa85fd46ebff3864d6/8644ebf81a4c510f0912aa536059252dd52aa5a1.jpg',
'https://gss1.bdstatic.com/9vo3dSag_xI4khGkpoWK1HF6hhy/baike/c0%3Dbaike92%2C5%2C5%2C92%2C30/sign=aa62e3280fb30f242197e451a9fcba26/eaf81a4c510fd9f906c11d20252dd42a2934a4a1.jpg'
]
p1 = Process(target=download, args=(images, q1))
p2 = Process(target=save_file, args=(q1,))
start = time.time() # 开始的时间戳
p1.start()
p2.start()
p1.join()
p2.join()
end = time.time() # 结束的时间戳
print('用时:{}秒'.format(end - start))
多进程适合在CPU 密集型操作(cpu 操作指令比较多,如科学计算,位数多的浮点运算) 多线程适合在IO 密集型操作(读写数据操作较多的,比如爬虫) 线程是并发,进程是并行;进程之间相互独立,是系统分配资源的最小单位,同一个进程中的所有线程共享资源。 进程:一个运行的程序或代码就是一个进程,一个没有运行的代码叫程序。进程是系统进行资源分配的最小单位,进程拥有自己的内存空间,所以进程间数据不共享,开销大。 线程:调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程的存在而存在,一个进程至少有一个线程,叫主线程,多个线程共享内存(数据共享和全局变量),因此提升程序的运行效率。 协程:用户态的轻量级线程,调度有用户控制,拥有自己的寄存器上下文和栈,切换基本没有内核切换的开销,切换灵活。 1、什么是进程? 进程是程序的一次动态执行过程。每个进程都拥有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据 操作系统负责其上所有进程的执行,操作系统会为这些进程合理地分配执行时间。 2、什么是线程? 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 一个线程是一个execution context(执行上下文),即一个cpu执行时所需要的一串指令。 假设你正在读一本书,没有读完,你想休息一下,但是你想在回来时恢复到当时读的具体进度。 有一个方法就是记下页数、行数与字数这三个数值,这些数值就是execution context。 如果你的室友在你休息的时候,使用相同的方法读这本书。 你和她只需要这三个数字记下来就可以在交替的时间共同阅读这本书了。 线程的工作方式与此类似。CPU会给你一个在同一时间能够做多个运算的幻觉,实际上它在每个运算上只花了极少的时间, 本质上CPU同一时刻只干了一件事。它能这样做就是因为它有每个运算的execution context。 就像你能够和你朋友共享同一本书一样,多任务也能共享同一块CPU。 3、什么是主线程? 主线程就是创建进程中产生的第一个线程,也就是main函数对应的线程。 4、线程ID 线程是一个轻量级进程,每一个用户态的线程 ,在内核中都对应这一个调度实体,也拥有着自己 的进程描述符。 tgid:线程组ID,线程组中的每一个线程的tgid都是相同的,在外表现为进程ID,它等于主线程的ID 。 tid:每一个线程在自己的用户层面上都有一个私有的pid,可以通过tid找到自己的虚拟地址,再通过页表映射到物理地址空间。在用户层面上展现的是线程的ID(tid),但在内核中它实际上是一个轻量级进程(pid)。 多线程 操作系统通过给不同的线程分配时间片(CPU运行时长)来调度线程,当CPU执行完一个线程的时间片后就会快速切换到下一个线程, 时间片很短而且切换切速度很快以至于用户根本察觉不到。早期的计算机是单核单线程的, 多个线程根据分配的时间片轮流被CPU执行,如今绝大多数计算机的CPU都是多核的, 多个线程在操作系统的调度下能够被多个CPU并发执行,程序的执行速度和CPU的利用效率大大提升。 绝大多数主流的编程语言都能很好地支持多线程,然而python由于GIL锁无法实现真正的多线程。 GIL锁 GIL是什么呢?仍然用篮球比赛的例子来帮助理解:把篮球场看作是CPU,一场篮球比赛看作是一个线程, 如果只有一个篮球场,多场比赛要排队进行,就是一个简单的单核多线程的程序;如果有多块篮球场, 多场比赛同时进行,就是一个简单的多核多线程的程序。然而python有着特别的规定: 每场比赛必须要在裁判的监督之下才允许进行,而裁判只有一个。这样不管你有几块篮球场, 同一时间只允许有一个场地进行比赛,其它场地都将被闲置,其它比赛都只能等待。
''' 进程间通信与信号量 队列: FIFO put() : 队列中存放,如果满了则阻塞 get() : 从队列中取值,如果空了则阻塞 full() 判断是否是满了 empty() 判断是否是空了 如果空了,True qsize() 获取长度 ''' from multiprocessing import Process, Queue queue = Queue(3) queue.put('馒头1') queue.put('馒头2') queue.put('馒头3') print(queue.full()) # 判断是否满了 print(queue) print(queue.qsize()) try: queue.put('馒头4', timeout=3) queue.put('馒头5', timeout=3) except: print('存放馒头失败') while True: try: print(queue.get(timeout=1)) except: print('队列为空,取不出东西') break
import json dict1 = {'tianfengli': [{'sno': '001', 'sname': 'xiaohua', 'age': 18, 'status': False}, {'sno': '002', 'sname': 'egou', 'age': 20, 'status': True}, {'sno': '003', 'sname': 'xiaoming', 'age': 18, 'status': False} ], 'beike': [ {'sno': '011', 'sname': 'xiaohua1', 'age': 18, 'status': False}, {'sno': '012', 'sname': 'egou1', 'age': 20, 'status': True}, {'sno': '013', 'sname': 'xiaoming1', 'age': 18, 'status': False} ] } # dict ---》 json字符串 序列化 # result = json.dumps(dict1) # print(result) # print(type(result)) # # # 反序列化 # r = json.loads(result) # print(r) # students = r.get('tianfengli') # for student in students: # if 'xiaohua1' == student.get('sname'): # print('找到了') # break # else: # print('没有此学生!') ''' 序列化: dumps 反序列化: loads dump load ''' with open('students.txt','w') as wstream: json.dump(dict1,wstream) print('保存成功') with open('students.txt','r') as rstream: content = json.load(rstream) print(content) students = content.get('beike') print(students)
dict1 = {'tianfengli': [{'sno': '001', 'sname': 'xiaohua', 'age': 18, 'status': False},
{'sno': '002', 'sname': 'egou', 'age': 20, 'status': True},
{'sno': '003', 'sname': 'xiaoming', 'age': 18, 'status': False}
],
'beike': [
{'sno': '011', 'sname': 'xiaohua1', 'age': 18, 'status': False},
{'sno': '012', 'sname': 'egou1', 'age': 20, 'status': True},
{'sno': '013', 'sname': 'xiaoming1', 'age': 18, 'status': False}
]
}
import pickle
result = pickle.dumps(dict1)
print(result)
r = pickle.loads(result)
print(r)
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return self.name
stu = Student('xiaohua', 20)
stu1 = Student('ergou', 19)
bobj = pickle.dumps(stu)
print(bobj)
stu = pickle.loads(bobj)
print(stu)
with open('stus.txt', 'wb') as ws:
pickle.dump(stu, ws)
pickle.dump(stu1, ws)
'''
正则表达式:
regular expression regex---> 验证匹配
邮箱
qq
用户名
密码
match() 匹配 从头进行匹配 匹配不成功则返回None
fullmatch() 从头到尾 整个字符串跟pattern进行匹配 匹配不成功则返回None
search() 扫描整个字符串查找匹配正则格式的内容,找到返回Match
Match对象:
match.group() 匹配的内容
match.span() 匹配内容的位置
'''
import re
# 通过re模块的compile函数,返回一个pattern对象
pattern = re.compile('abc')
# 通过pattern对象:match search findall split ...
match_obj = pattern.match('abcdef')
print(match_obj) # 匹配对象
# 匹配对象调用group获取匹配的内容
g = match_obj.group()
print(g)
match_obj = re.fullmatch('abc', 'xyzABCdef', re.I)
print(match_obj)
match_obj = re.search('abc', 'xyzABCdef', re.I)
print(match_obj)
print(match_obj.group()) # 匹配的内容
print(match_obj.span()) # 匹配内容的位置
print(match_obj.start())
'''
match
fullmatch
search: 只要在字符串中找到匹配的则不会继续查找
findall: 查找所有匹配的内容,返回值是一个列表
特殊字符:
‘.’ 任意字符除了换行
[] 表示的是一个范围 [xyz] 或者是x或者是y或者是z
[a-zA-Z] a-z或者A-Z之间的任意一个字母
[0-9] 任意一个数字
* >=0
+ >=1
? 0 或者 1
{n} [0-9]{6} 6位数字 \w{6} _admin 符合正则 admin123 不符合正则
{n,} >=n \d{6,} 1234567 符合 123456符合 1234不符合
\w{6,} _admin 符合正则 admin123 符合正则
{n,m} 大于等于n,小于等于m
预定义字符:
\d ~ [0-9] 相反:\D [^0-9] [^\d] digit
\w ~ [a-zA-Z0-9_] 相反:\W [^a-zA-Z0-9_] word
\s space 空白字符
\b
'''
import re
# a_b anb atb aqb ...
s = 'worldaxxubhello'
m_obj = re.search('a[xyzu]*b', s, re.I)
if m_obj:
print(m_obj.group())
else:
print('没有找到')
# 两头是一个数字中间是字母(可以是多个至少要有一个)
print('--------------------')
s = '6ui89klop0'
m_obj = re.search('[0-9][a-z]+[0-9]', s)
if m_obj:
print(m_obj.group())
else:
print('没有找到')
result = re.findall('[0-9][a-z]+[0-9]', s)
print(result)
print('--------qq验证-------------')
# qq 验证qq号码 不能是0开头 数字 5-10 长度
qq = input('请输入qq号码:')
# m_obj = re.fullmatch('[1-9]\d{4,9}', qq)
# if m_obj:
# print(m_obj.group())
# else:
# print('qq号码格式错误')
# 实现方式二: ^ 开头 $ 结束
m_obj = re.search('^[1-9]\d{4,9}$', qq)
if m_obj:
print(m_obj.group())
else:
print('qq号码格式错误')
'''
\w 表示字母数字下划线
{n,} >=n
{n} 必须是n位
{3,4} 3-4位
\d
\w
\b
注意: 在写正则表达式要在前面添加'r', raw
验证用户名: 可以是字母数字下划线,不能是数字或者_开头,用户名长度必须6位以上
手机号码验证:1开头 可以是13/15/18xxxxxxx 11位
座机:区号-电话号码 区号必须3或者四位 电话号码是8位数字
已知一句话: 'I am a good boy , a handsome boy',提取这句话中的所有单词
re 模块中的:
sub(pattern,repl,str) 替换
repl: 可以str类型也可以是callable类型
'''
import re
# 验证用户名: 可以是字母数字下划线,不能是数字或者_开头,用户名长度必须6位以上
# username = input('输入用户名:')
# m_obj = re.search('^[a-z]\w{5,}$', username, re.I)
# if m_obj:
# print('用户名合法')
# else:
# print('用户名不合法')
# 手机号码验证:1开头 可以是13/15/18xxxxxxx 11位
# phone = input('输入手机号码:')
# m_obj = re.search('^1[358]\d{9}$', phone)
# if m_obj:
# print('手机号码合法')
# else:
# print('手机号码不合法')
# 座机:区号-电话号码 区号必须3或者4位 电话号码是8位数字
# tel = input('输入座机号码:')
# m_obj = re.fullmatch('\d{3,4}-\d{8}', tel)
# if m_obj:
# print('号码合法')
# else:
# print('号码不合法')
# 已知一句话: 'I am a good boy , a handsome boy',提取这句话中的所有单词
s = 'I am a good boy , a handsome boy'
words = re.findall(r'\b[a-z]+\b', s, re.I)
print(words)
# \s space
s = ' am a good boy '
# s.strip()
# s = s.replace(' ', '#')
# print(s)
s = re.sub(r'\s+', "#", s)
print(s)
s = 'xiaohua=20 xiaoming=21 xiaohong=19'
def change(mobj):
content = mobj.group()
result = str(int(content) + 1)
return result
s = re.sub(r'\d+', change, s)
print(s)
'''
分组操作 () 表示的是一组 | 或者关系
分组命名匹配
() + \number : \number 表示引用组匹配的内容
'''
import re
# Jack Tom Lucy Linda Steven Daniel。。。 JOY
s = 'hi linda'
m_obj = re.search('(hi|hello) (Jack|Tom|Lucy|Linda)', s, re.I) #
print(m_obj)
print(m_obj.group())
print(m_obj.group(1))
print(m_obj.group(2))
# 邮箱:126,163,qq 而且必须符合邮箱的格式 xxx@163.com xxx的长度4-16位之间
# email = input('请输入邮箱:')
# m_obj = re.search(r'^(\w{4,16})@(126|163|qq).com$', email)
# if m_obj:
# email_name = m_obj.group(1)
# print(email_name + '符合邮箱格式')
# else:
# print('不符合格式!')
# 1开头,不是以4、7结尾的手机号码(11位) 或者 010-34567897|0311-37647326
# tel = input('请输入号码:')
# m_obj = re.fullmatch(r'(1\d{9}[0-35689]|\d{3,4}-\d{8})', tel)
# print(m_obj)
# 标签:<a class="logo" target="_blank" href="/">
'''
<div class="breadcrumb">
<a target="_blank" class="album-name" href="/pic/%E5%8B%92%E5%B8%83%E6%9C%97%C2%B7%E8%A9%B9%E5%A7%86%E6%96%AF/1989503">勒布朗·詹姆斯图册</a> > <span class="album-desc">词条图片</span>
</div>
'''
s = '<div class="breadcrumb">詹姆斯</div>'
m = re.fullmatch(r'<(.+) class="breadcrumb">(.+)</\1>', s) #
print(m)
print(m.group(2))
s = "<p class='breadcrumb'><a href='http://www.baidu.com'>詹姆斯 </a></p>"
m = re.fullmatch(r"<(.+) class='breadcrumb'><(.+) href='http://www.baidu.com'>.+</\2></\1>", s)
print(m.group())
print(m.group(1))
print(m.group(2))
# 命名的方式(?P<outer>) (?P<inner>)
# http://image.baidu.com/star/page
m = re.fullmatch(
r"<(?P<outer>.+) class='breadcrumb'><(?P<inner>.+) href='http://www.baidu.com'>.+</(?P=inner)></(?P=outer)>", s)
print(m.group())
print(m.group(1))
print(m.group(2))
# 贪婪与非贪婪:
s = 'abbbbbbbbbc'
m = re.match('ab+?', s)
print(m.group())
s = '<div>hello</div><div>world</div>'
m = re.match(r'<(?P<n>.+)>.+?</(?P=n)>', s)
print(m.group())
import re
s = '<div aaaaa>lalala</div><div hahahaha>lelelelele</div>'
m = re.match(r'<(?P<lala>.+)>.+</(?P=lala)>', s)
print(m.group())
with open('stus.txt', 'rb') as rs: while True: try: content = pickle.load(rs) print(content) except: print('读取完毕!') break
最大值 def func(list1): if isinstance(list1, list): max1 = list1[0] max_index = 0 for index, i in enumerate(list1): if i > max1: max1 = i max_index = index # 返出结果 return max1, max_index, 'a' # (max1, max_index,'a')
全局变量
books = ['红楼梦', '西游记', '水浒传', '三国演义']
number = 10
def func():
# books = []
books.append('斩龙')
print(books)
global number
number += 8
'''
闭包:
1.在一个函数中定义了另一个函数
2.内层函数使用了外层函数的变量
3.返回值是内层函数
'''
def outer_func(n):
a = 10
def inner_func():
r = a + n
print('r的值:', r)
print(locals())
return inner_func
'''
高阶函数:
把函数当成参数进行传递的函数。
1.sorted(iterable,key,reverse) 其中key就是一个函数参数
sorted(list1,key=函数)
通过key这个函数指定规则
2.map(function,iterable) ---》map object
['tom','jack','lily','lucy']
给一个可迭代的对象,通过function的作用,将其转成一个新的对象
3.filter(function,iterable): 返回值是一个filter object
需要对返回值进行转换:list(filter_object)
function函数的返回值必须是bool类型
4.reduce(function,iterable) ---> value
参数function是函数,此函数的参数必须两个 lambda x,y:x*y
参数iterable是一个可迭代对象,
1*2*3*4*5 ==》5!
'''
from functools import reduce
dict1 = {'zhangsan': 90, 'lisi': 100, 'wangwu': 89, 'zhaoliu': 92}
result = sorted(dict1.items(), key=lambda x: x[1])
dict1 = dict(result)
print(dict1)
# map 映射 [1,4,9,16,25]
map1 = map(lambda x: x + 2, [1, 2, 3, 4, 5])
print(map1)
print(list(map1))
numbers = [2, 4, 7, 9, 0, 12, 45, 78, 23]
filter1 = filter(None, numbers)
print(list(filter1))
filter2 = filter(lambda x: x % 2 == 0, numbers)
print(list(filter2))
# reduce() reduce(function, sequence[, initial]) -> value
list2 = [1, 2, 3, 4]
result = reduce(lambda x, y: x + y, list2, 1)
print(result)
result = reduce(lambda x, y: x * y, range(1, 6))
print(result)
map0 = map(lambda x, y: x + y, [1, 2, 3], [2, 2, 2])
print(list(map0))
'''
列表推导式:
格式: [表达式 for i in list|set|tuple|dict [if 条件]]
格式: [表达式1 if 条件 else 表达式2 for i in list|set|tuple|dict]
格式: [表达式1 for x in list|set|tuple|dict for y in list|set|tuple|dict]
'''
list1 = [8, 9, 0]
list2 = [x + 1 for x in list1 if x % 2 == 0]
print(list2)
list3 = [x + 1 if x % 2 == 0 else x + 2 for x in list1]
print(list3) # [3, 5, 7, 9, 9, 11, 5]
list0 = [1, 2, 3]
list4 = [(x, y) for x in list0 for y in list1]
print(list4)
'''
函数种类:
普通函数
匿名函数
高阶函数
递归函数
'''
# 递归函数:在函数的定义中,调用函数自身,递归函数
# 递归函数原则:1. 递归函数必须要有出口 2. 不断的向出口靠近
count = 0
def func():
global count
if count == 100:
# 出口
print('----->count:', count)
else:
print('----->count:', count)
count += 1 # 不断的向出口靠近
func()
count = 10 # 固定的次数
def get_sum(n):
if n == count:
return count
else:
return n + get_sum(n + 1)
s = get_sum(1)
print('=====================>', s)
# 使用递归的方式求斐波那契数列
list1 = []
def func(n):
for i in range(n):
if i == 1 or i == 0: # 出口
list1.append(1)
else:
list1.append(list1[i - 1] + list1[i - 2])
func(8)
print(list1)
'''
列表推导式:符号: []
集合推导式: 1. 符号{}
{表达式 for x in iterable if 条件}
{表达式1 if 条件 else 表达式2 for x in iterable }
...
2. 去重
字典推导式:
符号:{}
格式: {key:value for 循环}
'''
list1 = [i for i in range(10) if i % 2 != 0]
print(list1)
list2 = [1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 2, 4, 6]
set1 = {x for x in list2 if x % 2 == 0}
print(set1)
set2 = {x if x % 2 == 0 else x + 1 for x in list2}
print(set2)
dict1 = {'a': 1, 'b': 2, 'c': 3}
dict2 = {v: k for k, v in dict1.items()}
print(dict2)
foods = ['火锅', '茶', '麻辣香锅', '红烧肉']
dict3 = {index + 1: value for index, value in enumerate(foods)}
print(dict3)
print(type(dict3))
'''
生成器:generator 在Python中,这种一边循环一边计算的机制,称为生成器
1. 推导式使用的符号()
2. 函数+yield
结合next(g) ---> 得到下一个元素
'''
# list1 = [x for x in range(2000)]
# print(list1)
g = (x for x in range(3))
print(type(g)) # <class 'generator'>
print(g)
print(next(g))
print(next(g))
print(next(g))
print(next(g, 5))
'''
文件不是一个图片,文本文件
表格:
excel
数据库导出 ---》 csv
'''
import csv
def write_csv(file_path):
with open(file_path, 'w', newline='') as wstream:
csv_writer = csv.writer(wstream)
# 写东西
csv_writer.writerow(['001', 'xiaohua', 19])
csv_writer.writerow(['002', 'ergou', 20])
csv_writer.writerow(['003', 'xiaoming', 19])
def dictwrite_csv(file_path):
with open(file_path, 'w', newline='') as wstream:
fieldnames = ['sno', 'sname', 'age']
csv_writer = csv.DictWriter(wstream, fieldnames)
# 写东西
csv_writer.writeheader()
csv_writer.writerow({'sno': '001', 'sname': 'xiaohua', 'age': 19})
csv_writer.writerow({'sno': '002', 'sname': 'ergou', 'age': 22})
csv_writer.writerow({'sno': '003', 'sname': 'xiaoming', 'age': 21})
# 读取文件
def csv_read():
with open(file_path, 'r', newline='') as rstream:
csv_reader = csv.reader(rstream)
# 读取内容
for row in csv_reader:
print(row)
def dictcsv_read():
with open(file_path, 'r', newline='') as rstream:
csv_reader = csv.DictReader(rstream)
# 读取内容
for row in csv_reader:
print(dict(row))
file_path = r'C:\Users\running\Desktop\GPDay15(文件与系统模块)\代码\users.csv'
# write_csv(file_path)
# dictwrite_csv(file_path)
dictcsv_read()
'''
进程池:
Pool
阻塞式
非阻塞式:
'''
import os
import time
from multiprocessing import Pool
def task1():
print('洗衣服:', os.getpid(), os.getppid())
time.sleep(0.5)
return '我是进程:' + str(os.getpid())
# response = requests.get(url)
# return response.content
def callback(msg):
print('{}洗衣服任务完成!'.format(msg))
# 保存下载的文件到本地
if __name__ == '__main__':
pool = Pool(4)
# 非阻塞式
for i in range(10):
pool.apply_async(task1, callback=callback)
# 添加任务结束
pool.close()
# 阻塞主进程
pool.join()
print('main over')
多进程适合在CPU 密集型操作(cpu 操作指令比较多,如科学计算,位数多的浮点运算) 多线程适合在IO 密集型操作(读写数据操作较多的,比如爬虫) 线程是并发,进程是并行;进程之间相互独立,是系统分配资源的最小单位,同一个进程中的所有线程共享资源。 进程:一个运行的程序或代码就是一个进程,一个没有运行的代码叫程序。进程是系统进行资源分配的最小单位,进程拥有自己的内存空间,所以进程间数据不共享,开销大。 线程:调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程的存在而存在,一个进程至少有一个线程,叫主线程,多个线程共享内存(数据共享和全局变量),因此提升程序的运行效率。 协程:用户态的轻量级线程,调度有用户控制,拥有自己的寄存器上下文和栈,切换基本没有内核切换的开销,切换灵活。 1、什么是进程? 进程是程序的一次动态执行过程。每个进程都拥有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据 操作系统负责其上所有进程的执行,操作系统会为这些进程合理地分配执行时间。 2、什么是线程? 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 一个线程是一个execution context(执行上下文),即一个cpu执行时所需要的一串指令。 假设你正在读一本书,没有读完,你想休息一下,但是你想在回来时恢复到当时读的具体进度。 有一个方法就是记下页数、行数与字数这三个数值,这些数值就是execution context。 如果你的室友在你休息的时候,使用相同的方法读这本书。 你和她只需要这三个数字记下来就可以在交替的时间共同阅读这本书了。 线程的工作方式与此类似。CPU会给你一个在同一时间能够做多个运算的幻觉,实际上它在每个运算上只花了极少的时间, 本质上CPU同一时刻只干了一件事。它能这样做就是因为它有每个运算的execution context。 就像你能够和你朋友共享同一本书一样,多任务也能共享同一块CPU。 3、什么是主线程? 主线程就是创建进程中产生的第一个线程,也就是main函数对应的线程。 4、线程ID 线程是一个轻量级进程,每一个用户态的线程 ,在内核中都对应这一个调度实体,也拥有着自己 的进程描述符。 tgid:线程组ID,线程组中的每一个线程的tgid都是相同的,在外表现为进程ID,它等于主线程的ID 。 tid:每一个线程在自己的用户层面上都有一个私有的pid,可以通过tid找到自己的虚拟地址,再通过页表映射到物理地址空间。在用户层面上展现的是线程的ID(tid),但在内核中它实际上是一个轻量级进程(pid)。 多线程 操作系统通过给不同的线程分配时间片(CPU运行时长)来调度线程,当CPU执行完一个线程的时间片后就会快速切换到下一个线程, 时间片很短而且切换切速度很快以至于用户根本察觉不到。早期的计算机是单核单线程的, 多个线程根据分配的时间片轮流被CPU执行,如今绝大多数计算机的CPU都是多核的, 多个线程在操作系统的调度下能够被多个CPU并发执行,程序的执行速度和CPU的利用效率大大提升。 绝大多数主流的编程语言都能很好地支持多线程,然而python由于GIL锁无法实现真正的多线程。 GIL锁 GIL是什么呢?仍然用篮球比赛的例子来帮助理解:把篮球场看作是CPU,一场篮球比赛看作是一个线程, 如果只有一个篮球场,多场比赛要排队进行,就是一个简单的单核多线程的程序;如果有多块篮球场, 多场比赛同时进行,就是一个简单的多核多线程的程序。然而python有着特别的规定: 每场比赛必须要在裁判的监督之下才允许进行,而裁判只有一个。这样不管你有几块篮球场, 同一时间只允许有一个场地进行比赛,其它场地都将被闲置,其它比赛都只能等待。
#协程写法,主要用于优化程序io等待时间
import time,asyncio
async def fun1():
print("我是fun1")
await asyncio.sleep(2)
print("伙椒func1")
async def fun2():
print("我是func2")
await asyncio.sleep(3)
print("我是fun2")
async def fun3():
print("我是func3")
await asyncio.sleep(4)
print("我是fun3")
async def main():
tasks=[fun1(),fun2(),fun3()]#在python3.11以后这个用法会取消 推荐的用法是 tasks=[asyncio.create_task(func1()),asyncio.create_task(func2()),asyncio.create_task(func3())
]
await asyncio.wait(tasks) if __name__=="__main__": t1=time.time() asyncio.run(main()) t2=time.time() print(t2-t1) # 4.00594162940979
# 协程爬虫模型
import asyncio
async def download(url):
print("准备开始下载")
await asyncio.sleep(3)
print("下载完成")
async def main():
urls = ['http://www.baidu.com',
'https://www.sina.com', 'http://www.sooho.com']
tasks = []
for url in urls:
d = download(url=url)
tasks.append(d)
await asyncio.wait(tasks)
asyncio.run(main())
#aiohttp 模块学习 异步请求 #还有词一个 aiofile可以用来异步写文件 import asyncio import aiohttp import random
import aiofiles #这就是改进后的代码 用aiofiles来写文件 urls=['https://img1.baidu.com/it/u=2994957850,1031393616&fm=253&fmt=auto&app=120&f=JPEG?w=1280&h=800', 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic1.win4000.com%2Fwallpaper%2Fb%2F57bc0475f309b.jpg&refer=http%3A%2F%2Fpic1.win4000.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1670558482&t=a070289a3c1690d4bd9256c738dd1a60', 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fup.enterdesk.com%2Fedpic_source%2Fe0%2F60%2Fd8%2Fe060d8b4a31a16f0be8f0dffdaab1499.jpg&refer=http%3A%2F%2Fup.enterdesk.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1670558482&t=30ba9a04ad4194bee342810532fbed08'] async def download(url): name=str(random.randint(1,10)) async with aiohttp.ClientSession() as session: async with session.get(url) as rep: async with aiofiles.open(name+'.jpg','wb') as f:#已改进 await f.write(rep.content.read())# 和request.get(">>>").content 一样 还有两个 requests.json() request.text() 已改 print("gaoding") async def main(): tasks=[] for url in urls: d=download(url) tasks.append(d) await asyncio.wait(tasks) asyncio.run(main())
import os
import time
from threading import Thread,current_thread
def task1():
for i in range(5):
print('{}洗衣服:'.format(current_thread().name), i, os.getpid(), os.getppid())
time.sleep(0.5)
def task2(n):
for i in range(n):
print('{}劳动最光荣,扫地中...'.format(current_thread().name), i, os.getpid(), os.getppid())
time.sleep(0.5)
if __name__ == '__main__':
print('main:', os.getpid())
# 创建线程对象
t1 = Thread(target=task1,name='警察')
t2 = Thread(target=task2,name='小偷', args=(6,))
# 启动线程
t1.start()
t2.start()
# t1.join()
# t2.join()
for i in range(3):
print("t1:", t1.is_alive())
print("t2:", t2.is_alive())
print('main:', i)
time.sleep(1)
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import time
def fn(name):
print(name)
time.sleep(1)
with ThreadPoolExecutor(50) as t:
for i in range(1000):
t.submit(fn,name=f'线程{i}')
print('over')
线程池
from threading import Thread, Lock
number = 0
def task(lock):
global number
lock.acquire() # 握住
for i in range(100000):
number += 1
lock.release() # 释放锁
if __name__ == '__main__':
lock = Lock()
t1 = Thread(target=task, name='1号窗口', args=(lock,))
t2 = Thread(target=task, name='2号窗口', args=(lock,))
t3 = Thread(target=task, name='3号窗口', args=(lock,))
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
print('number:', number)
定时任务方法
from apscheduler.schedulers.blocking import BlockingScheduler
import datetime
scheduler=BlockingScheduler(timezone='Asia/Shanghai')
@scheduler.scheduled_job('cron',second=3)
def hello():
print(datetime.datetime.now().__str__())
scheduler.start()
boke
https://www.cnblogs.com/yuminhu/p/15763074.html
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script> jquery 引入
https://jquery.cuishifeng.cn/regexp.html jquery速查 还有正则
$(':button:first').click(function(){ jquery ajax 请求用法
//获取用户名或者密码
var username = $(':text').val()
var password = $(':password').val()
if(username==''||password==''){
alert('用户名或者密码不能为空!~')
return;
}
// 使用正则验证用户名或者密码
// 发送请求
$.ajax({
type:'GET',
url:'http://localhost/getForm.php',
data:{username:username,password:password},
dataType:'json',
success:function(data){
if(data!=null||data!=''){
$('#box').text('用户名:'+data.username+',password:'+data.password);
}
}
})
})
bootstrap 基本用法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>表单</title>
<script src="js/bootstrap.min.js"></script>
<link rel="stylesheet" href="css/bootstrap.min.css">
</head>
<body>
<div class="container">
<form>
<div class="form-group">
<label for="username">用户名</label>
<input type="text" class="form-control" id="username" placeholder="用户名">
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control" id="password" placeholder="密码">
</div>
<div class="checkbox">
<label>
<input type="checkbox"> 记住密码?
</label>
</div>
<button type="submit" class="btn btn-success">提交</button>
</form>
<hr>
<form class="form-horizontal">
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">Email</label>
<div class="col-sm-10">
<input type="email" class="form-control" id="inputEmail3" placeholder="Email">
</div>
</div>
<div class="form-group">
<label for="inputPassword3" class="col-sm-2 control-label">Password</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="inputPassword3" placeholder="Password">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label>
<input type="checkbox"> Remember me
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-warning">Sign in</button>
</div>
</div>
</form>
</div>
</body>
</html>
flask 简单笔记
from flask import Flask, request, render_template,url_for
import settings
app = Flask(__name__)
app.config.from_object(settings)
class Friend:
def __init__(self, name):
self.name = name
self.gender = '女'
def __str__(self):
return self.name
@app.route('/')
def index():
# print(request.remote_addr)
# return 'welcome to Beijing!'
dict1 = {'name': '文浦', 'age': 20, 'phone': '18866668888'}
friends = ['沈腾', '小沈阳', '蔡徐坤', '周琦']
# result = render_template('index.html', name='文浦', age=20)
# girl = GirlFriend('小蔡蔡')
girl = None
result = render_template('index.html', user=dict1, friends=friends, girl=girl)
print(result)
return result
@app.route('/index')
def show_index():
fs = ['沈腾', '小沈阳', '蔡徐坤', '周琦']
friends = []
for f in fs:
friend = Friend(f)
friends.append(friend)
return render_template('index1.html', friends=friends)
@app.route('/filter')
def use_filter():
fs = ['沈腾', '小沈阳', '蔡徐坤', '周琦']
return render_template('index2.html', message='this is a message', msg='<h1>MESSAGE</h1>'
, msg1='<font color="red" size="7">MESSAGE</font>', money=99.689, fs=fs)
@app.route('/block')
def show_block():
return render_template('index3.html')
@app.route('/about')
def show_about():
return render_template('about.html')
if __name__ == '__main__':
print(app.url_map)
app.run(host='0.0.0.0', port=8000)
from flask import Flask, render_template
import settings
app = Flask(__name__)
app.config.from_object(settings)
goods = ['可乐', '雪碧', '酸奶', '北冰洋', '芬达']
@app.route('/goods')
def show_goods():
return render_template('index4.html', goods=goods)
@app.route('/macro')
def show_marco():
return render_template('index5.html',goods=goods)
if __name__ == '__main__':
print(app.url_map)
app.run()
自动化搭建环境
1.自动搭建(一)
text
#安装
pip install webdriver-helper
1.1示例
python
from webdriver_helper import *
#get_webdriver()后,不使用with也会自动关闭浏览器
#with get_webdriver() as driver:
# driver.get("https://www.baidu.com")
#使用get_webdriver会在程序执行完之后自动关闭浏览器
driver = get_webdriver()
driver.get("https://www.baidu.com")
2.自动搭建(二)
1.下载第三方库webdriver_manager
python
from selenium import webdriver
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
browser = webdriver.Chrome(ChromeDriverManager().install()) # 会自动匹配浏览器版本进行下载对应的驱动
browser.get("https://www.baidu.com")
browser.find_element(By.Id,"kw").send_keys("python")
browser.find_element(By.Id,"su").click()
browser.quit() # 关闭浏览器并退出驱动
selenium cookies 转requests
https://www.jianshu.com/p/c34fc1962496
https://tooltt.com/header2json/ headers转json
从selenium到helium:
from selenium import webdriver
from helium import *
driver = webdriver.Chrome()
set_driver(driver)
go_to("http://127.0.0.1:8080/#/login")
从helium到selenium:
from helium import *
start_chrome()
driver = get_driver() #或者直接driver = start_chrome()
element = driver.find_element_by_id('auto_test')
print(element.text)
driver.save_screenshot("D:/1.png")
Requestium = Requests + Selenium
id
element = wd.find_element(By.CSS_SELECTOR, '#searchtext')
classname
elements = wd.find_elements(By.CSS_SELECTOR, '.animal')
属性选择
CSS 选择器 可以指定 选择的元素要 同时具有多个属性的限制,像这样 div[class=misc][ctype=gun]
a[href$="gov.cn"]
比如, 要选择a节点,里面的href属性以 http 开头 ,就可以这样写
a[href^="http"]
比如, 要选择a节点,里面的href属性包含了 miitbeian 字符串,就可以这样写
a[href*="miitbeian"]
CSS selector 表达式 可以这样写:
div.footer1 > span.copyright
就是 选择 一个class 属性值为 copyright 的 span 节点, 并且要求其 必须是 class 属性值为 footer1 的 div节点 的子节点
也可以更简单:
.footer1 > .copyright
如果我们要 同时选择所有class 为 plant 和 class 为 animal 的元素。怎么办?
这种情况,css选择器可以 使用 逗号 ,称之为 组选择 ,像这样
.plant , .animal
父元素的第n个子节点
我们可以指定选择的元素 是父元素的第几个子节点
使用 nth-child
比如,
我们要选择 唐诗 和宋词 的第一个 作者,
也就是说 选择的是 第2个子元素,并且是span类型
所以这样可以这样写 span:nth-child(2) ,
如果你不加节点类型限制,直接这样写 :nth-child(2)
就是选择所有位置为第2个的所有元素,不管是什么类型
学员对nth-child的含义很容易产生误解,请点击这里,观看白月黑羽给实战班学员答疑讲解 nth-child
父元素的倒数第n个子节点
也可以反过来, 选择的是父元素的 倒数第几个子节点 ,使用 nth-last-child
比如:
p:nth-last-child(1)
就是选择第倒数第1个子元素,并且是p元素
父元素的第几个某类型的子节点
我们可以指定选择的元素 是父元素的第几个 某类型的 子节点
使用 nth-of-type
比如,
我们要选择 唐诗 和宋词 的第一个 作者,
可以像上面那样思考:选择的是 第2个子元素,并且是span类型
所以这样可以这样写 span:nth-child(2) ,
还可以这样思考,选择的是 第1个span类型 的子元素
所以也可以这样写 span:nth-of-type(1)
父元素的倒数第几个某类型的子节点
当然也可以反过来, 选择父元素的 倒数第几个某类型 的子节点
使用 nth-last-of-type
像这样
p:nth-last-of-type(2)
奇数节点和偶数节点
如果要选择的是父元素的 偶数节点,使用 nth-child(even)
比如
p:nth-child(even)
如果要选择的是父元素的 奇数节点,使用 nth-child(odd)
p:nth-child(odd)
如果要选择的是父元素的 某类型偶数节点,使用 nth-of-type(even)
如果要选择的是父元素的 某类型奇数节点,使用 nth-of-type(odd)
兄弟节点选择
点击这里,边看视频讲解,边学习以下内容
相邻兄弟节点选择
上面的例子里面,我们要选择 唐诗 和宋词 的第一个 作者
还有一种思考方法,就是选择 h3 后面紧跟着的兄弟节点 span。
这就是一种 相邻兄弟 关系,可以这样写 h3 + span
表示元素 紧跟关系的 是 加号
后续所有兄弟节点选择
如果要选择是 选择 h3 后面所有的兄弟节点 span,可以这样写 h3 ~ span
如果输入下面的表达式
/html/body/div
这个表达式表示选择html下面的body下面的div元素。
注意 / 有点像 CSS中的 > , 表示直接子节点关系。
绝对路径选择
从根节点开始的,到某个节点,每层都依次写下来,每层之间用 / 分隔的表达式,就是某元素的 绝对路径
上面的xpath表达式 /html/body/div ,就是一个绝对路径的xpath表达式, 等价于 css表达式 html>body>div
自动化程序要使用Xpath来选择web元素,应该调用 WebDriver对象的方法 find_element_by_xpath 或者 find_elements_by_xpath,像这样:
elements = driver.find_elements(By.XPATH, "/html/body/div")
相对路径选择
有的时候,我们需要选择网页中某个元素, 不管它在什么位置 。
比如,选择示例页面的所有标签名为 div 的元素,如果使用css表达式,直接写一个 div 就行了。
那xpath怎么实现同样的功能呢? xpath需要前面加 // , 表示从当前节点往下寻找所有的后代元素,不管它在什么位置。
所以xpath表达式,应该这样写: //div
‘//’ 符号也可以继续加在后面,比如,要选择 所有的 div 元素里面的 所有的 p 元素 ,不管div 在什么位置,也不管p元素在div下面的什么位置,则可以这样写 //div//p
对应的自动化程序如下
elements = driver.find_elements(By.XPATH, "//div//p")
如果使用CSS选择器,对应代码如下
elements = driver.find_elements(By.CSS_SELECTOR,"div p")
如果,要选择 所有的 div 元素里面的 直接子节点 p , xpath,就应该这样写了 //div/p
如果使用CSS选择器,则为 div > p
通配符
如果要选择所有div节点的所有直接子节点,可以使用表达式 //div/*
* 是一个通配符,对应任意节点名的元素,等价于CSS选择器 div > *
代码如下:
elements = driver.find_elements(By.XPATH, "//div/*")
for element in elements:
print(element.get_attribute('outerHTML'))
根据属性选择
点击这里,边看视频讲解,边学习以下内容
Xpath 可以根据属性来选择元素。
根据属性来选择元素 是通过 这种格式来的 [@属性名='属性值']
注意:
•属性名注意前面有个@
•属性值一定要用引号, 可以是单引号,也可以是双引号
根据id属性选择
选择 id 为 west 的元素,可以这样 //*[@id='west']
根据class属性选择
选择所有 select 元素中 class为 single_choice 的元素,可以这样 //select[@class='single_choice']
如果一个元素class 有多个,比如
<p id="beijing" class='capital huge-city'>
北京
</p>
如果要选 它, 对应的 xpath 就应该是 //p[@class="capital huge-city"]
不能只写一个属性,像这样 //p[@class="capital"] 则不行
根据其他属性
同样的道理,我们也可以利用其它的属性选择
比如选择 具有multiple属性的所有页面元素 ,可以这样 //*[@multiple]
属性值包含字符串
要选择 style属性值 包含 color 字符串的 页面元素 ,可以这样 //*[contains(@style,'color')]
要选择 style属性值 以 color 字符串 开头 的 页面元素 ,可以这样 //*[starts-with(@style,'color')]
要选择 style属性值 以 某个 字符串 结尾 的 页面元素 ,大家可以推测是 //*[ends-with(@style,'color')], 但是,很遗憾,这是xpath 2.0 的语法 ,目前浏览器都不支持
按次序选择
点击这里,边看视频讲解,边学习以下内容
前面学过css表达式可以根据元素在父节点中的次序选择, 非常实用。
xpath也可以根据次序选择元素。 语法比css更简洁,直接在方括号中使用数字表示次序
比如
某类型 第几个 子元素
比如
要选择 p类型第2个的子元素,就是
//p[2]
注意,选择的是 p类型第2个的子元素 , 不是 第2个子元素,并且是p类型 。
注意体会区别
再比如,要选取父元素为div 中的 p类型 第2个 子元素
//div/p[2]
第几个子元素
也可以选择第2个子元素,不管是什么类型,采用通配符
比如 选择父元素为div的第2个子元素,不管是什么类型
//div/*[2]
某类型 倒数第几个 子元素
当然也可以选取倒数第几个子元素
比如:
•选取p类型倒数第1个子元素
//p[last()]
•选取p类型倒数第2个子元素
//p[last()-1]
•选择父元素为div中p类型倒数第3个子元素
//div/p[last()-2]
范围选择
xpath还可以选择子元素的次序范围。
比如,
•选取option类型第1到2个子元素
//option[position()<=2]
或者
//option[position()<3]
•选择class属性为multi_choice的前3个子元素
//*[@class='multi_choice']/*[position()<=3]
•选择class属性为multi_choice的后3个子元素
//*[@class='multi_choice']/*[position()>=last()-2]
为什么不是 last()-3 呢? 因为
last() 本身代表最后一个元素
last()-1 本身代表倒数第2个元素
last()-2 本身代表倒数第3个元素
组选择、父节点、兄弟节点
点击这里,边看视频讲解,边学习以下内容
组选择
css有组选择,可以同时使用多个表达式,多个表达式选择的结果都是要选择的元素
css 组选择,表达式之间用 逗号 隔开
xpath也有组选择, 是用 竖线 隔开多个表达式
比如,要选所有的option元素 和所有的 h4 元素,可以使用
//option | //h4
等同于CSS选择器
option , h4
再比如,要选所有的 class 为 single_choice 和 class 为 multi_choice 的元素,可以使用
//*[@class='single_choice'] | //*[@class='multi_choice']
等同于CSS选择器
.single_choice , .multi_choice
选择父节点
xpath可以选择父节点, 这是css做不到的。
某个元素的父节点用 /.. 表示
比如,要选择 id 为 china 的节点的父节点,可以这样写 //*[@id='china']/.. 。
当某个元素没有特征可以直接选择,但是它有子节点有特征, 就可以采用这种方法,先选择子节点,再指定父节点。
还可以继续找上层父节点,比如 //*[@id='china']/../../..
兄弟节点选择
前面学过 css选择器,要选择某个节点的后续兄弟节点,用 波浪线
xpath也可以选择 后续 兄弟节点,用这样的语法 following-sibling::
比如,要选择 class 为 single_choice 的元素的所有后续兄弟节点 //*[@class='single_choice']/following-sibling::*
等同于CSS选择器 .single_choice ~ *
如果,要选择后续节点中的div节点, 就应该这样写 //*[@class='single_choice']/following-sibling::div
xpath还可以选择 前面的 兄弟节点,用这样的语法 preceding-sibling::
比如,要选择 class 为 single_choice 的元素的所有前面的兄弟节点 //*[@class='single_choice']/preceding-sibling::*
而CSS选择器目前还没有方法选择前面的 兄弟节点
要了解更多Xpath选择语法,可以点击这里,打开Xpath选择器参考手册
selenium 注意点
点击这里,边看视频讲解,边学习以下内容
我们来看一个例子
我们的代码:
•先选择示例网页中,id是china的元素
•然后通过这个元素的WebElement对象,使用find_elements_by_xpath,选择里面的p元素,
# 先寻找id是china的元素
china = wd.find_element(By.ID, 'china')
# 再选择该元素内部的p元素
elements = china.find_elements(By.XPATH, '//p')
# 打印结果
for element in elements:
print('----------------')
print(element.get_attribute('outerHTML'))
运行发现,打印的 不仅仅是 china内部的p元素, 而是所有的p元素。
要在某个元素内部使用xpath选择元素, 需要 在xpath表达式最前面加个点 。
像这样
elements = china.find_elements(By.XPATH, './/p')
拿seleniumcoockies 给 requests
from selenium import webdriver
import requests
import time
def getCookies():
# 设置浏览器默认存储地址
options = webdriver.ChromeOptions()
# options.add_argument('--headless')
driver = webdriver.Chrome(options=options)
driver.maximize_window()
driver.get("http://ke.sukejiaoben.com/store.php/index/login")
# # 输入用户名
# driver.find_element_by_id("userId").send_keys("cheng")
# # 输入密码
# driver.find_element_by_id("password").send_keys("admin123")
# # 等待拖拽验证码
# time.sleep(10)
# # 点击提交
# driver.find_element_by_css_selector("input.log_button").click()
# # 获取cookie
time.sleep(15)
cookies = driver.get_cookies()
driver.close()
return cookies
def crawler():
sess = requests.Session()
sess.headers.clear()
# # 将selenium的cookies放到session中
for cookie in getCookies():
sess.cookies.set(cookie['name'], cookie['value'])
url = 'http://ke.sukejiaoben.com/store.php/index?jump_url=map_customer&menu_url=map_customer'
# data = {'page': 1, 'start': 0, 'limit': 10}
html = sess.get(url).text
print(html)
crawler()
python 打包离线环境 pip安装
1、有网并且网速快时:生成的依赖文件
pip freeze > requirements.txt
在新环境直接
pip install -r requirements.txt
pip install -r requestment.txt路径 –target=安装目录
2、新环境没网,网络不佳
旧环境中 有些情况下我们需要下载N个第三方包,或者下载的包依赖其它包,一个个下载非常浪费时间。这时我们可以通过如下两种方式的命令批量下载。:
pip download -r requirement.txt -d ./python_package
方式1 pip download -d /tmp/packagesdir <packagename> 方式2 pip download -d /tmp/packagesdir -r requirements.txt
就将requirement中的依赖 全部下载成whl文件 放在python_package中
在新环境安装时:
pip install -r requirements.txt –no-index –find-links=./python_package