Karen


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

python的迭代器、生成器、生成式

发表于 2018-08-22

迭代器

  • 迭代是Python最强大的功能之一,是访问集合元素的一种方式。

  • 迭代器是一个可以记住遍历的位置的对象。

  • 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

  • 迭代器有两个基本的方法:iter() 和 next()。

  • 字符串,列表或元组对象都可用于创建迭代器

可以直接作用于for循环的对象统称为可迭代对象:Iterable。

可以使用isinstance()判断一个对象是否是Iterable对象:

1
2
3
4
5
6
7
8
9
10
11
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False

生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。

把list、dict、str等Iterable变成Iterator可以使用iter()函数:

1
2
3
4
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

你可能会问,为什么list、dict、str等数据类型不是Iterator?

这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

生成器

1 第一种定义的方式:

  • 在 Python 中,使用了 yield 的函数被称为生成器(generator)。
  • 跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
  • 在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
  • 调用一个生成器函数,返回的是一个迭代器对象。

2 第二种定义方式:

  • 如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。
  • 生成器保存的不是数据而是一种算法
  • 在Python中,这种一边循环一边计算的机制,称为生成器:generator

要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

1
2
3
4
5
6
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>

如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:我们讲过,generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

所以,我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。

generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

##列表生成式

  • 列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。
1
2
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

####判断

for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:

1
2
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]

####还可以使用两层循环,可以生成全排列:

1
2
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

多个变量

1
2
3
4
5
6
7
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> for k, v in d.items():
... print(k, '=', v)
...
y = B
x = A
z = C
列表
1
2
3
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']

参考:

  1. https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143178254193589df9c612d2449618ea460e7a672a366000
  2. http://www.runoob.com/python3/python3-iterator-generator.html

博客中显示图片

发表于 2018-08-22
  1. 把主页配置文件_config.yml 里的post_asset_folder:这个选项设置为true

  2. 在你的hexo目录下执行这样一句话npm install hexo-asset-image --save,这是下载安装一个可以上传本地图片的插件,来自dalao:dalao的git

  3. 等待一小段时间后,再运行hexo n "xxxx"来生成md博文时,/source/_posts文件夹内除了xxxx.md文件还有一个同名的文件夹

  4. 最后在xxxx.md中想引入图片时,先把图片复制到xxxx这个文件夹中,然后只需要在xxxx.md中按照markdown的格式引入图片:

    ![你想输入的替代文字](xxxx/图片名.jpg)

    注意: xxxx是这个md文件的名字,也是同名文件夹的名字。只需要有文件夹名字即可,不需要有什么绝对路径。你想引入的图片就只需要放入xxxx这个文件夹内就好了,很像引用相对路径。

  5. 最后检查一下,hexo g生成页面后,进入public\2017\02\26\index.html文件中查看相关字段,可以发现,html标签内的语句是<img src="2017/02/26/xxxx/图片名.jpg">,而不是<img src="xxxx/图片名.jpg>。这很重要,关乎你的网页是否可以真正加载你想插入的图片。

数据分析———— 3

发表于 2018-08-22

数据加载、存储和文件格式

%a 星期的简写。如 星期三为Web
%A 星期的全写。如 星期三为Wednesday
%b 月份的简写。如4月份为Apr
%B 月份的全写。如4月份为April
%c: 日期时间的字符串表示。(如: 04/07/10 10:43:39)
%d: 日在这个月中的天数(是这个月的第几天)
%f: 微秒(范围[0,999999])
%H: 小时(24小时制,[0, 23])
%I: 小时(12小时制,[0, 11])
%j: 日在年中的天数 [001,366](是当年的第几天)
%m: 月份([01,12])
%M: 分钟([00,59])
%p: AM或者PM
%S: 秒(范围为[00,61],为什么不是[00, 59],参考python手册~_~)
%U: 周在当年的周数当年的第几周),星期天作为周的第一天
%w: 今天在这周的天数,范围为[0, 6],6表示星期天
%W: 周在当年的周数(是当年的第几周),星期一作为周的第一天
%x: 日期字符串(如:04/07/10)
%X: 时间字符串(如:10:43:39)
%y: 2个数字表示的年份
%Y: 4个数字表示的年份
%z: 与utc时间的间隔 (如果是本地时间,返回空字符串)
%Z: 时区名称(如果是本地时间,返回空字符串)
%%: %% => %

jira,禅道

数据分析—— 1

发表于 2018-08-20

数据分析是什么?

数据分析是基于商业目的,有目的的进行收集,整理,加工和分析数据,提炼有价信息的一个过程。

分为明确分析目的与框架,数据收集,数据处理,包括数据清洗和转换,数据分析,数据展现和报告6个阶段。

以下我们先来热身一些专业名词,放在开头也很好查阅。

伪随机数

伪随机数是用确定性的算法计算出来自[0,1]均匀分布的随机数序列。并不真正的随机,但具有类似于随机数的统计特征,如均匀性、独立性等。在计算伪随机数时,若使用的初值(种子)不变,那么伪随机数的数序也不变。伪随机数可以用计算机大量生成,在模拟研究中为了提高模拟效率,一般采用伪随机数代替真正的随机数。模拟中使用的一般是循环周期极长并能通过随机数检验的伪随机数,以保证计算结果的随机性。

矩阵

矩阵相乘最重要的方法是一般矩阵乘积。它只有在第一个矩阵的列数(column)和第二个矩阵的行数(row)相同时才有意义 。一般单指矩阵乘积时,指的便是一般矩阵乘积。一个m×n的矩阵就是m×n个数排成m行n列的一个数阵。由于它把许多数据紧凑的集中到了一起,所以有时候可以简便地表示一些复杂的模型。

布隆过滤器

布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难

BloomFilter算法

BloomFilter算法,是一种大数据排重算法。在一个数据量很大的集合里,能准确断定一个对象不在集合里;判断一个对象有可能在集合里,而且占用的空间不大。它不适合那种要求准确率很高的情况,零错误的场景。通过牺牲部分准确率达到高效利用空间的目的。

函数sum和cumsum

矩阵求和函数sum()和cumsum()函数。

矩阵

在数学中,矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合 [1] ,最早来自于方程组的系数及常数所构成的方阵。这一概念由19世纪英国数学家凯利首先提出。矩阵是高等代数学中的常见工具,也常见于统计分析等应用数学学科中。 [2] 在物理学中,矩阵于电路学、力学、光学和量子物理中都有应用;计算机科学中,三维动画制作也需要用到矩阵。 矩阵的运算是数值分析领域的重要问题。将矩阵分解为简单矩阵的组合可以在理论和实际应用上简化矩阵的运算。对一些应用广泛而形式特殊的矩阵,例如稀疏矩阵和准对角矩阵,有特定的快速运算算法。关于矩阵相关理论的发展和应用,请参考矩阵理论。在天体物理、量子力学等领域,也会出现无穷维的矩阵,是矩阵的一种推广。

笛卡尔坐标系

笛卡尔坐标系(Cartesian coordinates,法语:les coordonnées cartésiennes)就是直角坐标系和斜坐标系的统称。
相交于原点的两条数轴,构成了平面放射坐标系。如两条数轴上的度量单位相等,则称此放射坐标系为笛卡尔坐标系。两条数轴互相垂直的笛卡尔坐标系,称为笛卡尔直角坐标系,否则称为笛卡尔斜角坐标系。

exp函数

exp,高等数学里以自然常数e为底的指数函数,常数 e 的值约为 2.718282。

烟囱系统,数据孤岛

数据类型

在数据分析中,数据分为以下几种:

  1. 表格型数据,其中各列可能是不同的类型,比如字符串、数值、日期等。多表现为关系型数据库和文本文件中的数据
  2. 多维数组(矩阵)
  3. 通过关键列相互联系的多个表(比如mysql关系型数据库)
  4. 间隔平均或不平均的时间序列

有关数据分析的工具

  • Excel是最广泛应用的数据分析工具,其功能也很强大

  • python:

    python拥有活跃的科学计算社区,在学术领域和商业领域都有普遍的应用,用于数据科学,机器学习,web开发等多个方面。python作为一种胶水语言,能够轻松集成旧有算法和系统,不仅仅适用于研究和原型构建,同时也适用于构建生产系统

    在python做数据分析中,有一些非常重要的python库:

    1. NumPy(Numerical Python)
      这是python的科学计算的基础包

      • 快速⾼效的多维数组对象ndarray。
      • ⽤于对数组执⾏元素级计算以及直接对数组执⾏数学运算的函数。
      • ⽤于读写硬盘上基于数组的数据集的⼯具。
      • 线性代数运算、傅⾥叶变换,以及随机数⽣成。
      • 成熟的C API,⽤于Python插件和原⽣C、C++、Fortran代码访问NumPy的数据结构和计 算⼯具。
      • 对于数值型数据,NumPy数组在存储和处理数据时要⽐内置的Python数据结构⾼效得 多。
    2. pandas

      • pandas提供快速捷处结构化数据的数据结构和函数。
      • pandas兼具NumPy⾼性能的数组计算功能以及电⼦表格和关系型数据库(如SQL)灵活 的数据处理功能。它提供了复杂精细的索引功能,能更加便捷地完成重塑、切⽚和切块、 聚合以及选取数据⼦集等操作 。
      • 数据操作、准备、清洗是数据分析最重要的技能(耗时最⻓) 。
    3. matplotlib

      • 最流⾏的⽤于绘制图表和其它⼆维数据可视化的Python库
      • 适合创建出版物上⽤的图表
    4. IPython&jupyter

      • 适合探索⼯作流(探索、试错、重复)的过程,因为可以实时显示

      • IPython web notebook Jupyter notebook (⽀持40多种编程语⾔)

      • Jupyter notebook⽀持markdown和html

    5. Scipy

      • ⼀组专⻔解决科学计算中各种标准问题域的包的集合。
    6. scikit-learn
      • scikit-learn成为了Python的通⽤机器学习⼯具包
    7. statsmodels
      • statsmodels包含经典统计学和经济计量学的算法
1
2
3
4
5
常用模块引用惯例
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import statsmodels as sm

###Jupyter Notebook

notebook是Jupyter项⽬的重要组件之⼀,它是⼀个代码、⽂本(有标记或⽆标记)、数据可视化或其它输出的交互式⽂档。Python的Jupyter内核是使⽤IPython。

启动

1
$ jupyter notebook

​ 要新建⼀个notebook,点击按钮New,选择“Python3”或“conda[默认项]”。如果是第⼀次, 点击空格,输⼊⼀⾏Python代码。然后按Shift-Enter执⾏。 当保存notebook时(File⽬录下的Save and Checkpoint),会创建⼀个后缀名为.ipynb的⽂ 件。要加载存在的notebook,把它放到启动notebook进程的相同⽬录内。

1
2
3
4
查看当前目录
%pwd
查看当前目录下的文件
%ls

####在变量前后使用一个问号:显示对象或函数信息,两个问号:显示源代码

#####Jupyter键盘快捷键:

key

魔术命令

%timeit 测量任何Python语句,例如矩阵乘法的执行时间

魔术命令如下:

magic

集成Matplotlib

IPython在分析计算领域能够流⾏的原因之⼀是它⾮常好的集成了数据可视化和其它⽤户界⾯ 库,⽐如matplotlib

1
2
3
%matplotlib
import matplotlib.pyplot as plt
plt.plot(np.random.randn(50).cumsum())

​ 打印一张随机生成50个数求和的分析图

NumPy

NumPy是在⼀个连续的内存块中存储数据,独⽴于其他Python内置对象。NumPy的C语⾔编 写的算法库可以操作内存,⽽不必进⾏类型检查或其它前期⼯作。⽐起Python的内置序列, NumPy数组使⽤的内存更少。

​ NumPy是科学计算的基础包,特点是有快速⾼效的多维数组对象ndarray,⽤于对数组执⾏元素级计算以及直接对数组执⾏数学运算的函数,⽤于读写硬盘上基于数组的数据集的⼯具,它可以高效处理大数组的数据,在整个数组上执行复杂的矢量计算。

基于NumPy的算法要比纯Python效率高上数量级,且使用的内存更少。
NumPy的ndarray:⼀种多维数组对象

​ NumPy最重要的⼀个特点就是其N维数组对象(即ndarray),该对象是⼀个快速⽽灵活的⼤数据集容器。

创建ndarray
1
2
3
4
In [19]: data1 = [6, 7.5, 8, 0, 1]
In [20]: arr1 = np.array(data1)
In [21]: arr1
Out[21]: array([ 6. , 7.5, 8. , 0. , 1. ])
ndarray是⼀个通⽤的同构数据多维容器,所有元素必须是相同类型的
1
2
3
4
#  取维度⼤⼩
data.shape
# 取数据数据类型
data.dtype
嵌套序列(⽐如由⼀组等⻓列表组成的列表)将会被转换为⼀个多维数组
1
2
3
4
5
6
7
8
9
10
In [22]: data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]
In [23]: arr2 = np.array(data2)
In [24]: arr2
Out[24]:
array([[1, 2, 3, 4],
[5, 6, 7, 8]])
# 取维度
arr2.ndim
arr2.shape
arr2.dtype

​ 你可以利⽤这种数组对整块数据执⾏⼀些数学运算,其语法跟标量元素之间的运 算⼀样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
In [12]: import numpy as np
In [13]: data = np.random.randn(2, 3)
In [14]: data
Out[14]:
array([[-0.2047, 0.4789, -0.5194],
[-0.5557, 1.9658, 1.3934]])


In [15]: data * 10
Out[15]:
array([[ -2.0471, 4.7894, -5.1944],
[ -5.5573, 19.6578, 13.9341]])
In [16]: data + data
Out[16]:
array([[-0.4094, 0.9579, -1.0389],
[-1.1115, 3.9316, 2.7868]])
zeros和ones分别可以创建指定⻓度或形状的全0或全1数组。empty可以创建⼀个没有任何具 体值的数组
1
2
3
4
5
6
7
8
9
10
11
12
13
In [29]: np.zeros(10)
Out[29]: array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
In [30]: np.zeros((3, 6))
Out[30]:
array([[ 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0.]])
In [31]: np.empty((2, 3, 2))
np.ones(10)

# 生成15个元素的数组
In [32]: np.arange(15)
Out[32]: array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])

​ np.empty返回的都是⼀些未初始化的垃圾值

以下是关于这些函数的说明:

ndarray

ndarray的数据类型

ndarray的数据类型有float64/32/128,int64/32/128,他们之间可以互相转换,在省城ndarray时也可以指定dtype,如果不指定一般默认为int或float64

1
2
3
4
5
6
In [33]: arr1 = np.array([1, 2, 3], dtype=np.float64)
In [34]: arr2 = np.array([1, 2, 3], dtype=np.int32)
In [35]: arr1.dtype
Out[35]: dtype('float64')
In [36]: arr2.dtype
Out[36]: dtype('int32')
将浮点数转换成整数,则⼩数部分将会被截取删除
1
2
3
4
5
In [41]: arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1])
In [42]: arr
Out[42]: array([ 3.7, -1.2, -2.6, 0.5, 12.9, 10.1])
In [43]: arr.astype(np.int32)
Out[43]: array([ 3, -1, -2, 0, 12, 10], dtype=int32)

​ 调⽤astype总会创建⼀个新的数组(⼀个数据的备份)

NumPy数组的批量运算

​ 不⽤编写循环即可对数据执⾏批量运算。NumPy⽤户称其为矢量化(vectorization)。⼤⼩相 等的数组之间的任何算术运算都会将运算应⽤到元素级

1
2
3
4
5
6
7
8
9
10
11
12
13
In [51]: arr = np.array([[1., 2., 3.], [4., 5., 6.]])
In [52]: arr
Out[52]:
array([[ 1., 2., 3.],
[ 4., 5., 6.]])
In [53]: arr * arr
Out[53]:
array([[ 1., 4., 9.],
[ 16., 25., 36.]])
In [54]: arr - arr
Out[54]:
array([[ 0., 0., 0.],
[ 0., 0., 0.]])

数组与标量的算术运算会将标量值传播到各个元素

1
2
3
4
5
6
In [55]: 1 / arr
Out[55]:
array([[ 1.
, 0.5 , 0.3333],
[ 0.25 , 0.2 , 0.1667]])
In [56]: arr * 0.5

数组之间的⽐较会⽣成布尔值数组

1
2
3
4
5
6
7
8
9
In [57]: arr2 = np.array([[0., 4., 1.], [7., 2., 12.]])
In [58]: arr2
Out[58]:
array([[ 0., 4., 1.],
[ 7., 2., 12.]])
In [59]: arr2 > arr
Out[59]:
array([[False, True, False],
[ True, False, True]], dtype=bool)

索引和切⽚

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
In [60]: arr = np.arange(10)
In [61]: arr
Out[61]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [62]: arr[5]
Out[62]: 5
In [63]: arr[5:8]
Out[63]: array([5, 6, 7])

In [64]: arr[5:8] = 12
In [65]: arr
Out[65]: array([ 0, 1, 2, 3, 4, 12, 12, 12, 8, 9])
In [66]: arr_slice = arr[5:8]
In [67]: arr_slice
Out[67]: array([12, 12, 12])
In [68]: arr_slice[1] = 12345
In [69]: arr
Out[69]: array([0,1,2,3,4,12, 12345,12,8,9])

切⽚[ : ]会给数组中的所有值赋值

1
2
3
In [70]: arr_slice[:] = 64
In [71]: arr
Out[71]: array([ 0, 1, 2, 3, 4, 64, 64, 64, 8, 9])

ndarray切⽚的⼀份副本⽽⾮视图,就需要明确地进⾏复制操作,例如arr[5:8].copy()

1
2
3
4
5
6
7
8
In [72]: arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
In [73]: arr2d[2]
Out[73]: array([7, 8, 9])
# 两种⽅式⼀样
In [74]: arr2d[0][2]
Out[74]: 3
In [75]: arr2d[0, 2]
Out[75]: 3

多维数组中,如果省略了后⾯的索引,则返回对象会是⼀个维度低⼀点的ndarray

1
2
3
4
5
6
7
8
9
10
11
In [76]: arr3d = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
In [77]: arr3d
Out[77]:
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 7, 8, 9],
[10, 11, 12]]])
In [78]: arr3d[0]
Out[78]:
array([[1, 2, 3],
[4, 5, 6]])

标量值和数组都可以被赋值给arr3d[0]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
In [79]: old_values = arr3d[0].copy()
In [80]: arr3d[0] = 42
In [81]: arr3d
Out[81]:
array([[[42, 42, 42],
[42, 42, 42]],
[[ 7, 8, 9],
[10, 11, 12]]])
In [82]: arr3d[0] = old_values
In [83]: arr3d
Out[83]:
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 7, 8, 9],
[10, 11, 12]]])
In [84]: arr3d[1, 0]
Out[84]: array([7, 8, 9])

切⽚索引

1
2
3
4
5
6
7
8
9
In [90]: arr2d
Out[90]:
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
In [91]: arr2d[:2]
Out[91]:
array([[1, 2, 3],
[4, 5, 6]])

⼀次传⼊多个切⽚

第二个切片参数代表的是每一个子对象里的序列号

1
2
3
4
In [92]: arr2d[:2, 1:]
Out[92]:
array([[2, 3],
[5, 6]])

将整数索引和切⽚混合选取第⼆⾏的前两列

1
2
In [93]: arr2d[1, :2]
Out[93]: array([4, 5])

选择第三列的前两⾏

1
2
In [94]: arr2d[:2, 2]
Out[94]: array([3, 6])

“只有冒号”表示选取整个轴

1
2
3
4
5
6
7
8
9
10
11
In [95]: arr2d[:, :1]
Out[95]:
array([[1],
[4],
[7]])
In [96]: arr2d[:2, 1:] = 0
In [97]: arr2d
Out[97]:
array([[1, 0, 0],
[4, 0, 0],
[7, 8, 9]])

#####布尔型索引

假设我们有⼀个⽤于存储数据的数组以及⼀个存储姓名的数组(含有重复项)

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
In [98]: names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
In [99]: data = np.random.randn(7, 4)
In [100]: names
Out[100]:
array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'],
dtype='<U4')
In [101]: data
Out[101]:
array([[ 0.0929, 0.2817, 0.769 , 1.2464],
[ 1.0072, -1.2962, 0.275 , 0.2289],
[ 1.3529, 0.8864, -2.0016, -0.3718],
[ 1.669 , -0.4386, -0.5397, 0.477 ],
[ 3.2489, -1.0212, -0.5771, 0.1241],
[ 0.3026, 0.5238, 0.0009, 1.3438],
[-0.7135, -0.8312, -2.3702, -1.8608]])
In [102]: names == 'Bob'
Out[102]: array([ True, False, False, True, False, False, False], dtype=bool)
In [103]: data[names == 'Bob']
Out[103]:
array([[ 0.0929, 0.2817, 0.769 , 1.2464],
[ 1.669 , -0.4386, -0.5397, 0.477 ]])
In [104]: data[names == 'Bob', 2:]
Out[104]:
array([[ 0.769 , 1.2464],
[-0.5397, 0.477 ]])
In [105]: data[names == 'Bob', 3]
Out[105]: array([ 1.2464, 0.477 ])
要选择除”bob”以外的其他值,既可以使⽤不等于符号(!=),也可以通过~对条件进⾏否定(data[~(names == ‘Bob’)],data[data < 0] = 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
In [106]: names != 'Bob'
Out[106]: array([False, True, True, False, True, True, True], dtype=bool)

In [107]: data[~(names == 'Bob')]
Out[107]:
array([[ 1.0072, -1.2962, 0.275 , 0.2289],
[ 1.3529, 0.8864, -2.0016, -0.3718],
[ 3.2489, -1.0212, -0.5771, 0.1241],
[ 0.3026, 0.5238, 0.0009, 1.3438],
[-0.7135, -0.8312, -2.3702, -1.8608]])

In [110]: mask = (names == 'Bob') | (names == 'Will')
In [111]: mask
Out[111]: array([ True, False, True, True, True, False, False], dtype=bool)
In [112]: data[mask]
Out[112]:
array([[ 0.0929, 0.2817, 0.769 , 1.2464],
[ 1.3529, 0.8864, -2.0016, -0.3718],
[ 1.669 , -0.4386, -0.5397, 0.477 ],
[ 3.2489, -1.0212, -0.5771, 0.1241]])
In [113]: data[data < 0] = 0

In [114]: data
Out[114]:
array([[ 0.0929, 0.2817, 0.769 , 1.2464],[ 1.0072, 0., 0.275 , 0.2289],, 0.[ 1.3529, 0.8864, 0.],[ 1.669 , 0.[ 3.2489, 0., 0., 0., 0.477 ],, 0.1241],[ 0.3026, 0.5238, 0.0009, 1.3438],[ 0. , 0. , 0. , 0.]])

In [115]: data[names != 'Joe'] = 7
In [116]: data
Out[116]:
array([[ 7., 7., 7. , 7. ],[ 1.0072, 0., 0.275 , 0.2289],
[ 7.[ 7.[ 7., 7., 7., 7., 7., 7., 7., 7., 7., 7.],],],[ 0.3026, 0.5238, 0.0009, 1.3438],[ 0. , 0. , 0. , 0.]])

花式索引

​ 花式索引(Fancy indexing)是⼀个NumPy术语,它指的是利⽤整数数组进⾏索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
In [117]: arr = np.empty((8, 4))
In [118]: for i in range(8):
.....:
arr[i] = i
In [119]: arr
Out[119]:
array([[ 0., 0., 0., 0.],
[ 1., 1., 1., 1.],
[ 2., 2., 2., 2.],
[ 3., 3., 3., 3.],
[ 4., 4., 4., 4.],
[ 5., 5., 5., 5.],
[ 6., 6., 6., 6.],
[ 7., 7., 7., 7.]])

为了以特定顺序选取⾏⼦集,只需传⼊⼀个⽤于指定顺序的整数列表或ndarray即可

1
2
3
4
5
6
7
8
9
10
11
12
13
In [120]: arr[[4, 3, 0, 6]]
Out[120]:
array([[ 4., 4., 4., 4.],
[ 3., 3., 3., 3.],
[ 0., 0., 0., 0.],
[ 6., 6., 6., 6.]])

使⽤负数索引将会从末尾开始选取⾏
In [121]: arr[[-3, -5, -7]]
Out[121]:
array([[ 5., 5., 5., 5.],
[ 3., 3., 3., 3.],
[ 1., 1., 1., 1.]])

⼀次传⼊多个索引数组会有⼀点特别。它返回的是⼀个⼀维数组,其中的元素对应各个索引元组 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
In [122]: arr = np.arange(32).reshape((8, 4))
In [123]: arr
Out[123]:
array([[ 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]])
# 最终选出的是元素(1,0)、(5,3)、(7,1)和(2,2)
In [124]: arr[[1, 5, 7, 2], [0, 3, 1, 2]]
Out[124]: array([ 4, 23, 29, 10])
In [125]: arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]]
Out[125]:
array([[ 4, 7, 5, 6],

####数组转置和轴对换

转置是重塑的⼀种特殊形式,它返回的是源数据的视图(不会进⾏任何复制操作)

1
2
3
4
5
6
7
8
9
10
11
12
13
In [126]: arr = np.arange(15).reshape((3, 5))
In [127]: arr
Out[127]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
In [128]: arr.T
Out[128]:
array([[ 0, 5, 10],
[ 1, 6, 11],
[ 2, 7, 12],
[ 3, 8, 13],
[ 4, 9, 14]])
利⽤np.dot计算矩阵内积
1
2
3
4
5
6
7
8
9
10
11
12
13
14
In [129]: arr = np.random.randn(6, 3)
In [130]: arr
Out[130]:
array([[-0.8608, 0.5601, -1.2659],
[ 0.1198, -1.0635, 0.3329],
[-2.3594, -0.1995, -1.542 ],
[-0.9707, -1.307 , 0.2863],
[ 0.378 , -0.7539, 0.3313],
[ 1.3497, 0.0699, 0.2467]])
In [131]: np.dot(arr.T, arr)
Out[131]:
array([[ 9.2291, 0.9394, 4.948 ],
[ 0.9394, 3.7662, -1.3622],
[ 4.948 , -1.3622, 4.3437]])

对于⾼维数组,transpose需要得到⼀个由轴编号组成的元组才能对这些轴进⾏转置

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
In [132]: arr = np.arange(16).reshape((2, 2, 4))
In [133]: arr
Out[133]:
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7]],
[[ 8, 9, 10, 11],
[12, 13, 14, 15]]])
In [134]: arr.transpose((1, 0, 2))
Out[134]:
array([[[ 0, 1, 2, 3],
[ 8, 9, 10, 11]],
[[ 4, 5, 6, 7],
[12, 13, 14, 15]]])

In [135]: arr
Out[135]:
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7]],
[[ 8, 9, 10, 11],
[12, 13, 14, 15]]])

In [136]: arr.swapaxes(1, 2)
Out[136]:
array([[[ 0, 4],
[ 1, 5],
[ 2, 6],
[ 3, 7]],
[[ 8, 12],
[ 9, 13],
[10, 14],
[11, 15]]])
arr.swapaxes(0, 1)

https://blog.csdn.net/qq_21046135/article/details/71249195

通⽤函数(ufunc):快速的元素级数组函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
In [137]: arr = np.arange(10)
In [138]: arr
Out[138]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [139]: np.sqrt(arr)
Out[139]:
array([ 0.
, 1.
, 1.4142, 1.7321, 2.
, 2.2361, 2.4495,
2.6458, 2.8284, 3.
])
In [140]: np.exp(arr)
Out[140]:
array([
1.
,
2.7183,
7.3891,
20.0855,
54.5982,
148.4132, 403.4288, 1096.6332, 2980.958 , 8103.0839])

add或maximum接受2个数组(因此也叫⼆元(binary)ufunc),并返回⼀个结果数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
In [141]: x = np.random.randn(8)
In [142]: y = np.random.randn(8)
In [143]: x
Out[143]:
array([-0.0119, 1.0048, 1.3272, -0.9193, -1.5491, 0.0222, 0.7584,
-0.6605])
In [144]: y
Out[144]:
array([ 0.8626, -0.01 , 0.05 , 0.6702, 0.853 , -0.9559, -0.0235,
-2.3042])
In [145]: np.maximum(x, y)
Out[145]:
array([ 0.8626, 1.0048, 1.3272, 0.6702, 0.853 , 0.0222, 0.7584,
-0.6605])

返回浮点数数组的⼩数和整数部分

1
2
3
4
5
6
7
8
9
10
In [146]: arr = np.random.randn(7) * 5
In [147]: arr
Out[147]: array([-3.2623, -6.0915, -6.663 , 5.3731, 3.6182, 3.45 ,
5.0077])
In [148]: remainder, whole_part = np.modf(arr)
In [149]: remainder
Out[149]: array([-0.2623, -0.0915, -0.663 , 0.3731,
0.6182, 0.45 , 0.0077])
In [150]: whole_part
Out[150]: array([-3., -6., -6., 5., 3., 3., 5.])

####利⽤数组进⾏数据处理

​ ⽤数组表达式代替循环的做法,通常被称为⽮量化。⼀般来说,⽮量化数组运算要⽐等价的纯 Python⽅式快上⼀两个数量级(甚⾄更多)。 假设我们想要在⼀组值(⽹格型)上计算函数sqrt(x^2+y^2) np.meshgrid函数接受两个⼀维数组,并产⽣两个⼆维矩阵(对应于两个数组中所有的(x,y) 对)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
In [155]: points = np.arange(-5, 5, 0.01) # 1000 equally spaced points
In [156]: xs, ys = np.meshgrid(points, points)
In [157]: ys
Out[157]:
array([[-5. , -5. , -5. , ..., -5. , -5. , -5. ],
[-4.99, -4.99, -4.99, ..., -4.99, -4.99, -4.99],
[-4.98, -4.98, -4.98, ..., -4.98, -4.98, -4.98],
...,
[ 4.97, 4.97, 4.97, ..., 4.97, 4.97, 4.97],
[ 4.98, 4.98, 4.98, ..., 4.98, 4.98, 4.98],
[ 4.99, 4.99, 4.99, ..., 4.99, 4.99, 4.99]])
In [158]: z = np.sqrt(xs ** 2 + ys ** 2)
In [159]: z
Out[159]:
array([[ 7.0711, 7.064 , 7.0569, ..., 7.0499, 7.0569, 7.064 ],
[ 7.064 , 7.0569, 7.0499, ..., 7.0428, 7.0499, 7.0569],
[ 7.0569, 7.0499, 7.0428, ..., 7.0357, 7.0428, 7.0499],
...,
[ 7.0499, 7.0428, 7.0357, ..., 7.0286, 7.0357, 7.0428],
[ 7.0569, 7.0499, 7.0428, ..., 7.0357, 7.0428, 7.0499],
[ 7.064 , 7.0569, 7.0499, ..., 7.0428, 7.0499, 7.0569]])

#####matplotlib创建了这个⼆维数组的可视化

1
2
3
4
5
6
In [160]: import matplotlib.pyplot as plt
In [161]: plt.imshow(z, cmap=plt.cm.gray); plt.colorbar()
Out[161]: <matplotlib.colorbar.Colorbar at 0x7f715e3fa630>
In [162]: plt.title("Image plot of $\sqrt{x^2 + y^2}$ for a grid of values")
Out[162]: <matplotlib.text.Text at 0x7f715d2de748>
plt.show()
将条件逻辑表述为数组运算
1
2
3
In [165]: xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
In [166]: yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
In [167]: cond = np.array([True, False, True, True, False])

当cond中的值为True时,选取xarr的值,否则从yarr中选取

1
2
3
In [170]: result = np.where(cond, xarr, yarr)
In [171]: result
Out[171]: array([ 1.1, 2.2, 1.3, 1.4, 2.5])

假设有⼀个由随机数据组成的矩阵,你希望将所有正值替换为2,将所有负值替换为-2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
In [172]: arr = np.random.randn(4, 4)
In [173]: arr
Out[173]:
array([[-0.5031, -0.6223, -0.9212, -0.7262],
[ 0.2229, 0.0513, -1.1577, 0.8167],
[ 0.4336, 1.0107, 1.8249, -0.9975],
[ 0.8506, -0.1316, 0.9124, 0.1882]])
In [174]: arr > 0
Out[174]:
array([[False, False, False, False],
[ True, True, False, True],
[ True, True, True, False],
[ True, False, True, True]], dtype=bool)
In [175]: np.where(arr > 0, 2, -2)
Out[175]:
array([[-2, -2, -2, -2],
[ 2, 2, -2, 2],
[ 2, 2, 2, -2],
[ 2, -2, 2, 2]])

⽤常数2替换arr中所有正的值

1
2
3
4
5
6
7
8
9
10
11
12
13
In [176]: np.where(arr > 0, 2, arr)
Out[176]:
array([[-0.5031, -0.6223, -0.9212, -0.7262],
[ 2.
[ 2.
[ 2.
, 2.
, 2.
, -1.1577, 2. ],
, 2. , -0.9975],
, 2.
, -0.1316, 2.
]])

数学和统计⽅法

​ 可以通过数组上的⼀组数学函数对整个数组或某个轴向的数据进⾏统计计算。sum、mean以 及标准差std等聚合计算(aggregation,通常叫做约简(reduction))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
In [177]: arr = np.random.randn(5, 4)
In [178]: arr
Out[178]:
array([[ 2.1695, -0.1149, 2.0037, 0.0296],
[ 0.7953, 0.1181, -0.7485, 0.585 ],
[ 0.1527, -1.5657, -0.5625, -0.0327],
[-0.929 , -0.4826, -0.0363, 1.0954],
[ 0.9809, -0.5895, 1.5817, -0.5287]])
In [179]: arr.mean()
Out[179]: 0.19607051119998253
In [180]: np.mean(arr)
Out[180]: 0.19607051119998253
In [181]: arr.sum()
Out[181]: 3.9214102239996507

arr.mean(1)是“计算⾏的平均值”,arr.sum(0)是“计算每列的和”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
In [182]: arr.mean(axis=1)
Out[182]: array([ 1.022 , 0.1875, -0.502 , -0.0881, 0.3611])
In [183]: arr.sum(axis=0)
Out[183]: array([ 3.1693, -2.6345, 2.2381, 1.1486])
In [184]: arr = np.array([0, 1, 2, 3, 4, 5, 6, 7])
In [185]: arr.cumsum()
Out[185]: array([ 0, 1, 3, 6, 10, 15, 21, 28])
In [186]: arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
In [187]: arr
Out[187]:
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
# 所有元素的累积和
In [188]: arr.cumsum(axis=0)
Out[188]:
array([[ 0, 1, 2],
[ 3, 5, 7],
[ 9, 12, 15]])

⽤于布尔型数组的⽅法

1
2
3
In [190]: arr = np.random.randn(100)
In [191]: (arr > 0).sum()
Out[191]: 42

#####any⽤于测试数组中是否存在⼀个或多个True,⽽all则检查数组中所有值是否都是True,这两个 ⽅法也能⽤于⾮布尔型数组,所有⾮0元素将会被当做True

1
2
3
4
5
In [192]: bools = np.array([False, False, True, False])
In [193]: bools.any()
Out[193]: True
In [194]: bools.all()
Out[194]: False
排序
1
2
3
4
5
6
In [195]: arr = np.random.randn(6)
In [196]: arr
Out[196]: array([ 0.6095, -0.4938, 1.24 , -0.1357, 1.43 , -0.8469])
In [197]: arr.sort()
In [198]: arr
Out[198]: array([-0.8469, -0.4938, -0.1357, 0.6095, 1.24 , 1.43 ])

多维数组可以在任何⼀个轴向上进⾏排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
In [199]: arr = np.random.randn(5, 3)
In [200]: arr
Out[200]:
array([[ 0.6033, 1.2636, -0.2555],
[-0.4457, 0.4684, -0.9616],
[-1.8245, 0.6254, 1.0229],
[ 1.1074, 0.0909, -0.3501],
[ 0.218 , -0.8948, -1.7415]])
In [201]: arr.sort(1)
In [202]: arr
Out[202]:
array([[-0.2555, 0.6033, 1.2636],
[-0.9616, -0.4457, 0.4684],
[-1.8245, 0.6254, 1.0229],
[-0.3501, 0.0909, 1.1074],
[-1.7415, -0.8948, 0.218 ]])
唯⼀化以及其它的集合逻辑

找出数组中的唯⼀值并返回已排序的结果

1
2
3
4
5
6
7
8
In [206]: names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
In [207]: np.unique(names)
Out[207]:
array(['Bob', 'Joe', 'Will'],
dtype='<U4')
In [208]: ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])
In [209]: np.unique(ints)
Out[209]: array([1, 2, 3, 4])

函数np.in1d⽤于测试⼀个数组中的值在另⼀个数组中的成员资格,返回⼀个布尔型数组

1
2
3
In [211]: values = np.array([6, 0, 0, 3, 2, 5, 6])
In [212]: np.in1d(values, [2, 3, 6])
Out[212]: array([ True, False, False, True, True, False, True], dtype=bool)

数组的集合运算如下:

ndarray2

⽤于数组的⽂件输⼊输出

​ NumPy的内置⼆进制格式读写 np.save和np.load是读写磁盘数组数据的两个主要函数。默认情况下,数组是以未压缩的原始 ⼆进制格式保存在扩展名为.npy的⽂件中的

1
2
3
4
In [213]: arr = np.arange(10)
In [214]: np.save('some_array', arr)
In [215]: np.load('some_array.npy')
Out[215]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

通过np.savez可以将多个数组保存到⼀个未压缩⽂件中

将数据压缩,可以使⽤numpy.savez_compressed

线性代数

矩阵乘法的dot函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
In [223]: x = np.array([[1., 2., 3.], [4., 5., 6.]])
In [224]: y = np.array([[6., 23.], [-1, 7], [8, 9]])
In [225]: x
Out[225]:
array([[ 1., 2., 3.],
[ 4., 5., 6.]])
In [226]: y
Out[226]:
array([[ 6., 23.],
[ -1., 7.],
[ 8., 9.]])
In [227]: x.dot(y)
Out[227]:
array([[ 28., 64.],
[ 67., 181.]])

⼀个⼆维数组跟⼀个⼤⼩合适的⼀维数组的矩阵点积运算之后将会得到⼀个⼀维数组

1
2
In [229]: np.dot(x, np.ones(3))
Out[229]: array([ 6., 15.])

@符也可以⽤作中缀运算符,进⾏矩阵乘法

1
2
In [230]: x @ np.ones(3)
Out[230]: array([ 6., 15.])

伪随机数⽣成

⽤normal来得到⼀个标准正态分布的4×4样本数组

1
2
3
4
5
6
7
In [238]: samples = np.random.normal(size=(4, 4))
In [239]: samples
Out[239]:
array([[ 0.5732, 0.1933, 0.4429, 1.2796],
[ 0.575 , 0.4339, -0.7658, -1.237 ],
[-0.5367, 1.8545, -0.92 , -0.1082],
[ 0.1525, 0.9435, -1.0953, -0.144 ]])

Python内置的random模块则只能⼀次⽣成⼀个样本值。从测试结果中可以看出,如果 需要产⽣⼤量样本值,numpy.random快了不⽌⼀个数量级

numpy.random的数据⽣成函数使⽤了全局的随机种⼦。要避免全局状态,你可以使⽤ numpy.random.RandomState,创建⼀个与其它隔离的随机数⽣成器

部分numpy.random函数如下:

ndarray3

ndarray4

示例:

​ ⽤np.random模块⼀次性随机产⽣1000个“掷硬币”结果(即两个数中任选⼀个),将其分别设 置为1或-1,然后计算累计和

1
2
3
4
5
6
7
8
In [251]: nsteps = 1000
In [252]: draws = np.random.randint(0, 2, size=nsteps)
In [253]: steps = np.where(draws > 0, 1, -1)
In [254]: walk = steps.cumsum()
In [255]: walk.min()
Out[255]: -3
In [256]: walk.max()
Out[256]: 31

我们想要知道本次随机漫步需要多久才能距离初始0点⾄少10步远(任⼀⽅向均可)

1
2
In [257]: (np.abs(walk) >= 10).argmax()
Out[257]: 37

Django-分页、中间键

发表于 2018-08-17

> 分页功能

ticket

数据库的增加

删除

修改

自己实现登录注册

  1. 中间件Middleware

中间件:

a) 是一个轻量级的,底层的插件,可以介入Django的请求和响应的过程(面向切面编程)

b) 中间件的本质就是一个python类

c) 面向切面编程(Aspect Oriented Programming)简称AOP,AOP的主要实现目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获取逻辑过程中各部分之间低耦合的隔离效果

思考:

什么是中间件,在settings.py中有很多的中间件,主要是用来做什么功能的呢,他们处理请求的url的过程在那些阶段呢,一般用来做那些数据的处理呢

1.1 中间件的处理函数
__init__:没有参数,在服务器响应的第一个请求的时候自动调用,用户确定时候启动该中间件

process_request(self, request): 在执行视图前被调用,每个请求上都会被调用,不主动进行返回或返回HttpResponse对象

process_view(self, request, view_func,view_args, view_kwargs):调用视图之前执行,每个请求都会调用,不主动进行返回或返回HttpResponse对象

process_template_response(self, request, response):在视图刚好执行完后进行调用,每个请求都会调用,不主动进行返回或返回HttpResponse对象

process_response(self, request, response):所有响应返回浏览器之前调用,每个请求都会调用,不主动进行返回或返回HttpResponse对象

process_exception(self, request, exception):当视图抛出异常时调用,不主动进行返回或返回HttpResponse对象
1.2 处理流程

图

1.3 自定义中间件流程
  1. 在工程目录下创建middleware目录
  2. 目录中创建一个python文件
  3. 在根据功能需求,创建切入需求类,重写切入点方法
  4. from django.utils.deprecation import MiddlewareMixin
class AuthUserMiddleWare(MiddlewareMixin):

    def process_request(self, request):

        print(‘xxxx’)
  1. 启动中间件,在settings中进行配置,MIDDLEWARE中添加middleware.文件名.类名

图

2.分页

2.1 分页库Paginator的基本语法

django提供了分页的工具,存在于django.core中

Paginator: 数据分页工具
Page:具体的某一页

Paginator:

对象创建: Paginator(数据集,每一页数据)

属性:

count  计算和

num_pages: 页面总和

page_range: 页码列表,从1开始

方法:

page(页码):获取的一个page对象,页码不存在则抛出invalidPage的异常
2.2 常见错误
invalidPage:page()传递无效页码

PageNotAnInteger:Page()传递的不是整数

Empty:page()传递的值有效,但是没有数据
2.3 page对象

page:

对象获取,通过Paginator的page()方法获得

属性:

object_list: 当前页面上所有的数据对象
number: 当前页的页码值
paginator: 当前page关联的Paginator对象

方法:

has_next()   判断是否有下一页
has_previous():  判断是否有上一页
has_other_pages():  判断是否有上一页或下一页
next_page_number();  返回下一页的页码
previous_page_number(): 返回上一页的页码
len(): 返回当前也的数据的个数
2.4 Paginator对象和Page对象的关系

如下有学生表,学生表中有25条数据,通过分页,每一个6条数据,去实现分页的功能

图

2.5 代码

图

Django-模型

发表于 2018-08-17

##模型

前言

  Django对数据库提供了很好的支持,对不同的数据库,django提供了统一调用的API,我们可以根据不同的业务需求使用不同是数据库。Django中引入了ORM(Objects Relational Mapping)对象关系映射,是一种程序技术。在下面会详细的讲解。

修改mysql配置

1. 在settings.py中配置数据库连接信息
1
2
3
4
5
6
'ENGINE':'django.db.backends.mysql',
'NAME':'', #数据库名
'USER':'', #账号
'PASSWORD':'', #密码
'HOST':'127.0.0.1', #IP(本地地址也可以是localhost)
'PORT':'3306', #端口
2. mysql数据库中创建定义的数据库

a) 进入mysql

1
mysql -u root -p

b) 创建数据库

1
create database xxx charset=utf-8;

3. 配置数据库链接

a) 安装pymysql

1
pip install pymysql

b) 在工程目录下的init.py文件中输入,完成数据库的驱动加载

1
2
import pymysql
pymysql.install_as_MySQLdb()

4. 定义模型
重要概念:模型,表,属性,字段

一个模型类在数据库中对应一张表,在模型类中定义的属性,对应模型对照表中的一个字段

定义属性见定义属性文件地址

创建学生模型类
class Student(models.Model):

    s_name = models.CharField(max_length=10)
    s_age = models.IntegerField()
    s_gender = models.BooleanField()

    class Meta:
        db_table = 'cd_student'
        ordering =[]
    对象的默认排序字段,获取对象列表时使用,升序ordering['id'],降序ordering['-id']
5.迁移数据库

a) 生成迁移文件

1
python manage.py makemigrations

注意:如果执行后并没有生成迁移文件,一直提示No changes detected这个结果的话,就要手动的去处理了。有两点处理方式:

1) 先删除掉pycache文件夹

2) 直接强制的去执行迁移命令,python manage.py makemigrations xxx (xxx就是app的名称)

3) 查看自动生成的数据库,查看表django_migrations,删掉app字段为xxx的数据(xxx就是app的名称)

b) 执行迁移生成数据库

1
python manage.py migrate

注意: 生成迁移文件的时候,并没有在数据库中生成对应的表,而是执行migrate命令之后才会在数据库中生成表

6. ORM

ORM(Objects Relational Mapping)对象关系映射,是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。可以简单理解为翻译机。

图

7.模型查询
a) 模型成员objects

Django默认通过模型的objects对象实现模型数据查询

b) 过滤器

查询集表示从数据库获取的对象集合

查询集可以有多个过滤器

过滤器就是一个函数,基于所给的参数限制查询的结果

从SQL角度来说,查询集合和select语句等价,过滤器就像where条件

Django有两种过滤器用于筛选记录

    filter      : 返回符合筛选条件的数据集

    exclude   : 返回不符合筛选条件的数据集

多个filter和exclude可以连接在一起查询

当然还有如下这些过滤器:

all()            返回所有数据

filter()     返回符合条件的数据

exclude()        过滤掉符合条件的数据

order_by()       排序

values()         一条数据就是一个字典,返回一个列表
c) 查询单个数据

get():返回一个满足条件的对象。如果没有返回符合条件的对象,会应该模型类DoesNotExist异常,如果找到多个,会引发模型类MultiObjectsReturned异常

first():返回查询集中的第一个对象

last():返回查询集中的最后一个对象

count():返回当前查询集中的对象个数

exists():判断查询集中是否有数据,如果有数据返回True,没有返回False

d) 限制查询集

限制查询集,可以使用下表的方法进行限制,等同于sql中的limit

模型名.objects.all()[0:5] 小标不能为负数

e) 字段查询

对sql中的where实现,作为方法,filter(),exclude(),get()的参数

语法:属性名称__比较运算符 = 值

外键:属性名_id

注意:like语句中使用%表示通配符。比如sql语句查询 where name like ‘%xxx%’,等同于filter(name_contains=’xxx’)

f) 比较运算符

contains:是否包含,大小写敏感

startswith,endswith:以values开头或者结尾,大小写敏感
以上的运算符前加上i(ignore)就不区分大小写了

isnull,isnotnull:是否为空。filter(name__isnull=True)

in:是否包含在范围内。filter(id__in=[1,2,3])

gt,gte,lt,lte:大于,大于等于,小于,小于等于。filter(age__gt=10)

pk:代表主键,也就是id。filter(pk=1)

g) 聚合函数

agregate()函数返回聚合函数的值

Avg:平均值

Count:数量

Max:最大

Min:最小

Sum:求和

例如: Student.objects.aggregate(Max(‘age’))

h) F对象/Q对象

F对象:可以使用模型的A属性与B属性进行比较

背景:在模型中有两个字段,分别表示学生成绩A与成绩B,要对成绩AB进行比较计算,就需要使用到F对象。

例如有如下例子1:

班级中有女生个数字段以及男生个数字段,统计女生数大于男生数的班级可以如下操作:

grades = Grade.objects.filter(girlnum__gt=F('boynum'))

F对象支持算数运算

grades = Grade.objects.filter(girlnum__gt=F(‘boynum’) + 10)

例子2:

查询python班下语文成绩超过数学成绩10分的学生

    grade = Grade.objects.filter(g_name='python').first()
    students = grade.student_set.all()

    stu = students.filter(s_yuwen__gt= F('s_shuxue') + 10)

Q对象:

Q()对象就是为了将过滤条件组合起来

当我们在查询的条件中需要组合条件时(例如两个条件“且”或者“或”)时。我们可以使用Q()查询对象

使用符号&或者|将多个Q()对象组合起来传递给filter(),exclude(),get()等函数

Q()对象的前面使用字符“~”来代表意义“非”

例子1:

查询学生中不是12岁的或者姓名叫张三的学生

student = Student.objects.filter(~Q(age=12) | Q(name='张三'))

例子2:

查询python班语文小于80并且数学小于等于80的学生

grade = Grade.objects.filter(g_name='python').first()
students = grade.student_set.all()
stu = students.filter(~Q(s_yuwen__gte=80) & Q(s_shuxue__lte=80))

​
例子3:

查询python班语文大于等于80或者数学小于等于80的学生

grade = Grade.objects.filter(g_name='python').first()
students = grade.student_set.all()

stu = students.filter(Q(s_yuwen__gte=80) | Q(s_shuxue__lte=80))
8.模型字段定义属性

定义属性

概述
    ·django根据属性的类型确定以下信息
        ·当前选择的数据库支持字段的类型
        ·渲染管理表单时使用的默认html控件
        ·在管理站点最低限度的验证

    ·django会为表增加自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后,则django不会再生成默认的主键列

    ·属性命名限制
        ·遵循标识符规则(不使用python预定义的标识符号,内置函数名,异常等。避免使用下划线等)
        ·由于django的查询方式,不允许使用连续的下划线



库
    ·定义属性时,需要字段类型,字段类型被定义在django.db.models.fields目录下,为了方便使用,被导入到django.db.models中

    ·使用方式
        ·导入from django.db import models
        ·通过models.Field创建字段类型的对象,赋值给属性


逻辑删除
    ·对于重要数据都做逻辑删除,不做物理删除,实现方法是定义isDelete属性,类型为BooleanField,默认值为False


字段类型
    ·AutoField
        ·一个根据实际ID自动增长的IntegerField,通常不指定如果不指定,一个主键字段将自动添加到模型中

    ·CharField(max_length=字符长度)
        ·字符串,默认的表单样式是 TextInput

    ·TextField
        ·大文本字段,一般超过4000使用,默认的表单控件是Textarea

    ·IntegerField
        ·整数

    ·DecimalField(max_digits=None, decimal_places=None)
        ·使用python的Decimal实例表示的十进制浮点数
        ·参数说明
            ·DecimalField.max_digits
                ·位数总数
            ·DecimalField.decimal_places
                ·小数点后的数字位数

    ·FloatField
        ·用Python的float实例来表示的浮点数

    ·BooleanField
        ·true/false 字段,此字段的默认表单控制是CheckboxInput

    ·NullBooleanField
        ·支持null、true、false三种值

    ·DateField([auto_now=False, auto_now_add=False])
        ·使用Python的datetime.date实例表示的日期
        ·参数说明
            ·DateField.auto_now
                ·每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为false
            ·DateField.auto_now_add
                ·当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为false
        ·说明
            ·该字段默认对应的表单控件是一个TextInput. 在管理员站点添加了一个JavaScript写的日历控件,和一个“Today"的快捷按钮,包含了一个额外的invalid_date错误消息键
        ·注意
            ·auto_now_add, auto_now, and default 这些设置是相互排斥的,他们之间的任何组合将会发生错误的结果

    ·TimeField
        ·使用Python的datetime.time实例表示的时间,参数同DateField

    ·DateTimeField
        ·使用Python的datetime.datetime实例表示的日期和时间,参数同DateField

    ·FileField
        ·一个上传文件的字段

    ·ImageField
        ·继承了FileField的所有属性和方法,但对上传的对象进行校验,确保它是个有效的image


字段选项
    ·概述
        ·通过字段选项,可以实现对字段的约束
        ·在字段对象时通过关键字参数指定

    ·null
        ·如果为True,则该字段在数据库中是空数据,默认值是 False

    ·blank
        ·如果为True,则该字段允许为空白,默认值是 False

    ·注意
        ·null是数据库范畴的概念,blank是表单验证证范畴的

    ·db_column
        ·字段的名称,如果未指定,则使用属性的名称

    ·db_index
        ·若值为 True, 则在表中会为此字段创建索引

    ·default
        ·默认值

    ·primary_key
        ·若为 True, 则该字段会成为模型的主键字段

    ·unique
        ·如果为 True, 这个字段在表中必须有唯一值

模型对应关系描述如下:

1:1   一对一 OneToOneField
1:N   一对多 ForeignKey
M:N   多对多 ManyToManyField
常见的几种数据关系,django都提供了很好的支持

1. 一对一

1.1 模型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
创建学生的模型:
class Student(models.Model):
stu_name = models.CharField(max_length=6, unique=True)
stu_sex = models.BooleanField(default=0)
stu_birth = models.DateField()
stu_delete = models.BooleanField(default=0)
stu_create_time = models.DateField(auto_now_add=True)
stu_operate_time = models.DateField(auto_now=True)
stu_tel = models.CharField(max_length=11)
stu_yuwen = models.DecimalField(max_digits=3, decimal_places=1, default=0)
stu_shuxue = models.DecimalField(max_digits=3, decimal_places=1, default=0)

class Meta:
db_table = 'stu'

创建学生拓展的模型:
class StuInfo(models.Model):

stu_addr = models.CharField(max_length=30)
stu_age = models.IntegerField()
stu = models.OneToOneField(Student)

class Meta:
db_table = 'stu_info'
使用models.OneToOneField()进行关联
class StuInfo(models.Model):下是通过班级获取学生信
    stu = models.OneToOneField(Student)
注意:在数据中关联字段名称叫stu_id
1.2 通过学生拓展表去获取学生信息
1
2
stuinfo = StuInfo.objects.all().first()
student = stuinfo.stu
注意:通过拓展表去获取学生的信息的话,语法如下; 学生拓展表的单条对象.关联字段,即可获取到学生表的数据
1.3 通过学生获取人信息1
1
2
stu = Student.objects.all().first()
stuInfo = stu.stuInfo
注意:通过学生获取关联表的数据的话,语法如下: 学生对象.关联的表名,即可获取到关联表的数据
1.3.1 通过学生获取人信息2
在关联字段OneToOneField中加入参数related_name='xxx'
1
2
3
在
stu = Student.objects.all().first()
stuInfo = stu.xxx
注意:通过学生获取关联表的数据的话,语法如下: 学生对象.关联的字段中定义的related_name参数,即可获取到关联表的数据
1.4 设置对应关系的字段为保护模式 :
models.CASCADE                      默认值
models.PROTECT                    保护模式
models.SET_NULL                 置空模式
models.SET_DETAULT          置默认值
models.SET()     删除的时候吃重新动态指向一个实体访问对象元素
on_delete = models.PROTECT

1
2
修改on_delete参数
models.OneToOneField('Student', on_delete=models.SET_NULL, null=True)
在删除student对象的时候,stuinfo的关联字段会设置为空null=True,如下命令去删除student的数据:
1
Student.objects.filter(id=1).delete()
1.5 定义on_delete=models.PROTECT
p =  Student.objects.all().first()
p.delete()

注意:这个时候去执行该业务逻辑的方法的时候会报错

2. 一对多

2.1 模型
1
2
3
4
5
6
7
8
9
10
定义一个班级类还有学生类,实现一对多的关系:
先定义班级类
Class Grade(models.Model):
g_name = models.CharField(max_length=16)

定义student
class Student:
s_name = models.CharField(max_length=10)
s_age = models.IntegerField(default=1)
s_grade = models.ForeignKey(Grade, on_delete=PROTECT)

注意:使用models.ForeignKey关联
获取对象元素 grade.student_set

2.2 获取数据

语法:通过一获取多的数据

公式: 一的对象.多的模型_set

然后在获取数据all(), get(), filter() 等等

如下先通过学生去获取班级信息:

1
2
stu = Student.objects.first()
stu.stu_grade

如下是通过班级获取学生信息:

1
2
g = Grade.objects.all().first()
g.stugrade.all() ---> 其中stugrade是定义的related_name参数

重点:
定义了related_name字段以后,只能通过related_name去反向获取数据,在也不能通过_set方法去获取数据了

2.3 性能对比
获取班级的学生(通过一获取多)
1
2
3
4
5
6
7
1. 低性能方法:
g = Grade.objects.all().first()
s = Student.objects.filter(s_grade=g)

2. 高性能方法:
g = Grate.objects.all().first()
s = g.student_set.all()
2.4 练习题
2.6 获取python班下的所有学生的信息
1
2
gs = Grade.objects.filter(g_name='python')[0]
allstu = gs.student_set.all()
2.7 获取python班下语文成绩大于80分的女学生
1
2
gs = Grade.objects.filter(g_name='python')[0]
allstu = gs.student_set.filter(stu_yuwen__gte=80)
2.8 获取python班下语文成绩超过数学成绩10分的男学生
1
2
gs = Grade.objects.filter(g_name='python')[0]
allstu = gs.student_set.filter(stu_yuwen__gte=F('stu_shuxue') + 10)
2.9 获取出生在80后的男学生,查看他们的班级
1
2
gs = Grade.objects.filter(g_name='python')[0]
allstu = gs.student_set.filter(stu_birth__gte='1980-01-01', stu_birth__lte='1990-01-01')

3. 多对多

3.1 M:N 模型

定义购物车,用户的例子实现多对多:


1
2
3
4
5
6
7
8
1. 创建用户模型:
class GoodsUser(models.Model):
u_name = models.CharField(max_length=32)

2. 创建商品模型:
class Goods(models.Model):
g_name = models.CharField(max_length=32)
g_user = models.ManyToManyField(User)

3.2 多对多表结构
多对多关系:
1. 生成表的时候会多生成一张表(实际会有三张表)
2. 生成的表是专门用来维护关系的
3. 生成的表是使用两个外键来维护多对多的关系
4. 两个一对多的关系来实现多对多的实现   
5. 删除一个表的数据的话,中间关联表也要删除相关的信息
3.3 练习题
3.3.1 获取第一个用户购买了那些商品
1
2
gu = GoodsUser.objects.all().first()
allstu = gu.goods_set.all()
3.3.2 获取指定商品的购买用户信息
1
2
g = Goods.objects.filter(id=1)[0]
g.g_user.all()

Django-配置

发表于 2018-08-17

##模型

前言

  Django对数据库提供了很好的支持,对不同的数据库,django提供了统一调用的API,我们可以根据不同的业务需求使用不同是数据库。Django中引入了ORM(Objects Relational Mapping)对象关系映射,是一种程序技术。在下面会详细的讲解。

修改mysql配置

1. 在settings.py中配置数据库连接信息
1
2
3
4
5
6
'ENGINE':'django.db.backends.mysql',
'NAME':'', #数据库名
'USER':'', #账号
'PASSWORD':'', #密码
'HOST':'127.0.0.1', #IP(本地地址也可以是localhost)
'PORT':'3306', #端口
2. mysql数据库中创建定义的数据库

a) 进入mysql

1
mysql -u root -p

b) 创建数据库

1
create database xxx charset=utf-8;

3. 配置数据库链接

a) 安装pymysql

1
pip install pymysql

b) 在工程目录下的init.py文件中输入,完成数据库的驱动加载

1
2
import pymysql
pymysql.install_as_MySQLdb()

4. 定义模型
重要概念:模型,表,属性,字段

一个模型类在数据库中对应一张表,在模型类中定义的属性,对应模型对照表中的一个字段

定义属性见定义属性文件地址

创建学生模型类
class Student(models.Model):

    s_name = models.CharField(max_length=10)
    s_age = models.IntegerField()
    s_gender = models.BooleanField()

    class Meta:
        db_table = 'cd_student'
        ordering =[]
    对象的默认排序字段,获取对象列表时使用,升序ordering['id'],降序ordering['-id']
5.迁移数据库

a) 生成迁移文件

1
python manage.py makemigrations

注意:如果执行后并没有生成迁移文件,一直提示No changes detected这个结果的话,就要手动的去处理了。有两点处理方式:

1) 先删除掉pycache文件夹

2) 直接强制的去执行迁移命令,python manage.py makemigrations xxx (xxx就是app的名称)

3) 查看自动生成的数据库,查看表django_migrations,删掉app字段为xxx的数据(xxx就是app的名称)

b) 执行迁移生成数据库

1
python manage.py migrate

注意: 生成迁移文件的时候,并没有在数据库中生成对应的表,而是执行migrate命令之后才会在数据库中生成表

6. ORM

ORM(Objects Relational Mapping)对象关系映射,是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。可以简单理解为翻译机。

图

7.模型查询
a) 模型成员objects

Django默认通过模型的objects对象实现模型数据查询

b) 过滤器

查询集表示从数据库获取的对象集合

查询集可以有多个过滤器

过滤器就是一个函数,基于所给的参数限制查询的结果

从SQL角度来说,查询集合和select语句等价,过滤器就像where条件

Django有两种过滤器用于筛选记录

    filter      : 返回符合筛选条件的数据集

    exclude   : 返回不符合筛选条件的数据集

多个filter和exclude可以连接在一起查询

当然还有如下这些过滤器:

all()            返回所有数据

filter()     返回符合条件的数据

exclude()        过滤掉符合条件的数据

order_by()       排序

values()         一条数据就是一个字典,返回一个列表
c) 查询单个数据

get():返回一个满足条件的对象。如果没有返回符合条件的对象,会应该模型类DoesNotExist异常,如果找到多个,会引发模型类MultiObjectsReturned异常

first():返回查询集中的第一个对象

last():返回查询集中的最后一个对象

count():返回当前查询集中的对象个数

exists():判断查询集中是否有数据,如果有数据返回True,没有返回False

d) 限制查询集

限制查询集,可以使用下表的方法进行限制,等同于sql中的limit

模型名.objects.all()[0:5] 小标不能为负数

e) 字段查询

对sql中的where实现,作为方法,filter(),exclude(),get()的参数

语法:属性名称__比较运算符 = 值

外键:属性名_id

注意:like语句中使用%表示通配符。比如sql语句查询 where name like ‘%xxx%’,等同于filter(name_contains=’xxx’)

f) 比较运算符

contains:是否包含,大小写敏感

startswith,endswith:以values开头或者结尾,大小写敏感
以上的运算符前加上i(ignore)就不区分大小写了

isnull,isnotnull:是否为空。filter(name__isnull=True)

in:是否包含在范围内。filter(id__in=[1,2,3])

gt,gte,lt,lte:大于,大于等于,小于,小于等于。filter(age__gt=10)

pk:代表主键,也就是id。filter(pk=1)

g) 聚合函数

agregate()函数返回聚合函数的值

Avg:平均值

Count:数量

Max:最大

Min:最小

Sum:求和

例如: Student.objects.aggregate(Max(‘age’))

h) F对象/Q对象

F对象:可以使用模型的A属性与B属性进行比较

背景:在模型中有两个字段,分别表示学生成绩A与成绩B,要对成绩AB进行比较计算,就需要使用到F对象。

例如有如下例子1:

班级中有女生个数字段以及男生个数字段,统计女生数大于男生数的班级可以如下操作:

grades = Grade.objects.filter(girlnum__gt=F('boynum'))

F对象支持算数运算

grades = Grade.objects.filter(girlnum__gt=F(‘boynum’) + 10)

例子2:

查询python班下语文成绩超过数学成绩10分的学生

    grade = Grade.objects.filter(g_name='python').first()
    students = grade.student_set.all()

    stu = students.filter(s_yuwen__gt= F('s_shuxue') + 10)

Q对象:

Q()对象就是为了将过滤条件组合起来

当我们在查询的条件中需要组合条件时(例如两个条件“且”或者“或”)时。我们可以使用Q()查询对象

使用符号&或者|将多个Q()对象组合起来传递给filter(),exclude(),get()等函数

Q()对象的前面使用字符“~”来代表意义“非”

例子1:

查询学生中不是12岁的或者姓名叫张三的学生

student = Student.objects.filter(~Q(age=12) | Q(name='张三'))

例子2:

查询python班语文小于80并且数学小于等于80的学生

grade = Grade.objects.filter(g_name='python').first()
students = grade.student_set.all()
stu = students.filter(~Q(s_yuwen__gte=80) & Q(s_shuxue__lte=80))

​
例子3:

查询python班语文大于等于80或者数学小于等于80的学生

grade = Grade.objects.filter(g_name='python').first()
students = grade.student_set.all()

stu = students.filter(Q(s_yuwen__gte=80) | Q(s_shuxue__lte=80))
8.模型字段定义属性

定义属性

概述
    ·django根据属性的类型确定以下信息
        ·当前选择的数据库支持字段的类型
        ·渲染管理表单时使用的默认html控件
        ·在管理站点最低限度的验证

    ·django会为表增加自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后,则django不会再生成默认的主键列

    ·属性命名限制
        ·遵循标识符规则(不使用python预定义的标识符号,内置函数名,异常等。避免使用下划线等)
        ·由于django的查询方式,不允许使用连续的下划线



库
    ·定义属性时,需要字段类型,字段类型被定义在django.db.models.fields目录下,为了方便使用,被导入到django.db.models中

    ·使用方式
        ·导入from django.db import models
        ·通过models.Field创建字段类型的对象,赋值给属性


逻辑删除
    ·对于重要数据都做逻辑删除,不做物理删除,实现方法是定义isDelete属性,类型为BooleanField,默认值为False


字段类型
    ·AutoField
        ·一个根据实际ID自动增长的IntegerField,通常不指定如果不指定,一个主键字段将自动添加到模型中

    ·CharField(max_length=字符长度)
        ·字符串,默认的表单样式是 TextInput

    ·TextField
        ·大文本字段,一般超过4000使用,默认的表单控件是Textarea

    ·IntegerField
        ·整数

    ·DecimalField(max_digits=None, decimal_places=None)
        ·使用python的Decimal实例表示的十进制浮点数
        ·参数说明
            ·DecimalField.max_digits
                ·位数总数
            ·DecimalField.decimal_places
                ·小数点后的数字位数

    ·FloatField
        ·用Python的float实例来表示的浮点数

    ·BooleanField
        ·true/false 字段,此字段的默认表单控制是CheckboxInput

    ·NullBooleanField
        ·支持null、true、false三种值

    ·DateField([auto_now=False, auto_now_add=False])
        ·使用Python的datetime.date实例表示的日期
        ·参数说明
            ·DateField.auto_now
                ·每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为false
            ·DateField.auto_now_add
                ·当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为false
        ·说明
            ·该字段默认对应的表单控件是一个TextInput. 在管理员站点添加了一个JavaScript写的日历控件,和一个“Today"的快捷按钮,包含了一个额外的invalid_date错误消息键
        ·注意
            ·auto_now_add, auto_now, and default 这些设置是相互排斥的,他们之间的任何组合将会发生错误的结果

    ·TimeField
        ·使用Python的datetime.time实例表示的时间,参数同DateField

    ·DateTimeField
        ·使用Python的datetime.datetime实例表示的日期和时间,参数同DateField

    ·FileField
        ·一个上传文件的字段

    ·ImageField
        ·继承了FileField的所有属性和方法,但对上传的对象进行校验,确保它是个有效的image


字段选项
    ·概述
        ·通过字段选项,可以实现对字段的约束
        ·在字段对象时通过关键字参数指定

    ·null
        ·如果为True,则该字段在数据库中是空数据,默认值是 False

    ·blank
        ·如果为True,则该字段允许为空白,默认值是 False

    ·注意
        ·null是数据库范畴的概念,blank是表单验证证范畴的

    ·db_column
        ·字段的名称,如果未指定,则使用属性的名称

    ·db_index
        ·若值为 True, 则在表中会为此字段创建索引

    ·default
        ·默认值

    ·primary_key
        ·若为 True, 则该字段会成为模型的主键字段

    ·unique
        ·如果为 True, 这个字段在表中必须有唯一值

模型对应关系描述如下:

1:1   一对一 OneToOneField
1:N   一对多 ForeignKey
M:N   多对多 ManyToManyField
常见的几种数据关系,django都提供了很好的支持

1. 一对一

1.1 模型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
创建学生的模型:
class Student(models.Model):
stu_name = models.CharField(max_length=6, unique=True)
stu_sex = models.BooleanField(default=0)
stu_birth = models.DateField()
stu_delete = models.BooleanField(default=0)
stu_create_time = models.DateField(auto_now_add=True)
stu_operate_time = models.DateField(auto_now=True)
stu_tel = models.CharField(max_length=11)
stu_yuwen = models.DecimalField(max_digits=3, decimal_places=1, default=0)
stu_shuxue = models.DecimalField(max_digits=3, decimal_places=1, default=0)

class Meta:
db_table = 'stu'

创建学生拓展的模型:
class StuInfo(models.Model):

stu_addr = models.CharField(max_length=30)
stu_age = models.IntegerField()
stu = models.OneToOneField(Student)

class Meta:
db_table = 'stu_info'
使用models.OneToOneField()进行关联
class StuInfo(models.Model):下是通过班级获取学生信
    stu = models.OneToOneField(Student)
注意:在数据中关联字段名称叫stu_id
1.2 通过学生拓展表去获取学生信息
1
2
stuinfo = StuInfo.objects.all().first()
student = stuinfo.stu
注意:通过拓展表去获取学生的信息的话,语法如下; 学生拓展表的单条对象.关联字段,即可获取到学生表的数据
1.3 通过学生获取人信息1
1
2
stu = Student.objects.all().first()
stuInfo = stu.stuInfo
注意:通过学生获取关联表的数据的话,语法如下: 学生对象.关联的表名,即可获取到关联表的数据
1.3.1 通过学生获取人信息2
在关联字段OneToOneField中加入参数related_name='xxx'
1
2
3
在
stu = Student.objects.all().first()
stuInfo = stu.xxx
注意:通过学生获取关联表的数据的话,语法如下: 学生对象.关联的字段中定义的related_name参数,即可获取到关联表的数据
1.4 设置对应关系的字段为保护模式 :
models.CASCADE                      默认值
models.PROTECT                    保护模式
models.SET_NULL                 置空模式
models.SET_DETAULT          置默认值
models.SET()     删除的时候吃重新动态指向一个实体访问对象元素
on_delete = models.PROTECT

1
2
修改on_delete参数
models.OneToOneField('Student', on_delete=models.SET_NULL, null=True)
在删除student对象的时候,stuinfo的关联字段会设置为空null=True,如下命令去删除student的数据:
1
Student.objects.filter(id=1).delete()
1.5 定义on_delete=models.PROTECT
p =  Student.objects.all().first()
p.delete()

注意:这个时候去执行该业务逻辑的方法的时候会报错

2. 一对多

2.1 模型
1
2
3
4
5
6
7
8
9
10
定义一个班级类还有学生类,实现一对多的关系:
先定义班级类
Class Grade(models.Model):
g_name = models.CharField(max_length=16)

定义student
class Student:
s_name = models.CharField(max_length=10)
s_age = models.IntegerField(default=1)
s_grade = models.ForeignKey(Grade, on_delete=PROTECT)

注意:使用models.ForeignKey关联
获取对象元素 grade.student_set

2.2 获取数据

语法:通过一获取多的数据

公式: 一的对象.多的模型_set

然后在获取数据all(), get(), filter() 等等

如下先通过学生去获取班级信息:

1
2
stu = Student.objects.first()
stu.stu_grade

如下是通过班级获取学生信息:

1
2
g = Grade.objects.all().first()
g.stugrade.all() ---> 其中stugrade是定义的related_name参数

重点:
定义了related_name字段以后,只能通过related_name去反向获取数据,在也不能通过_set方法去获取数据了

2.3 性能对比
获取班级的学生(通过一获取多)
1
2
3
4
5
6
7
1. 低性能方法:
g = Grade.objects.all().first()
s = Student.objects.filter(s_grade=g)

2. 高性能方法:
g = Grate.objects.all().first()
s = g.student_set.all()
2.4 练习题
2.6 获取python班下的所有学生的信息
1
2
gs = Grade.objects.filter(g_name='python')[0]
allstu = gs.student_set.all()
2.7 获取python班下语文成绩大于80分的女学生
1
2
gs = Grade.objects.filter(g_name='python')[0]
allstu = gs.student_set.filter(stu_yuwen__gte=80)
2.8 获取python班下语文成绩超过数学成绩10分的男学生
1
2
gs = Grade.objects.filter(g_name='python')[0]
allstu = gs.student_set.filter(stu_yuwen__gte=F('stu_shuxue') + 10)
2.9 获取出生在80后的男学生,查看他们的班级
1
2
gs = Grade.objects.filter(g_name='python')[0]
allstu = gs.student_set.filter(stu_birth__gte='1980-01-01', stu_birth__lte='1990-01-01')

3. 多对多

3.1 M:N 模型

定义购物车,用户的例子实现多对多:


1
2
3
4
5
6
7
8
1. 创建用户模型:
class GoodsUser(models.Model):
u_name = models.CharField(max_length=32)

2. 创建商品模型:
class Goods(models.Model):
g_name = models.CharField(max_length=32)
g_user = models.ManyToManyField(User)

3.2 多对多表结构
多对多关系:
1. 生成表的时候会多生成一张表(实际会有三张表)
2. 生成的表是专门用来维护关系的
3. 生成的表是使用两个外键来维护多对多的关系
4. 两个一对多的关系来实现多对多的实现   
5. 删除一个表的数据的话,中间关联表也要删除相关的信息
3.3 练习题
3.3.1 获取第一个用户购买了那些商品
1
2
gu = GoodsUser.objects.all().first()
allstu = gu.goods_set.all()
3.3.2 获取指定商品的购买用户信息
1
2
g = Goods.objects.filter(id=1)[0]
g.g_user.all()

网络爬虫7-关于进程和线程

发表于 2018-08-15

网络爬虫5-xpath的使用、json数据解析

发表于 2018-08-14

1 xpath使用
(1)属性筛选
//input[@id=”kw”]
//span[@class=”bg s_ipt_wr quickdelete-wrap”]
通过class进行选择的时候,需要将所有的class全部写进来才可以
(2)索引和层级
//div[@id=”head”]/div/div[@id=”u1”]/a[2]
(3)获取内容和属性
//div[@id=”u1”]/a[2]/text() 获取内容
//div[@id=”u1”]/a[2]/@href 获取属性
(4)xpath函数
选出class属性以mn开头的a
//div[@id=”u1”]/a[starts-with(@class,”mn”)]
选出内容以地开头的a
//div[@id=”u1”]/a[starts-with(text(),”地”)]
选出class属性包含av的a
//div[@id=”u1”]/a[contains(@class,”av”)]
选出内容包含多产的a
//div[@id=”u1”]/a[contains(text(),”多产”)]
代码中使用xpath,首先将文档生成对象,然后根据对象的方法找到想要的节点。可以是本地文件,也可以是网络文件
xpath方法返回的是一个列表,通过下标进行取内容
//[@id=”u1”]/a[2]
//
[@id=”su”] #su #form > input[type=”hidden”]:nth-child(14)
2 xpath案例
sc.chinaz.com 美女图片
懒加载:一个网页中有100个图片。需要101次请求才能加载完毕。首先加载出现在可视区内的图片,在可视区外的图片多没有加载。在用户滚动滚动条的时候,通过js动态的去监听当前这个图片有没有出现在可视区,如果出现在可视区,再去加载这个图片
前端如何实现? 当图片出现在可视区的时候,js动态的将src2修改为src,就会加载这个图片
出现的形式:
src2 data-src data-original class=”lazy”
糗事百科
谷歌中的xpath只能作为参考,实际都要以代码为准
3 json数据解析
认识json?
前台 :由用户看到的界面称之为前台
后台 :管理员负责的查看的,后台管理系统
前端 :html、css、js,在浏览器那一块工作的
后端 :服务器端的,php、java、python
短信发送平台。会提供单独的接口
天气接口。
前端后端交互一般都是json格式。
后端服务器和特定功能服务器之间的交互,后端服务器称之为前端,提供服务的称之为后端,之间交互的格式也是json格式

<note>你长得真漂亮</note>
'note': '你长得真漂亮'
json语法:
(1)数据都在键值对中
(2)数据以逗号隔开
(3){}保存对象    对应的是咱的字典
(4)[]保存数组
字符串都是以双引号括起来
json的值可以是:数字、字符串、逻辑值、数组、字典、null  

Python如何解析json?
(1)原生解析,通过字典、列表解析
    import json
    json.dumps() : 将python的字典或者列表转化为json格式字符串
        ensure_ascii = False  可以看懂中文
    json.loads() : 将json格式的字符串转化为Python的对象
    了解下面的
    json.dump() : 将python对象转化为json字符串之后直接写入到文件中
    json.load() : 将文件中的json字符串直接读到python对象中
(2)jsonpath(了解)
    jsonpath用来干什么?用来解析json数据,当解析复杂一点的json数据就要用到这个。
    obj[0]['lala']['goudan'][2]['dudu']['xixi']
    http://blog.csdn.net/luxideyao/article/details/77802389
    安装:pip install lxml    pip install jsonpath
    和xpath对比
    /    $    根元素
    /    .    路径分隔符,直接子元素
    .    @    当前元素
    //    ..    任意位置开始查找
    下标xpath从1开始,jsonpath从0开始

https://rate.tmall.com/list_detail_rate.htm?itemId=562099309982&spuId=893336129&sellerId=2616970884&order=3&currentPage=2

网络爬虫2-爬虫初步

发表于 2018-08-08

网络爬虫2-爬虫初步

本章主要介绍初步的python爬虫方法
包括处理url参数,构建请求对象,模拟各种请求方式,handler处理器,opener和代理等

  1. url lib.parse()

    主要的方法用于处理参数或者url:

    • url.parse.quote()

      url中不能使用中文,使用的是url编码也可以说是百分号编码

      url中只能有字母、数字、下划线、冒号 // ? =等

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import urllib.parse

    url = 'https://www.baidu.com/s?ie=utf-8&f=3&rsv_bp=0&rsv_idx=1&tn=baidu&wd=%E5%B0%8F%E5%8F%AF%E7%88%B1&rsv_pq=96be106200031fd5&rsv_t=7e37zpPVn6Se%2FFpX446ehNfmIYvVrtgj1SOOkWgO48bW4gpLGLIloR6C3%2Fk&rqlang=cn&rsv_enter=1&rsv_sug3=11&rsv_sug1=3&rsv_sug7=101&rsv_sug2=0&prefixsug=%25E5%25B0%258F%25E5%258F%25AF%25E7%2588%25B1&rsp=3&inputT=3801&rsv_sug4=3801'

    string = urllib.parse.quote(url)
    string_reverse = urllib.parse.unquote(string)

    print(string)
    print(string_reverse)

    其中对url编码是对所有的(包括=号等都进行了编码),编码后不可访问,应该对中文参数单独进行编码;解码后可访问

    • urllib.parse.urlencode()

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      url = 'https://www.baidu.com/s?'
      data = {
      'ie': 'utf8',
      'wd': 'little'
      }
      # 将data拼接到url后面,组成完整的url
      # 方法:遍历这个字典,拼接为制定格式
      lt = []
      for k, v in data.items():
      value = k + '=' + v
      lt.append(value)
      # 将lt用&符号拼接
      query_string = '&'.join(lt)
      # 拼接url
      url += query_string

      print(url)

      以上的办法可以将data中的参数处理为url中可以携带的参数,但我们有更加简便的写法,直接使用urllib.parse.urlencode(data)方法

      1
      2
      3
      4
      # 使用urlencode()
      query_string = urllib.parse.urlencode(data)
      url += query_string
      print(url)

    也可以用url编码工具

  1. 构建请求对象

    不同的浏览器UA都是不同的,网站服务器根据UA给予不同的响应

    我们需要伪装UA进行请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import urllib.request
    import ssl

    ssl._create_default_https_context = ssl._create_unverified_context

    url = 'http://www.baidu.com/'

    # 如何定制UA
    # 在这个头部不仅可以定制ua,还可以定制其他的请求头部,一般只需要定制ua
    headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36'
    }

    # 构建请求对象
    request = urllib.request.Request(url=url, headers=headers)

    # 发送请求,直接打开这个请求对象即可
    response = urllib.request.urlopen(request)
  2. 模拟各种请求方式

    get

    模拟发送带参数的get
    例子:输入搜索国家,单独保存为txt文件

    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
    import urllib.request
    import urllib.parse

    # 让用户输入搜索关键字

    keyword = input('请输入要搜索的关键字:')
    url = 'https://www.baidu.com/s?'

    # get参数

    data = {
    'ie': 'utf8',
    'wd': keyword,
    }
    query_string = urllib.parse.urlencode(data)

    url += query_string

    # 向url发送请求,得到响应

    headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.3',
    }
    request = urllib.request.Request(url=url, headers=headers)

    response = urllib.request.urlopen(request)

    # 拼接文件名字

    filename = keyword + '.html'

    # 写入到文件中

    with open(filename, 'wb') as fp:
    fp.write(response.read())

    post

    例子:百度翻译
    调用百度翻译的sug接口,进行简单翻译

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import urllib.request
    import urllib.parse

    url = 'http://fanyi.baidu.com/sug'
    # 将表单数据写成一个字典
    formdata = {
    'kw': 'baby'
    }
    # 将formdata单独处理一下
    formdata = urllib.parse.urlencode(formdata).encode('utf8')
    headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.3',
    }
    # 构建请求对象
    request = urllib.request.Request(url=url, headers=headers)
    response = urllib.request.urlopen(request, data=formdata)

    print(response.read().decode('utf8'))
  • 调用百度翻译的接口,获取复杂一些的翻译

    在头部中除了CA还需要发送多个头部参数,可以得到单个针对性单词的翻译,但如果需要根据单词来查询需要破解百度翻译的加密sign和token

    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
    import urllib.request
    import urllib.parse

    # 加密的接口,如果想要得到,需要破解
    url = 'http://fanyi.baidu.com/v2transapi'

    word = 'wolf'
    # 表单数据
    formdata = {
    'from': 'en',
    'to': 'zh',
    'query': word,
    'transtype': 'realtime',
    'simple_means_flag': '3',
    'sign': '275695.55262',
    'token': '268ca3a468d99f5aac3a179efad0ab28',
    }
    # 处理表单数据
    formdata = urllib.parse.urlencode(formdata).encode('utf8')
    headers = {
    # 'Accept': '*/*',
    # 将其注释掉,索要完整的格式
    # 'Accept-Encoding': 'gzip, deflate',
    # 'Accept-Language': 'zh-CN,zh;q=0.9',
    # 'Connection': 'keep-alive',
    # 将其注释掉,让其自动计算即可
    # 'Content-Length': '120',
    # 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
    'Cookie': 'BAIDUID=55279ECD6DDA84C66A41BA7CC1E6840E:FG=1; PSTM=1533627007; BIDUPSID=6F6C332F8A0E3C9949BD5D9F884F1FFB; PSINO=3; BDRCVFR[Y1-7gJ950Fn]=jCHWiyEa0lYpAN8n1msQhPEUf; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; H_PS_PSSID=1465_26963_26432_21099_26350_26925_22157; locale=zh; to_lang_often=%5B%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%2C%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%5D; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1533694190; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1533694190; from_lang_often=%5B%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%2C%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%5D',
    'Host': 'fanyi.baidu.com',
    'Origin': 'http://fanyi.baidu.com',
    'Referer': 'http://fanyi.baidu.com/?aldtype=16047',
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36',
    'X-Requested-With': 'XMLHttpRequest',
    }
    request = urllib.request.Request(url=url, headers=headers)
    response = urllib.request.urlopen(request, data=formdata)

    print(response.read().decode('utf-8'))
    • 可能出现的错误有:

      {“error”:997,”from”:”en”,”to”:”zh”,”query”:”(‘wolf’,)”}:错误原因在于你没有模拟浏览器发送请求,请求头不一样,请求头有12个参数

    有一些请求头参数不是必须的,比如以下两个,去掉就可以了

​ ‘Accept-Encoding’: ‘gzip, deflate’:需要解压缩才能读取,所以直接去掉

​ ‘Content-Length’: ‘120’: 浏览器计算长度,去掉,让其自动计算

​ 加密的接口,需要破解

​

ajax-get

例子:豆瓣电影排行榜

获取豆瓣电影排行榜请求的ajax-get的url

让用户输入需要第几页的数据

通过分析我们可以得知,在豆瓣电影排行榜的url中的参数有start&limit两个,其中start是偏移量,limit是每一页显示的数量,每一页显示的睡昂是固定的10条,而偏移量start是等于(n-1)*10开始。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import urllib.request
import urllib.parse

url = 'https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&'
print('每页显示10条数据')
page = int(input('请输入页码:'))
# 根据page计算出来start和limit
start = (page-1) * 10
limit = 10
data = {
'start': start,
'limit': limit,
}
url += urllib.parse.urlencode(data)

headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36',
}
request = urllib.request.Request(url=url, headers=headers)

response = urllib.request.urlopen(request)


print(response.read().decode('utf8'))

ajax-post

在肯德基的官网上爬去城市门店的分布,其为post类型的ajax请求,我们下面模拟浏览器对官网发起请求,获取数据,而请求的url可以在下图中看到

例子:肯德基

肯德基

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import urllib.request
import urllib.parse

url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname'
city = input('请输入要搜索的城市:')
data = {
'cname': city,
'pid': '',
'pageIndex': '1',
'pageSize': '10'
}
data = urllib.parse.urlencode(data).encode('utf8')
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36',
}
request = urllib.request.Request(url=url, headers=headers)

response = urllib.request.urlopen(request, data=data)

print(response.read().decode('utf8'))

  1. URLError\HTTPEroor

    异常处理类,属于urllib.error这个模块

  • URLError:断网或者主机不存在的时候会被触发

    为了使代码更加健壮,应该对可能出现异常的代码进行异常捕获,使用try,Exception进行捕获

    1
    2
    3
    4
    5
    6
    7
    url = 'http://www.maodan.com/'
    try:
    response = urllib.request.urlopen(url)
    except Exception as e:
    print(e)

    print('不影响后面的代码')
    • Exception: 是官方的异常基类,可以捕获所有的异常

      但是如果想精确捕获:需要指定具体的异常,如果指定错误当然就无法捕获了

  • HTTPEroor:文件不存在
    HTTPEroor是URLError的子类,如果多个except同时捕获,为了能够精确捕获应该把父类写在最后

  1. 复杂get

    例子:百度贴吧

    需求描述:输入贴吧名,输入需要爬取的气势和结束页码,以贴吧名字创建文件夹,将每一页的内容全部保存下来到第n页.html中
    我们需要有下载功能的函数,保存功能的函数两个函数

    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
    import urllib.request
    import urllib.parse
    import os
    import time

    def main():
    name = input('请输入要爬取的贴吧的名字:')
    start_page = int(input('请输入要爬取的起始页码:'))
    end_page = int(input('请输入要爬取的结束页码:'))
    url = 'https://tieba.baidu.com/f?'

    for page in range(start_page, end_page + 1):
    print('正在爬取第%s页......' % page)
    # 根据url和page拼接指定页码的url
    request = handle_request(page, baming, url)
    # 根据请求对象发送请求得到响应写入到指定的文件中
    down_load(request, baming, page)
    print('结束爬取第%s页' % page)
    time.sleep(3)

    # 模拟发起请求,保存数据到指定的地址
    def down_load(request, name, page):
    response = urllib.request.urlopen(request)
    # 通过代码创建指定的文件夹
    dirname = name
    # 判断不存在的时候创建
    if not os.path.exists(dirname):
    os.mkdir(dirname)
    # 文件的名字
    filename = '第%s页.html' % page
    # 得到文件的路径
    filepath = os.path.join(dirname, filename)
    # 将内容直接写入到filepath中
    with open(filepath, 'wb') as fp:
    fp.write(response.read())

    # 构建url和请求对象
    def handle_request(page, name, url):
    pn = (page-1) * 50
    # 拼接url
    data = {
    'kw': name,
    'ie': 'utf8',
    'pn': pn
    }
    url += urllib.parse.urlencode(data)
    # print(url)
    headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36',
    }
    # 构建请求对象
    request = urllib.request.Request(url, headers=headers)
    return request

    if __name__ == '__main__':
    main()
  2. Handler处理器,自定义Opener

    urlopen不能自己定制头部,引入自己定制的头部

    为了解决代理和cookie这些更加高级的功能,而引入handler代理

    首先我们来熟悉一下这个,使用它请求一个最简单的功能,高级功能的步骤是一摸一样的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import urllib.request

    url = 'http://www.baidu.com/'

    # 创建handler
    handler = urllib.request.HTTPHandler()
    # 根据handler创建opener
    opener = urllib.request.build_opener(handler)
    # 发送请求的时候不要使用urlopen使用opener.open发送
    response = opener.open(url)
    print(response.read().decode('utf8'))
  3. 代理

    访问不能过于频繁,需要休眠7-10秒左右

    可以设置多个代理,交替请求
    代理服务器:快代理,西祠代理,芝麻代理,阿布云代理等
    代理是需要购买,分为高匿代理,透明代理,普通代理
    代理有了ip和端口号就能使用
    验证代理是否可用,可以用程序,软件(花刺代理验证)等方式

  • 浏览器如何设置代理
    设置–>高级设置–>代理:前去配置代理的ip和端口
  • 代码中如何设置代理
    访问百度搜索的ip服务,可以看到ip地址已经变成了代理地址,已经在代码中使用了代理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    import urllib.request
    url = 'http://www.baidu.com/s?ie=UTF-8&wd=ip'
    handler = urllib.request.ProxyHandler(proxies={'http': '218.60.8.98:3129'})
    opener = urllib.request.build_opener(handler)

    r = opener.open(url)

    with open('代理.html', 'wb') as fp:
    fp.write(r.read())
1234
karen2066

karen2066

35 日志
8 标签
© 2018 karen2066
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4