Python学习

类型

类型转换

int():将…转换为整数

float():将…转换为浮点数

str():将…转换为字符串

判断类型

查看变量类型type(),传入变量会告诉类型

判断变量类型isinstance(elem, type):判断elem是否为type类型,返回bool类型的真假

字符串

原始字符串:字符串前加一个r,可以不用转义

方法/函数 参数 返回值 作用
.title() 新字符串 将字符串各单词首字母改为大写
.upper() 新字符串 将字符串中字母全部改为大写
.lower() 新字符串 将字符串中字母全部改为小写
.rstrip() 新字符串 将字符串右侧空白删除
.lstrip() 新字符串 将字符串左侧空白删除
.strip() 新字符串 将字符串两端空白删除
str() 1 新字符串 将参数类型转换为字符串
.replace(old,new) 将字符串中old替换成new字符串
.split(sep=None) 分隔符 拆分后的列表 用参数作为分隔符拆分字符串返回列表
.join(iterable) 序列 字符串 将参数序列用.前的字符作为分隔符拼起来

格式化

format格式化

1
2
3
4
5
6
# 位置参数
"{0} love {1}".format("I", "you")
# 关键字参数
"{a} love {b}".format(a = "I", b = "you")

# 若混合使用,位置参数必须在关键字参数的前面

%格式化操作符

1
2
"%c" % 97
# 类似于C

序列

列表

方法/函数 参数 返回值 作用
list() 可迭代对象 列表 将可迭代对象转换为列表
len() 列表 列表长度 返回列表长度
max() 列表 一个列表元素 返回列表中最大值
min() 列表 一个列表元素 返回列表中最小值
.append() 1 将参数添加到列表末尾
.insert() 1,2 将参数2插入到列表下标为参数1的位置
del 1(删除索引或切片) 删除列表元素,用法del alist[1]
.pop() [1] 删除的元素 删除末尾元素,或下标为参数1的元素
.remove() 1 删除列表中第一个值为参数1的元素
.sort() [reverse=True] 将原列表从小到大排序(+可选参数从大到小)
sorted() 1,[reverse=True] 排序好的列表 将参数1列表 sort排序
.reverse() 反转列表元素排列顺序
.extend() 1(一个列表) 将参数列表添加到列表末尾
+号运算符 将两个列表合并
.count() 1 统计参数在列表中出现的次数
.index() 1 返回参数在列表中第一次出现的索引值

生成列表

函数range(start,end,step):包含start不包括end,步长为step

生成序列、元组

enumerate(iterable):生成一个二元组,每个二元组分别是可迭代对象的索引号和对应元素

zip(iter1,....):由多个可迭代对象生成多元组

收集参数

形参前加一个*,可获取任意数量的参数,将参数变为序列。

实参前加一个*,可将序列解包

使用range()创建数字列表

1
2
3
4
numbers = list(range(1,6))
print(numbers)
###########
[1, 2, 3, 4, 5]
1
2
3
4
squares = [value**2 for value in range(1,11)]
print(squares)
############
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

列表的切片

列表切片同 range

1
2
3
4
players = ['charles', 'martina', 'michael', 'florence', 'eli'] 
print(players[0:3])
##############
['charles', 'martina', 'michael']

负数则倒数开始,-1是倒数第一个

列表的复制

使用切片来复制列表,不要直接用=

因为只是给原来的列表起了两个名字

元组

元组是不能被修改的列表

修改元组只能给元组变量重新赋值

元组的定义是看,逗号,而不是小括号

删除整个元组使用del

tuple(ierable) :将可迭代对象转换为元组

集合

集合 具有唯一性, 会自动删除重复的数据

创建集合

1
2
3
4
set1 = {1,2,3,1}
set2 = set([1,2,3,1])
set1 == set2
# True

使用add()添加元素,remove()移除元素

不可变集合

1
set3 = frozenset([1,2,3,1])

if

连环判断else if可以写成elif

python的and对应&&or对应||

确定列表是否为空if alist: #不为空则进入if

三元运算符

a = x if x<y else y

如果if后条件为true,则为x;否则为y

循环

for循环

1
2
3
range(stop)
range(start, stop)
range(start, stop, step)

while 循环

else可用于for或者while,表示循环条件不成立时候应执行的内容

当循环有break和else时,break跳出时候并不会执行else内容

字典

字典示例

1
alien_0 = {'color': 'green', 'points': 5}

访问字典

1
2
3
print(alien_0['color'])
#####################
green

添加键值对

1
2
# 直接添加
alien_0['x'] = 60

创建空字典

1
2
3
4
5
6
alien_0 = {}

## 用dict()创建字典
d = dict((('name',"Li"),("sex","man")))
# d为
{'name': 'Li', 'sex': 'man'}

修改键值

1
2
# 直接赋值修改
alien_0['color'] = 'red'

删除键值

1
2
# 同列表
del alien_0['x']

遍历字典

遍历所有的键值对

1
2
3
4
5
6
7
8
9
10
11
user_0 = { 
'username': 'efermi',
'first': 'enrico',
'last': 'fermi',
}

for key, value in user_0.items():
print("\nKey: " + key)
print("Value: " + value)

# 方法 .items() 返回一个键值对列表

遍历所有键

1
for name in favorite_languages.keys():

遍历所有值

1
for name in favorite_languages.values():

字典内置方法

  • .fromkeys()
    • 用于创建并返回一个新的字典
    • 第一个参数是字典的键序列
    • 第二个参数是键的值
    • 会将所有键初始化为一个值
  • .get()
    • 用于访问字典项
    • 第一个参数为 需要查找值的
    • 第二个参数可选,如果没有找到返回的字符串
  • .clear():用于清空字典
  • .copy():拷贝整个字典
  • .pop(key):弹出(删除)键
  • .popitem():弹出一个键值
  • .setdefault():类似于get查找,但如果查找不到键时候会自动添加进字典
  • .update():更新字典,例如p.update(name = "LiHua")

当形参收集参数是 两个星号**时,参数会被打包成字典

函数

想在函数内修改全局变量,需要使用global关键字

1
2
3
4
5
count = 0
def fun():
global count
count = 5

想在内部函数修改外部函数的局部变量,使用nonlocal关键字,用法同global

lambda函数

1
2
3
4
5
6
7
lambda x,y:x+y
# :冒号 前为函数的参数,多个参数使用,分割
# 后为 函数的返回值

# 要想调用这个lambda函数,需要绑定一个名字
p = lambda x, y: x+y
p(2,3)

filter() 过滤器

两个参数

  • 第一个参数:为函数,则将第二个参数可迭代对象传进去,筛选出函数返回True的值

    为None(不可省略),则将第二个参数中 为True的 筛选出来

可结合lambda表达式

1
2
3
list(filter(lambda x:x%2,range(10)))
# 输出
[1, 3, 5, 7, 9]

map()

map将第二个参数可迭代对象送到第一个参数函数内加工,返回加工后的返回值序列

第二个参数是收集参数,因此第一个参数 函数可以是多参数函数

1
2
3
list(map(lambda x:2*x,range(10)))
# 输出
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

文件系统

打开文件

1
f = open("read.txt")	# 以默认只读模式打开

mode模式

模式 描述
t 文本模式 (默认)。
x 写模式,新建一个文件,如果该文件已存在则会报错。
b 二进制模式。
+ 打开一个文件进行更新(可读可写)。
U 通用换行模式(Python 3 不支持)。
r 只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。
r+ 打开一个文件用于读写。文件指针将会放在文件的开头。
rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。
w 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
w+ 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb+ 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
a **打开一个文件用于追加。**如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
ab 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
ab+ 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

默认为文本模式,如果要以二进制模式打开,加上 b

file 对象

file 对象使用 open 函数来创建,下表列出了 file 对象常用的函数:

序号 方法及描述
1 file.close()关闭文件。关闭后文件不能再进行读写操作。
2 file.flush()刷新文件内部缓冲,直接把内部缓冲区的数据立刻写入文件, 而不是被动的等待输出缓冲区写入。
3 file.fileno()返回一个整型的文件描述符(file descriptor FD 整型), 可以用在如os模块的read方法等一些底层操作上。
4 file.isatty()如果文件连接到一个终端设备返回 True,否则返回 False。
5 file.next()**Python 3 中的 File 对象不支持 next() 方法。**返回文件下一行。
6 file.read([size])从文件读取指定的字节数,如果未给定或为负则读取所有
7 file.readline([size])读取整行,包括 “\n” 字符。
8 file.readlines([sizeint])读取所有行并返回列表,若给定sizeint>0,返回总和大约为sizeint字节的行, 实际读取值可能比 sizeint 较大, 因为需要填充缓冲区。
9 file.seek(offset[, whence])移动文件读取指针到指定位置
10 file.tell()返回文件当前位置。
11 file.truncate([size])从文件的首行首字符开始截断,截断文件为 size 个字符,无 size 表示从当前位置截断;截断之后后面的所有字符被删除,其中 windows 系统下的换行代表2个字符大小。
12 file.write(str)将字符串写入文件,返回的是写入的字符长度。
13 file.writelines(sequence)向文件写入一个序列字符串列表,如果需要换行则要自己加入每行的换行符。
  • .tell():当前文件指针的位置

OS模块

菜鸟教程OS模块

pickle 保存数据

pickling:将python的对象转换为二进制数据保存;unpickling:从二进制文件中读取python对象

1
2
3
4
5
6
7
8
9
10
11
12
import pickle
alist = [1, 3.14, "You"]
file = open("alist.pkl","wb")
pickle.dump(alist, file)
file.close()
###############

############### 读取
import pickle
file = open("alist.pkl","rb")
alist = pickle.load(file)
file.close()

异常处理

捕获异常

1
2
3
4
5
6
7
8
try:
# 检测范围
except Exception [as reason]: # 后可省略,或者引用具体错误信息reason
# 异常处理语句
except TypeError:
# ...
finally:
# 无论是否出现异常都会被执行的内容

抛出异常

使用raise关键字抛出异常

1
raise ZeroDivisionError("除数不嫩那个为零!") # 括号为对异常的解释,可省略

else

只要try语句块没有出现任何异常,就会执行else语句块的内容

with语句

1
2
3
4
5
6
try:
with open("read.txt") as f:
for each_line in f:
print(each_line)
except OSError as reason:
print("出错啦:" + str(reason))

类和对象

定义一个类

__init__为构造方法,定义这个类的属性,第一个参数为self(类似this)

1
2
3
4
5
6
7
class Ball:
def __init_(self, name):
self.name = name
def setName(self, name):
self.name = name
def kick(self):
print("我叫%s,谁踢我?!" % self.name)

公有、私有

python默认公有,定义私有属性需要在 变量名或者函数名前加上__两个下划线,就会变成私有的了。

类的继承

1
2
3
4
5
# 语法
class 类名(被继承的类):

#### 多重继承
class 子类(父类1, 父类2, ....): # 多个父类使用,分隔

子类定义与父类同名的方法或者属性,则会自动覆盖父类对应的方法或者属性(方法重写)

子类在构造方法中需要写父类的构造方法

1
2
3
4
5
6
7
8
9
10
11
class Shark(Fish):
def __init__(self):
Fish.__init__(self)
self.hungry = True

## 或者使用super(推荐)
class Shark(Fish):
def __init__(self):
super.__init__()
self.hungry = True

类的组合

一个类中的属性是另一个类

类对象和实例对象

1
2
3
4
5
6
7
8
9
10
class C:
count = 0
a = C()
b = C()
c = C()
print(a.count, b.count, c.count) # 0 0 0
c.count += 10
print(a.count, b.count, c.count) # 0 0 10
C.count += 100
print(a.count, b.count, c.count) # 100 100 10

当对实例对象c的属性进行赋值时,会覆盖类对象的属性;如果没有赋值,则引用类对象的属性

绑定

类方法第一个参数要传self

类和对象 BIF(内置函数):

BIF 用途
issubclass(class,classinfo) 如果class是classinfo的子类则返回True
isinstance(object,classinfo) 如果object是classinfo的[子类]实例对象 返回True
hasattr(object, name) 若object对象含有name属性,返回True
getattr(object, name[,default]) 返回对象指定的属性值,若属性不存在返回default的值/default不存在AttributeError异常
setattr(object,name,value) 设置对象中指定属性的值,指定属性不存在则新建属性
delattr(object,name) 删除对象指定属性,属性不存在抛出AttributeError异常
property() 给属性绑定getter setter方法

classinfo可以为 类对象组成的元组, 只要其中有一个类来符合条件 就可

魔法方法

注意

魔法方法

  • 魔法方法总是被左右各两个下画线包围,例如_ _init_ _()
  • 魔法方法是面向对象的Python的一切

构造和析构

__init__(self[,…])

  • 相当于构造方法

  • _ _init_ _()方法的返回值一定是None

  • 只有在需要进行初始化的时候才重写

  • _ _init_ _()不是实例化对象时第一个被调用的魔法方法

__new__(cls[, …])

  • 在一个对象实例化的时候调用的第一个方法
  • 第一个参数不是self而是这个类(cls),而其他的参数会直接传递给_ _init_ _()方法
  • 返回一个实例对象,通常是cls这个类实例化的对象,当然也可以返回其他对象
  • 当继承一个不可变的类型的时候,需要重写这个方法

__del__

  • 只要在程序没有退出的时候,就执行了 del 对象名,就会执行对象对应的类中的del方法
  • _ _del_ _()方法是当垃圾回收机制回收这个对象的时候调用的

算数运算

  • python2.2后将int()、float()、str()、list()、tuple()这些BIF转换为工厂函数<class 'type'>
    • 普通bif是<class 'builtin_function_or_method'>
  • 工厂函数理解工厂函数

常见算术运算

反运算

  • 反运算魔法方法,一定要注意顺序问题

一元操作符

定制

  • 如果类中的方法名和属性同名,属性会覆盖方法

属性访问

  • 通过点(.)操作符去访问对象的属性

    • 还有bif:getattr,setattr,delattr
    • property()函数
  • super().可以有效解决死循环

描述符(property原理)

  • 描述符就是将某种特殊类型的类的实例指派给另一个类的属性

    • 特殊类型的类指的是:至少要在这个类里边定义_ _get_ _()__set_ _()__delete_ _()三个特殊方法中的任意一个
  • 与其类似的property()

定制序列

  • 容器包括:

    • 序列类型:如列表、元组、字符串
    • 映射类型:如字典
  • 定制容器的有关协议:

    • 如果希望定制的容器不可变,则只需要定义_ _len_ _()_ _getitem_ _()方法
    • 如果希望定制的容器是可变的,除了_ _len_ _()_ _getitem_ _()方法,还需要定义_ _setitem_ _()_ _delitem_ _()两个方法
  • 定制容器相关的魔法方法:

迭代器

  • 提供迭代方法的容器称为迭代器,通常接触的迭代器有序列(如列表、元组、字符)、字典等,它们都支持迭代的操作

  • 对一个容器对象调用iter()就得到它的迭代器。调用next()迭代器就会返回下一个值,如果迭代器没有值可以返回了Python会抛出一个名为StopIteration的异常。

生成器

  • 仅通过普通函数,不涉及魔法方法

  • Python是通过生成器来实现类似于协同程序的概念:生成器可以暂时挂起函数,并保留函数的局部变量等数据,然后再次调用它的时候,从上次暂停的位置继续执行下去。

  • yield语句

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def myGen():
    print("生成器被执行")
    yield 1
    yield 2

    myG = myGen()
    >>>next(myG) ### 生成器被执行
    ### 1
    >>>next(myG) ### 2
  • 由于Python的for循环会自动调用next()方法和处理StopIteration异常,所以for循环当然也是可以对生成器产生作用

生成器表达式

  • 列表推导式

    1
    2
    1. [i*i for i in range(10)]
    2. [i for i in range(10) if not(i%2) and i%3]
  • 字典推导式

    1
    2
    >>> {i:i % 2 == 0 for i in range(10)}
    {0: True, 1: False, 2: True, 3: False, 4: True, 5: False, 6: True, 7: False,8: True, 9: False}
  • 集合推导式

    1
    2
    >>> {i for i in [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 7, 7, 8]}
    {1, 2, 3, 4, 5, 6, 7, 8}

模块

导入模块

1
2
3
import modelName
import modelName as newName
from modelName import *

__name__属性

作为程序运行时候,py程序的__name__属性的值是'__main__'

作为模块被导入时,这个值为模块的名字

1
2
3
# 作为模块被导入的时候不会执行,作为程序的时候会执行
if __name__ == '__main__':

爬虫

urllib模块

模块函数 参数/返回值 作用
urllib.request.urlopen() 参数为url字符串或者request对象,返回类文件对象 访问网址

爬取的都是以utf-8编码的bytes对象

1
2
3
import urllib.request
response = urllib.request.urlopen("https://www.baidu.com")
html = response.read() # urlopen返回的类文件对象可以直接用文件方法read()

示例:下载图片

1
2
3
4
5
6
7
# coding=utf-8
import urllib.request
img_obj = urllib.request.Request("http://placekitten.com/800/800")
img_reponse = urllib.request.urlopen(img_obj)
with open("./cat.jpg", "wb") as f:
jpg = img_reponse.read()
f.write(jpg)

request库

  • r.text是服务器响应的内容,会自动根据响应头部的字符编码进行解码。

  • r.content也是相应返回的字节码

  • r.encoding是服务器内容使用的文本编码。

  • r.status_code用于检测响应的状态码,如果返回200,就表示请求成功了;如果返回的是4xx,就表示客户端错误;返回5xx则表示服务器错误响应。我们可以用r.status_code来检测请求是否正确响应。

  • 使用代理,需配置get()参数proxies

    • proxies = {
          'http' : address,
          'https' : address,
      }
      
      1
      2
      3
      4
      5

      - 保存cookie,创建一个session对象, 使用session对象来进行get和post

      - ```python
      session = requests.Session()

Xpath模块

  • 安装 python -m pip install lxml
  • 使用时需导入 from lxml import etree

学习语法:菜鸟教程 Xpath教程

  • 提取标签内容使用/text()
  • 提取标签属性的属性值使用/@attrname

部分语法

表达式 描述
nodename 选取此节点的所有子节点。
/ 从根节点选取。
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
. 选取当前节点。
选取当前节点的父节点。
@ 选取属性。
[starts-with(@attr,value)] 筛选出 标签属性值前半部分相同的
string(.) 提取标签里所有的文字

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from lxml import etree
import requests

def test():
t_response = requests.get("http://www.zdqx.com/pcbz/")
t_html = t_response.text
print(t_response.encoding)
selector = etree.HTML(t_html.encode('ISO-8859-1').decode())
result = selector.xpath('//ul[@class="desklist hauto"]/li/a/p/text()')
for i in result:
print(i)


if __name__ == '__main__':
test()
  • 如果出现中文乱码,自行查看网页编码进行转码,同上面例子
  • 其他例子
    • /html/head/title:选择HTML文档中head元素内的title元素。
    • /html/head/title/text():选择上面提到的title元素的文本。
    • //td:选择所有的td元素。
    • //div[@class="mine"]:选择所有具有class="mine"属性的div元
      素。
  • xpath 返回的是列表,列表元素是Selector类型的对象
  • .extract()可以将Selector对象中data参数存储的字符串提取出来

Python并行化

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
import multiprocessing  # 并行化需要导入的库


def printf(i):
print(i) # 可迭代对象的处理,尽量不要用输出,输出会乱


if __name__ == '__main__':
pool = multiprocessing.Pool(processes=4) # 并行化对象pool的声明,参数为进程数,与电脑核数有关
l = [i for i in range(10)]
pool.map(printf, l) # 使用.map方法来对可迭代对象来进行参数一函数的处理
pool.close() # 关闭,执行完close后不会有新的进程加入到pool
pool.join() # join函数等待所有子进程结束

正则表达式

re模块

使用前需要导入模块 import re

方法 作用
re.sarch(正则,字符串) 从字符串中找正则匹配返回 一个匹配对象
re.compile(正则字符串) 将正则编译成模式对象
匹配对象.group() 获得匹配对象匹配的字符串
模式对象.findall() 包含子组则返回子组组成的列表或者元组

正则知识

字符类

表示一个字符的范围,可以创建一个字符类。

使用中括号将任何内容包起来就是一个字符类;(大小写敏感,可关闭)含义:匹配这个字符类的任何字符就算匹配

  • 可使用-小斜杠来表示范围
    • 匹配小写字母[a-z]
  • 也可直接输入:[abcde12345]:匹配abcde12345中的任何一个字符

重复匹配

使用大括号这对元字符来实现重复匹配的功能

  • 匹配 3 次:[a-z]{3}
  • 匹配3次或者4次或者5次(3到5次):[a-z]{3,5}

元字符

  • ^:匹配字符串的开始位置

  • $:匹配字符串的结束位置

  • \

    • 将普通字符变成特殊(转义)字符;或者将特殊字符变成普通字符
    • 反斜杠后加数字1~99,表示引用序号对应的子组所匹配的字符串
    • 反斜杠后 0开头或者 三位数字 表示八进制数对应的ASCII字符
  • 小括号()

    子组:被 元字符小括号()括起来的正则表达式称为一个子组

    例如:(you)\1匹配的是youyou

  • 中括号[]:生成一个字符类,内部的元字符全都市区了特殊的功能

    • -:表示范围
    • /:用于字符串转义:\n匹配换行符
    • ^:表示取反
    • {}:表示重复
      • 重复2次(you){2}:匹配youyou
      • 重复2~4次(you){2,4}:匹配youyouyouyouyouyouyouyouyou
      • *:相当于{0,} 0次或者无穷多次
      • +:相当于{1,} 匹配至少1次至无穷多次
      • ?:相当于{0,1} 匹配0次或者1次
元字符 说明
. 匹配除换行符以外的任意字符
\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,‘n’ 匹配字符 “n”。‘\n’ 匹配一个换行符。序列 ‘\’ 匹配 “” 而 “(” 则匹配 “(”。
^ \A
$ \Z
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 “z” 以及 “zoo”。* 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,‘zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。
? 匹配前面的子表达式零次或一次。例如,“do(es)?” 可以匹配 “do” 或 “does” 。? 等价于 {0,1}。
{n} n 是一个非负整数。匹配确定的 n 次。例如,‘o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。
{n,} n 是一个非负整数。至少匹配n 次。例如,‘o{2,}’ 不能匹配 “Bob” 中的 ‘o’,但能匹配 “foooood” 中的所有 o。‘o{1,}’ 等价于 ‘o+’。‘o{0,}’ 则等价于 ‘o*’。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,“o{1,3}” 将匹配 “fooooood” 中的前三个 o。‘o{0,1}’ 等价于 ‘o?’。请注意在逗号和两个数之间不能有空格。
? 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 “oooo”,‘o+?’ 将匹配单个 “o”,而 ‘o+’ 将匹配所有 ‘o’。
. 匹配除换行符(\n、\r)之外的任何单个字符。要匹配包括 ‘\n’ 在内的任何字符,请使用像"(.|\n)"的模式。
(pattern) 匹配 pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在JScript 中则使用 $0…$9 属性。要匹配圆括号字符,请使用 ‘(’ 或 ‘)’。
(?:pattern) 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 “或” 字符 (|) 来组合一个模式的各个部分是很有用。例如, 'industr(?:y|ies) 就是一个比 ‘industry|industries’ 更简略的表达式。
(?=pattern) 正向肯定预查(look ahead positive assert),在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)“能匹配"Windows2000"中的"Windows”,但不能匹配"Windows3.1"中的"Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern) 正向否定预查(negative assert),在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如"Windows(?!95|98|NT|2000)“能匹配"Windows3.1"中的"Windows”,但不能匹配"Windows2000"中的"Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?<=pattern) 反向(look behind)肯定预查,与正向肯定预查类似,只是方向相反。例如,"`(?<=95
(?<!pattern) 反向否定预查,与正向否定预查类似,只是方向相反。例如"`(?<!95
x|y 匹配 x 或 y。例如,‘z|food’ 能匹配 “z” 或 “food”。‘(z|f)ood’ 则匹配 “zood” 或 “food”。
[xyz] 字符集合。匹配所包含的任意一个字符。例如, ‘[abc]’ 可以匹配 “plain” 中的 ‘a’。
[^xyz] 负值字符集合。匹配未包含的任意字符。例如,[^abc] 可以匹配 “plain” 中的’p’、‘l’、‘i’、‘n’。
[a-z] 字符范围。匹配指定范围内的任意字符。例如,[a-z]可以匹配 ‘a’ 到 ‘z’ 范围内的任意小写字母字符。
[^a-z] 负值字符范围。匹配任何不在指定范围内的任意字符。例如,[^a-z] 可以匹配任何不在 ‘a’ 到 ‘z’ 范围内的任意字符。
\b 匹配一个单词边界,也就是指单词和空格间的位置。例如, ‘er\b’ 可以匹配"never" 中的 ‘er’,但不能匹配 “verb” 中的 ‘er’。
\B 匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。
\cx 匹配由 x 指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 ‘c’ 字符。
\d 匹配一个数字字符。等价于 [0-9]。
\D 匹配一个非数字字符。等价于 [^0-9]
\f 匹配一个换页符。等价于 \x0c 和 \cL。
\n 匹配一个换行符。等价于 \x0a 和 \cJ。
\r 匹配一个回车符。等价于 \x0d 和 \cM。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]
\t 匹配一个制表符。等价于 \x09 和 \cI。
\v 匹配一个垂直制表符。等价于 \x0b 和 \cK。
\w 匹配字母、数字、下划线。等价于[A-Za-z0-9_]
\W 匹配非字母、数字、下划线。等价于 [^A-Za-z0-9_]
\xn 匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,‘\x41’ 匹配 “A”。‘\x041’ 则等价于 ‘\x04’ & “1”。正则表达式中可以使用 ASCII 编码。
\num 匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,‘(.)\1’ 匹配两个连续的相同字符。
\n 标识一个八进制转义值或一个向后引用。如果 \n 之前至少 n 个获取的子表达式,则 n 为向后引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。
\nm 标识一个八进制转义值或一个向后引用。如果 \nm 之前至少有 nm 个获得子表达式,则 nm 为向后引用。如果 \nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的向后引用。如果前面的条件都不满足,若 n 和 m 均为八进制数字 (0-7),则 \nm 将匹配八进制转义值 nm。
\nml 如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。
\un 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, \u00A9 匹配版权符号 (?)。

实例

贪婪 和 非贪婪

贪婪模式:在 符合的条件下,会尽可能多的去匹配

非贪婪:匹配最少次数

默认为贪婪模式,启用非贪婪模式,在 表示重复的元字符后 加上一个?

实践遇到的问题

  • 字符类中匹配-,例如一个字符类匹配abc- 4个字符

    • 需要将-写在最前面,如[-abc],不写在最前面均表示范围
  • 如何匹配小括号()

    • 若匹配的字面值含有()需要将其用中括号括起表示一个字符类

      例如:想匹配(需要写成[(]

Scrapy 爬虫框架

1
2
3
4
5
6
7
创建项目:scrapy startproject xxx
进入项目:cd xxx #进入某个文件夹下
创建爬虫:scrapy genspider xxx(爬虫名) xxx.com (爬取域)
生成文件:scrapy crawl xxx -o xxx.json (生成某种类型的文件)
运行爬虫:scrapy crawl XXX
列出所有爬虫:scrapy list
获得配置信息:scrapy settings [options]

框架流程

**【1】**创建一个scrpy框架项目

cmd命令行输入

1
scrapy startproject [dirname]

会在当前目录生成一个[dirname]的文件夹

该文件夹目录结构为

  • scrapy.cfg:项目的配置文件。
  • [dirname]/:项目的大本营。
  • [dirname]/items.py:定义项目中需要抓取并需要后期处理的数据。
  • [dirname]/middlewares.py:项目中的中间件(自定义扩展下载功能的组件)。
  • [dirname]/pipelines.py:用于存放执行后期数据处理的功能,从而使得数据的爬取的处理分开
  • [dirname]/settings.py:项目的设置文件,配置Scrapy。可以设置user-agent,爬取时间间隔,设置代理等等
  • [dirname]/spiders/:放置爬虫代码的目录。
settings.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# robots协议
ROBOTSTXT_OBEY = True # / False

# 显示指定类型的日志信息
LOG_LEVEL = 'ERROR' # 只显示错误信息,运行时不会太多无关信息

# User-Agent 头部
USER-AGENT = ''

# 开启管道 pipelines.py
ITEM_PIPELINES = {
'类名' = 300, # 数值表示优先级,越小越高
# 可以在管道里写多个处理类
}

# 指定图片存储路径
IMAGES_STORE = './imgs'

# 开启中间件
# 开启爬虫中间件
SPIDER_MIDDLEWARES ={
'类名' = 543, # 同管道类
}
# 开启下载中间件
DOWNLOADER_MIDDLEWARES ={
'类名' = 543, # 同管道类
}
pipelines.py

需要在setting内开启管道配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class xxx():
fp = None

# 重写方法,该方法在开始爬虫前调用一次
def open_spider(self,spider):
print('开始爬虫...')
self.fp = open('./resu.txt','w',encoding='utf-8')

# 重写方法 专门用来处理item类型对象
# 接收spiders提交过来的item对象
# 每接收到一个item都会被调用一次
def process_item(self, item, spider):
author = item['author']
content = item['content']
self.fp.write(author + ':' + content + '\n')

return item # 如果有下一个管道处理类,会将item返回给下一个管道处理类

# 重写方法,结束爬虫后调用一次
def close_spider(self, spider):
print('爬虫结束...')
self.fp.close()
middlewares.py

中间件 使用前需要在settings.py中开启

  • MiddleproSpiderMiddleware(obgect)爬虫中间件

  • MiddleproDownloaderMiddleware(obgect)下载中间件

    • from_crawler 返回下载中间件实例化对象

    • process_request 用来拦截处理请求

      • 可以写UA伪装

        1
        2
        request.headers['User-Agent'] = ''	# 可以封装UA池
        request.meta['proxy'] = '' # 使用代理
    • process_response 用于拦截处理响应

    • process_exception 用于拦截发生异常请求

      • 一般写代理ip(可以写到request函数)

        1
        # 代理池
    • spider_opend 打印日志

**【2】**定义Items.py

Items是将要装载抓取的数据的容器,它工作方式像python里面的字典,但它提供更多的保护,比如对未定义的字段填充以防止拼写错误。

它通过创建一个scrapy.item.Item类来声明,定义它的属性为scrpy.item.Field对象,就像是一个对象关系映射(ORM).
我们通过将需要的item模型化,来控制从dmoz.org获得的站点数据,比如我们要获得站点的名字,url和网站描述,我们定义这三种属性的域。要做到这点,我们编辑在tutorial目录下的items.py文件,我们的Item类将会是这样

示例

1
2
3
4
5
from scrapy.item import Item, Field 
class DmozItem(Item):
title = Field()
link = Field()
desc = Field()

**【3】**在[dirname]/spiders/创建python文件

根目录输入命令,会自动创建

1
scrapy genspider [name] [start_url]

在里面编写爬虫类Spider,Spider是用户编写的用于从网站上爬取数据的类

  • 创建一个自定义的Spider时,必须继承scrapy.Spider类,且定义以下三个属性:
    • name:用于区别不同的Spider该名字必须是唯一的,不可以为不同的Spider设定相同的名字。

    • start_requests:包含了Spider在启动时进行爬取的URL列表。该函数必须返回Requests对象或者其生成器。

      • # Requests对象属性
        print(type(response))  # scrapy.http.response.html.HtmlResponse
        print(type(response.text))  # str
        print(type(response.body))  # bytes
        print(response.encoding)  # utf-8
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
          
        - parse():这是Spider的一个默认回调函数。当下载器返回Response的时候,parse()函数就会被调用,每个初始URL完成下载后生成的Response对象将会作为唯一的参数传递给parse()函数。该方法负责解析返回的数据(response data),提取数据(生成item)以及生成需要进一步处理的URL的Request对象。

        #### **【4】**开始爬取

        在scrapy项目根目录执行命令`scrapy crawl [obj_name]`,其中`obj_name`是python文件自定义类里的name值

        也可以在项目根目录创建`main.py`文件

        ```python
        from scrapy.cmdline import execute
        execute("scrapy crawl [obj_name]".split())
1
2
3
4
# 命令行执行
scrapy crawl [obj_name]
scrapy crawl [obj_name] -o ./[file_name].csv
#或其他格式 json csv xml pickle

Scrapy shell

根目录执行命令

  • 加载网页(参数url需要加双引号)

    1
    scrapy shell url

    加载网页成功后,response回应存储在本地变量response中

    • response.body :获得网页html代码
    • response.headers :获得网页的header部分信息

Selector选择器

选择器包scrapy.selector.Selector

  • response.selector.xpath():xPath选择器;也可省略写成.xpath():会返回一系列selectors
  • response.selector.css():CSS选择器;也可省略写成.css():会返回一系列selectors
  • response.selector.extract():返回一个unicode字符串,为选中的数据
  • response.selector.re():正则表达式选择器,返回字符串

请求传参

爬取的内容不再同一页面,

用parse函数最后yield scrapy.http.Request来调用一个更深的页面(自己定义函数)

图片爬取

管道类需要继承ImagesPipeLine

使用前需要先导入包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from scrapy.pipelines.images import ImagesPipeline

class imgsPipleLine(ImagesPipeline):

# 重写 根据图片地址进行图片数据请求,传入图片src地址
def get_media_requests(self, item, info):
yield scrapy.Request(item['src'])

# 方法重写 指定图片存储的路径
def file_path(self, request, response=None, info=None):
imgName = '' # 指定每个图片名称
return imgName

def item_completed(self, results, item, info):
return item # 赶回给下一个即将被执行的管道类

需要在settings.py中指定图片存储路径

selenium

基于浏览器 自动化测试模块

selenium-python中文文档 (python-selenium-zh.readthedocs.io)

安装

1
python -m pip install selenium

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from selenium import webdriver
# 4 自定义设置chrome路径,并用chromedriver控制
options = webdriver.ChromeOptions()

######## 如果想隐藏浏览器窗口(无可视化界面),添加下面两行代码
options.add_argument('--headless')
options.add_argument('--disable-gpu')
########

######## 规避网站检测selenium
options.add_experimental_option('excludeSwitches',['enable-automation'])
########

# chrome可执行文件的路径,需要修改chrome文件名
options.binary_location = r'F:\palmer\MiniSoftware\RunningCheeseChrome\App\chrom.exe'
bro = webdriver.Chrome(executable_path='./chromedriver.exe', chrome_options=options)
bro.get('https://www.baidu.com')

webdriver.Chrome类对象

属性 含义
.page_source 获取浏览器当前页面的html源码数据
方法 含义
.get(url) 打开网址
.quit() 关闭浏览器
.close() 关闭当前标签
.find_element_by_xpath() 使用xpath来寻找元素
.find_element_by_id/name() 通过属性值id或者name值来寻找
.execute_script() 执行js代码
.back() 网页后退
.dorward() 网页前进

WebElement类对象

属性/方法 含义
.get_attribute() 得到标签属性的值
.send_keys() 写入内容

定位元素

  • 根据Id属性值定位:返回第一个name属性匹配的元素,如果没有元素匹配,会抛出NoSuchElementException异常。

    • login_form = driver.find_element_by_id('loginForm')
      
      1
      2
      3
      4
      5

      - 根据Name属性值定位:同上

      - ```python
      continue = driver.find_element_by_name('continue')
  • 使用XPath定位:

    • clear_button = driver.find_element_by_xpath("")
      
      1
      2
      3
      4
      5
      6

      - 使用链接文本定位超链接:

      - ```python
      continue_link = driver.find_element_by_link_text('Continue')
      continue_link = driver.find_element_by_partial_link_text('Conti')
    • 参数即为 a标签内的文字

  • 标签名定位

    • heading1 = driver.find_element_by_tag_name('h1')
      
      1
      2
      3
      4
      5

      - class定位

      - ```python
      content = driver.find_element_by_class_name('content')
    • 只返回匹配的第一个

  • css选择器定位

    • content = driver.find_element_by_css_selector('p.content')
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21

      ### 等待事件

      #### 显式等待

      显式的`waits`等待一个确定的条件触发然后才进行更深一步的执行。最糟糕的的做法是`time.sleep()`,这指定的条件是等待一个指定的时间段。 这里提供一些便利的方法让你编写的代码只等待需要的时间,`WebDriverWait`结合`ExpectedCondition`是一种实现的方法:

      ```python
      from selenium import webdriver
      from selenium.webdriver.common.by import By
      from selenium.webdriver.support.ui import WebDriverWait
      from selenium.webdriver.support import expected_conditions as EC

      driver = webdriver.Firefox()
      driver.get("http://somedomain/url_that_delay_loading")
      try:
      element = WebDriverWait(driver,10).until(
      EC.presence_of_element_located((By.ID,"myDynamicElement"))
      )
      finally:
      driver.quit()

这段代码会等待10秒,如果10秒内找到元素则立即返回,否则会抛出TimeoutException异常,WebDriverWait默认每500毫秒调用一下ExpectedCondition直到它返回成功为止。ExpectedCondition类型是布尔的,成功的返回值就是true,其他类型的ExpectedCondition成功的返回值就是 not null

隐式等待

当我们要找一个或者一些不能立即可用的元素的时候,隐式waits会告诉WebDriver轮询DOM指定的次数,默认设置是0次。一旦设定,WebDriver对象实例的整个生命周期的隐式调用也就设定好了。

1
2
3
4
5
6
from selenium import webdriver

driver = webdriver.Firefox()
driver.implicitly_wait(10) # 设置隐式等待 最大等待时间
driver.get("http://somedomain/url_that_delays_loading")
myDynamicElement = driver.find_element_by_id('myDynamicElement')

行为链

更多函数查阅7. WebDriver API — Selenium Python Bindings 2 documentation (selenium-python.readthedocs.io)

1
2
3
4
5
6
from selenium.webdriver import ActionChains	#导入类
action = ActionChains(bro) #初始化类,bro为浏览器对象
action.click_and_hold(div) #点击并拖住,div为查找的一个元素
action.move_by_offset(x,y).perform() #移动x,y像素,perform立即执行

action.release() # 释放动作链

frame切换/窗口切换

  • 需要定位iframe标签内部标签(子页面)

    • 需要先切换到子页面

    • bro.switch_to.frame([id])	#先切换作用域再定位
      
      1
      2
      3
      4
      5

      - 切换回主页面

      - ```python
      wd.switch_to.default_content()
  • 切换到新窗口

    • for handle in wd.window_handles:
          # 先切换到该窗口
          wd.switch_to.window(handle)
          # 得到该窗口的标题栏字符串,判断是不是我们要操作的那个窗口
          if '窗口标题字符串' in wd.title:
              # 如果是,那么这时候WebDriver对象就是对应的该该窗口,正好,跳出循环,
              break
      
      1
      2
      3
      4
      5
      6
      7
      8

      - 切换回旧窗口:先保存旧窗口句柄,然后再切换

      - ```python
      # mainWindow变量保存当前窗口的句柄
      mainWindow = wd.current_window_handle
      #通过前面保存的老窗口的句柄,自己切换到老窗口
      wd.switch_to.window(mainWindow)

选择框

radio 单选框

1
2
3
4
5
<div id="s_radio">
<input type="radio" name="teacher" value="小江老师">小江老师<br>
<input type="radio" name="teacher" value="小雷老师">小雷老师<br>
<input type="radio" name="teacher" value="小凯老师" checked="checked">小凯老师
</div>
1
2
3
4
5
6
7
8
# 获取当前选中的元素
element = wd.find_element_by_css_selector(
'#s_radio input[checked=checked]')
print('当前选中的是: ' + element.get_attribute('value'))

# 点选 小雷老师
wd.find_element_by_css_selector(
'#s_radio input[value="小雷老师"]').click()

checkbox 复选框

1
2
3
4
5
<div id="s_checkbox">
<input type="checkbox" name="teacher" value="小江老师">小江老师<br>
<input type="checkbox" name="teacher" value="小雷老师">小雷老师<br>
<input type="checkbox" name="teacher" value="小凯老师" checked="checked">小凯老师
</div>
1
2
3
4
5
6
7
8
9
10
# 先把 已经选中的选项全部点击一下
elements = wd.find_elements_by_css_selector(
'#s_checkbox input[checked="checked"]')

for element in elements:
element.click()

# 再点击 小雷老师
wd.find_element_by_css_selector(
"#s_checkbox input[value='小雷老师']").click()

select

  • select_by_value:根据选项的 value属性值,选择元素。

    • # <option value="foo">Bar</option>
      
      s.select_by_value('foo')
      
      1
      2
      3
      4
      5
      6
      7
      8
      9

      - `select_by_index`:根据选项的 **次序**(从0开始),选择元素

      - `select_by_visible_text`:根据选项的 **可见文本**,选择元素

      - ```python
      # <option value="foo">Bar</option>

      s.select_by_visible_text('Bar')
  • deselect_by_value :根据选项的value属性值, 去除选中元素

  • deselect_by_index :根据选项的次序,去除选中元素

  • deselect_by_visible_text :根据选项的可见文本,去除选中元素

  • deselect_all :去除选中所有元素

select 单选下拉框
1
2
3
4
5
6
7
8
# 导入Select类
from selenium.webdriver.support.ui import Select

# 创建Select对象
select = Select(wd.find_element_by_id("ss_single"))

# 通过 Select 对象选中小雷老师
select.select_by_visible_text("小雷老师")
select 多选框

多选框同

图形界面

pyside2

安装

1
python -m pip install pyside2

控件

按钮

QPushButton 就是常见的按钮

信号:被点击

当按钮被点击就会发出 clicked 信号,可以这样指定处理该信号的函数

1
button.clicked.connect(handleCalc)
方法:改变文本

代码中可以使用 setText 方法来改变按钮文本,比如

1
button.setText(text)
方法:禁用、启用

所有控件(继承自QWidget类)都支持 禁用和启用方法。

禁用后,该控件不再处理用户操作

  • 禁用
1
button.setEnabled(False)
  • 启用
1
button.setEnabled(True)
单行文本框

QLineEdit 是只能单行编辑的文本框。

信号:文本被修改

当文本框中的内容被键盘编辑,被点击就会发出 textChanged 信号,可以这样指定处理该信号的函数

1
edit.textChanged.connect(handleTextChange)
信号:按下回车键

当用户在文本框中任何时候按下回车键,就会发出 returnPressed 信号。

有时我们需要处理这种情况,比如登录界面,用户输完密码直接按回车键就进行登录处理,比再用鼠标点击登录按钮快捷的多。

可以指定处理 returnPressed 信号,如下所示

1
passwordEdit.returnPressed.connect(onLogin)
方法:获取文本

通过 text 方法获取编辑框内的文本内容,比如

1
text = edit.text()
方法:设置提示

通过 setPlaceholderText 方法可以设置提示文本内容,比如

1
edit.setPlaceholderText('请在这里输入URL')
方法:设置文本

通过 setText 方法设置编辑框内的文本内容为参数里面的文本字符串,比如

1
edit.setText('你好,白月黑羽')

原来的所有内容会被清除

方法:清除所有文本

clear 方法可以清除编辑框内所有的文本内容,比如

1
edit.clear()
方法:拷贝文本到剪贴板

copy 方法可以拷贝当前选中文本到剪贴板,比如

1
edit.copy()
方法:粘贴剪贴板文本

paste 方法可以把剪贴板内容,拷贝到编辑框当前光标所在处,比如

1
edit.paste()
多行纯文本框

QPlainTextEdit 是可以 多行 的纯文本编辑框。

信号:文本被修改

当文本框中的内容被键盘编辑,被点击就会发出 textChanged 信号,可以这样指定处理该信号的函数

1
edit.textChanged.connect(handleTextChange)
信号:光标位置改变

当文本框中的光标位置变动,就会发出 cursorPositionChanged 信号,可以这样指定处理该信号的函数

1
edit.cursorPositionChanged.connect(handleChanged)
方法:获取文本

通过 toPlainText 方法获取编辑框内的文本内容,比如

1
text = edit.toPlainText()
获取选中文本
1
2
3
#### 获取 QTextCursor 对象
textCursor = edit.textCursor()
selection = textCursor.selectedText()
方法:设置提示

通过 setPlaceholderText 方法可以设置提示文本内容,比如

1
edit.setPlaceholderText('请在这里输入薪资表')
方法:设置文本

通过 setPlainText 方法设置编辑框内的文本内容 为参数里面的文本字符串,比如

1
2
edit.setPlainText('''你好,白月黑羽
hello byhy''')

原来的所有内容会被清除

方法:在末尾添加文本

通过 appendPlainText 方法在编辑框末尾添加文本内容,比如

1
edit.appendPlainText('你好,白月黑羽')

注意:这种方法会在添加文本后 自动换行

方法:在光标处插入文本

通过 insertPlainText 方法在编辑框末尾添加文本内容,比如

1
edit.insertPlainText('你好,白月黑羽')

注意:这种方法 不会 在添加文本后自动换行

方法:清除所有文本

clear 方法可以清除编辑框内所有的文本内容,比如

1
edit.clear()
方法:拷贝文本到剪贴板

copy 方法可以清除编辑框内所有的文本内容,比如

1
edit.copy()
方法:粘贴剪贴板文本

paste 方法可以把剪贴板内容,拷贝到编辑框当前光标所在处,比如

1
edit.paste()
文本浏览框

QTextBrowser只能查看文本 控件。

通常用来显示一些操作日志信息、或者不需要用户编辑的大段文本内容。

官网介绍

该控件 获取文本、设置文本、清除文本、剪贴板复制粘贴 等等, 都和上面介绍的 多行纯文本框是一样的。

下面我们主要讲解不同点

方法:在末尾添加文本

通过 append 方法在编辑框末尾添加文本内容,比如

1
textBrowser.append('你好,白月黑羽')

有时,浏览框里面的内容长度超出了可见范围,我们在末尾添加了内容,往往希望控件自动翻滚到当前添加的这行,

可以通过 ensureCursorVisible 方法来实现

1
2
textBrowser.append('你好,白月黑羽')
textBrowser.ensureCursorVisible()

注意:这种方法会在添加文本后 自动换行

方法:在光标处插入文本

通过 insertPlainText 方法在编辑框末尾添加文本内容,比如

1
edit.insertPlainText('你好,白月黑羽')

注意:这种方法 不会 在添加文本后自动换行

文本标签

QLabel 就是常见的标签

方法:改变文本

代码中可以使用 setText 方法来改变标签文本内容,比如

1
button.setText(text)
显示图片

QLabel可以用来显示图片,有时一个图片可以让界面好看很多

怎么用QLabel 显示图片呢?

可以在 Qt Designer上 属性编辑器 QLabel 栏 的 pixmap 属性设置中选择图片文件指定。

组合选择框

QComboBox 是组合选择框

信号:选项改变

如果用户操作修改了QComboBox中的选项就会发出 currentIndexChanged 信号,可以这样指定处理该信号的函数

1
cbox.currentIndexChanged.connect(handleSelectionChange)
方法:添加一个选项

代码中可以使用 addItem 方法来添加一个选项到 末尾 ,参数就是选项文本

1
cbox.addItem('byhy')
方法:添加多个选项

代码中可以使用 addItems 方法来添加多个选项到 末尾,参数是包含了多个选项文本的列表

1
cbox.addItems(['byhy','白月黑羽','python教程'])
方法:清空选项

代码中可以使用 clear 方法来清空选项,也就是删除选择框内所有的选项

1
cbox.clear()
方法:获取当前选项文本

代码中可以使用 currentText 方法来获取当前 选中的选项 的文本,比如

1
method = cbox.currentText()
列表

QListWidget 是列表控件

方法:添加一个选项

代码中可以使用 addItem 方法来添加一个选项到 末尾 ,参数就是选项文本

1
listWidget.addItem('byhy')
方法:添加多个选项

代码中可以使用 addItems 方法来添加多个选项到 末尾,参数是包含了多个选项文本的列表

1
listWidget.addItems(['byhy','白月黑羽','python教程'])
方法:删除一个选项

代码中可以使用 takeItem 方法来删除1个选项,参数是该选项所在行

1
listWidget.takeItem(1)

就会删除第二行选项

方法:清空选项

代码中可以使用 clear 方法来清空选项,也就是删除选择框内所有的选项

1
listWidget.clear()
方法:获取当前选项文本

currentItem 方法可以得到列表当前选中项对象(QListWidgetItem) ,再调用这个对象的 text 方法,就可以获取文本内容,比如

1
listWidget.currentItem().text()

就获取了 第1行,第1列 的单元格里面的文本。

1
listWidget.currentItem().text()
表格

QTableWidget 是表格控件

创建列 和 标题栏

我们可以通过 Qt designer 为一个表格创建列和对应的标题栏。

只需要双击 Qt designer 设计的窗体中的 表格控件, 就会出现这样的对话框。

标签栏中,点击左下角的加号,就可以为 添加一个列,并且设置标题栏名称。

方法:设置表格行数

代码中可以使用 setRowCount 方法来设置表格行数,比如

1
table.setRowCount(10)
方法:获取所有行数

代码中可以使用 rowCount 方法来获取表格所有的函数,比如

1
rowcount = table.rowCount()
方法:获取当前选中是第几行

代码中可以使用 currentRow 方法来获取当前选中是第几行,比如

1
currentrow = table.currentRow()

注意:行数是从0开始的, 第一行的行数是 0

方法:插入一行、删除一行

insertRow 方法可以在指定位置插入一行,比如

1
table.insertRow(0)

就插入一行到第 1 行这个位置, 表格原来第1行(包括原来的第1行)以后的内容,全部往下移动一行。

1
table.insertRow(2)

就插入一行到第 3 行这个位置, 表格原来第3行(包括原来的第3行)以后的内容,全部往下移动一行。

removeRow 方法可以删除指定位置的一行,比如

1
table.removeRow(0)

就删除第 1 行, 表格原来第1行以后的内容,全部往上移动一行。

1
table.removeRow(2)

就删除第 3 行, 表格原来第3行以后的内容,全部往上移动一行。

方法:清除/删除所有内容

clearContents 方法可以清除表格所有的内容,比如

1
table.clearContents()

清除后,仍然会留下表格栏

如果连表格栏都要删除,可以加上 setRowCount(0),像这样

1
2
table.clearContents()
table.setRowCount(0)
方法:获取单元格文本的内容

item 方法可以指定位置的单元格对象(QTableWidgetItem) ,再调用这个对象的 text 方法,就可以获取文本内容,比如

1
table.item(0,0).text()

就获取了 第1行,第1列 的单元格里面的文本。

1
table.item(2,4).text()

就获取了 第3行,第5列 的单元格里面的文本。

方法:设置单元格文本内容

qt表格的单元格内的内容对象 是一个 单元格对象 QTableWidgetItem 实例

如果单元格 没有被设置过 内容,可以这样

1
2
3
4
5
from PySide2.QtWidgets import QTableWidgetItem

item = QTableWidgetItem()
item.setText('白月黑羽')
table.setItem(row, 0, item)

也可以简写为

1
2
3
from PySide2.QtWidgets import QTableWidgetItem

table.setItem(row, 0, QTableWidgetItem('白月黑羽'))

如果单元格 已经被设置过 文本内容,

item 方法可以获取指定位置的 QTableWidgetItem ,再调用这个对象的 setText 方法,就可以设置单元格文本内容,比如

1
table.item(0,0).setText()

就设置了 第1行,第1列 的单元格里面的文本。

1
table.item(2,4).setText()

就设置了 第3行,第5列 的单元格里面的文本。

如果希望某个单元格为 只读,不允许修改,可以使用QTableWidgetItem对象的 setFlags 方法,像这样

1
2
3
4
5
6
from PySide2.QtWidgets import QTableWidgetItem
from PySide2.QtCore import Qt

item = QTableWidgetItem('白月黑羽')
item.setFlags(Qt.ItemIsEnabled) #### 参数名字段不允许修改
table.setItem(row, 0, item)

如果想文本内容 居中对齐,每个当对应的QTableWidgetItem 调用 setTextAlignment,如下

1
2
3
4
5
6
7
8
from PySide2.QtWidgets import QTableWidgetItem
from PySide2.QtCore import Qt

item = QTableWidgetItem()
item.setText('白月黑羽')
#### 文本居中
item.setTextAlignment(Qt.AlignHCenter)
table.setItem(row, 0, item)
方法:设定列宽、宽度自动缩放

Qt Designer 上目前没法拖拽设定 每个列的宽度,只能在代码中指定。

如下所示

1
2
3
4
5
#### 设定第1列的宽度为 180像素
table.setColumnWidth(0, 180)

#### 设定第2列的宽度为 100像素
table.setColumnWidth(1, 100)

如想让 表格控件宽度 随着父窗口的缩放自动缩放,可以

在 属性编辑器 中 勾选 HorizontalHeaderStretchLastSection

或者使用下面代码

1
table.horizontalHeader().setStretchLastSection(True)
信号:单元格内容改动

当用户修改了一个单元格的内容,会发出 cellChanged 信号,并且携带参数指明该单元格的行号和列号。

我们的代码可以对该信号进行相应的处理。

示例代码如下

1
2
3
4
5
6
7
8
9
def __init__(self):
#### 指定单元格改动信号处理函数
self.ui.table.cellChanged.connect(self.cfgItemChanged)


def cfgItemChanged(self,row,column):
#### 获取更改内容
cfgName = self.ui.table.item(row, 0).text() #### 首列为配置名称
cfgValue = self.ui.table.item(row, column).text()
单选按钮 和 按钮组

QRadioButton 是单选按钮

说明

同一个父窗口 里面的多个单选按钮,只能选中一项。

如果你有多组单选按钮, 每组都应该有不同的父控件,或者不同的Layout。

通常建议:多组单选按钮,放到不同的 按钮组 QButtonGroup 中,按钮组就是父控件

信号:选中状态改变

如果用户操作点击了按钮组 QButtonGroup 中的一个按钮, QButtonGroup 就会发出 buttonClicked 信号,可以这样指定处理该信号的函数

1
buttongroup.buttonClicked.connect(handleButtonClicked)

然后,在处理函数中调用QButtonGroup对象的 checkedButton() 函数,返回值就是被选中的按钮对象。

再调用这个返回的按钮对象的 text() 方法得到界面文本,就可以知道是哪个选项被选中了。

勾选按钮 和 按钮组

QCheckBox 是勾选按钮

说明

通常建议:多组勾选按钮,放到不同的 按钮组 QButtonGroup 中,按钮组就是父控件。

信号:选中状态改变

如果用户操作点击了按钮组 QButtonGroup 中的一个按钮, QButtonGroup 就会发出 buttonClicked 信号,可以这样指定处理该信号的函数

1
buttongroup.buttonClicked.connect(handleButtonClicked)

然后,在处理函数中调用QButtonGroup对象的 checkedButton() 函数,返回值就是被选中的按钮对象。

再调用这个返回的按钮对象的 text() 方法得到界面文本,就可以知道是哪个选项被选中了。

tab页控件

我们可以通过tab页控件把界面分为好几各页面

通过Qt designer 只需要拖拽控件到各个页面即可。

要修改tab页的标题,可以先点击该tab页,然后在属性编辑器currentTabText修改

tab页中布局Layout

如果我们要在tab页上布局, 你可能会在对象查看器总直接右键点击该tab,可以你会发现 右键菜单里面没有布局项。

这是 Qt designer 非常坑爹的地方,我当时足足花了一个小时才找到方法。

  1. 首先需要你在tab页上添加一个控件
  2. 然后点击 在对象查看器 右键点击上层 TabWidget ,这时,你就会发现有布局菜单了
进度条

QProgressBar 是进度条,如下图所示

说明

进度条也是一个常用的控件,当程序需要做一件比较耗费时间的任务(比如统计数据,下载文件等)时,可以用来向用户指示操作的进度。

而且有了进度显示,用户就知道应用程序仍在运行,并没有出问题。

QProgressBar进度条把每个进度称之为一个step(步骤)。

我们可以通过它的 setRange 方法设定步骤个数,比如

1
progressBar.setRange(0,5)

就设定了,进度分为5步。

然后,通过 setValue 方法,指定当前完成到了哪一步,比如

1
progressBar.setValue(3)

就表示完成了 3/5, 也就是 60%, 进度条就会显示60%的进度。

可以使用reset()将进度条倒退到开头。

有时候我们的任务没法知道完成了多少,比如下载一个未知大小的文件。

这时,可以把range 范围都设置为0,这样,进度条会显示忙碌指示符,而不是显示进度百分比。

下面是一个进度条程序的示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from PySide2.QtWidgets import QApplication, QMainWindow, QPushButton,  QProgressBar,QMessageBox
from time import sleep
from threading import Thread

class Stats():
def __init__(self):
self.window = QMainWindow()
self.window.resize(500, 400)
self.window.move(300, 300)

self.progressBar = QProgressBar(self.window)
self.progressBar.resize(300, 20)
self.progressBar.move(80, 30)
#### 进度是 0 - 5,
self.progressBar.setRange(0,5)

self.button = QPushButton('统计', self.window)
self.button.move(80, 80)

self.button.clicked.connect(self.handleCalc)

#### 统计进行中标记,不能同时做两个统计
self.ongoing = False

def handleCalc(self):
def workerThreadFunc():
self.ongoing = True
for i in range(1,6):
sleep(1)
#### 设置进度值
self.progressBar.setValue(i)
self.ongoing = False

if self.ongoing:
QMessageBox.warning(
self.window,
'警告','任务进行中,请等待完成')
return

#### 通常任务执行比较耗时,应该在新的线程中进行
#### 否则会阻塞主线程显示界面
worker = Thread(target=workerThreadFunc)
worker.start()

app = QApplication([])
stats = Stats()
stats.window.show()
app.exec_()

上面的代码,运行时,会有很多告警,因为我们在新线程中操作界面对象,容易出问题。

更合理的方法是通过信号,在线程之间传递信息,对界面的操作都在主线程中完成。

如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
from PySide2.QtWidgets import QApplication, QMainWindow, QPushButton,  QProgressBar,QMessageBox
from time import sleep
from threading import Thread
from PySide2.QtCore import Signal,QObject

#### 信号库
class SignalStore(QObject):
#### 定义一种信号
progress_update = Signal(int)
#### 还可以定义其他作用的信号

#### 实例化
so = SignalStore()

class Stats():
def __init__(self):
#### 连接信号到处理的slot函数
so.progress_update.connect(self.setProgress)

self.window = QMainWindow()
self.window.resize(500, 400)
self.window.move(300, 300)

self.progressBar = QProgressBar(self.window)
self.progressBar.resize(300, 20)
self.progressBar.move(80, 30)
#### 进度是 0 - 5,
self.progressBar.setRange(0,5)

self.button = QPushButton('统计', self.window)
self.button.move(80, 80)

self.button.clicked.connect(self.handleCalc)

#### 统计进行中标记,不能同时做两个统计
self.ongoing = False

def handleCalc(self):
def workerThreadFunc():
self.ongoing = True
for i in range(1,6):
sleep(1)
#### 发出信息,通知主线程进行进度处理
so.progress_update.emit(i)
self.ongoing = False

if self.ongoing:
QMessageBox.warning(
self.window,
'警告','任务进行中,请等待完成')
return

worker = Thread(target=workerThreadFunc)
worker.start()

#### 处理进度的slot函数
def setProgress(self,value):
self.progressBar.setValue(value)

app = QApplication([])
stats = Stats()
stats.window.show()
app.exec_()
数字输入框

QSpinBox 是数字输入框,可以输入或使用上下箭头选择数字

获取数字

通过 value 方法获取编辑框内的文本内容,比如

1
number = box.value()

注意:返回的是整数对象,不是字符串

方法:设置数字

通过 setValue 方法可以设置提示文本内容,比如

1
box.setValue(100)
日期控件

QDateEdit 类可以用来选择日期时间

获取日期

当用户点击日期时间控件并且选取了 日期和时间,后来程序要获取这个控件里面选定的日期时间,可以使用date方法获取日期对象。

如下所示

1
2
3
4
5
6
7
8
9
10
#### 返回 PySide2.QtCore.QDate 对象
qdate = dateEdit.date()

#### 可以转化为 指定格式的字符串
dateStr = qdate.toString('yyyy-MM-dd')

#### 也可以获取年月日 对应的数字 ,比如日期是2020年5月2号
year = qdate.year() #### 返回 2020
month = qdate.month() #### 返回 5
day = qdate.day() #### 返回 2

QDate 对象的具体说明参考官方文档

选择文件框

QFileDialog 类可以用来选择文件或者目录

选择目录

通过 getExistingDirectory 静态方法 选择目录。

该方法,第一个参数是父窗口对象,第二个参数是选择框显示的标题。

比如

1
2
3
from PySide2.QtWidgets import QFileDialog

filePath = QFileDialog.getExistingDirectory(self.ui, "选择存储路径")

返回值即为选择的路径字符串。

如果用户点击了 选择框的 取消选择按钮,返回 空字符串。

选择单个文件

如果你想弹出文件选择框,选择一个 已经存在 的文件,可以使用 QFileDialog 静态方法 getOpenFileName ,比如

1
2
3
4
5
6
7
8
from PySide2.QtWidgets import QFileDialog

filePath, _ = QFileDialog.getOpenFileName(
self.ui, #### 父窗口对象
"选择你要上传的图片", #### 标题
r"d:\\data", #### 起始目录
"图片类型 (*.png *.jpg *.bmp)" #### 选择类型过滤项,过滤内容在括号中
)

该方法返回值 是一个元组,第一个元素是选择的文件路径,第二个元素是文件类型,如果你只想获取文件路径即可,可以采用上面的代码写法。

如果用户点击了 选择框的 取消选择按钮,返回 空字符串。

如果你想弹出文件选择框,选择路径和文件名,来 保存一个文件 ,可以使用 QFileDialog 静态方法 getSaveFileName ,比如

1
2
3
4
5
6
7
8
from PySide2.QtWidgets import QFileDialog

filePath, _ = QFileDialog.getSaveFileName(
self.ui, #### 父窗口对象
"保存文件", #### 标题
r"d:\\data", #### 起始目录
"json类型 (*.json)" #### 选择类型过滤项,过滤内容在括号中
)
选择多个文件

如果要选择多个文件,使用 getOpenFileNames 静态方法

1
2
3
4
5
6
7
8
from PySide2.QtWidgets import QFileDialog

filePaths, _ = QFileDialog.getOpenFileNames(
self.ui, #### 父窗口对象
"选择你要上传的图片", #### 标题
r"d:\\data", #### 起始目录
"图片类型 (*.png *.jpg *.bmp)" #### 选择类型过滤项,过滤内容在括号中
)

上例中 filePaths 对应的返回值是一个列表,里面包含了选择的文件。

如果用户点击了 选择框的 取消选择按钮,返回 空列表。

库方法

random 库

函数/方法 参数 返回值/作用
random.choice() 一个序列 随机返回序列中的一个元素