diff --git a/README.md b/README.md
index 60256f7..a5e4a2e 100755
--- a/README.md
+++ b/README.md
@@ -14,9 +14,11 @@
### 视频资源
-1. [《Python零基础教程快速上手》](https://www.bilibili.com/video/BV1FT4y1R7sz) - Python基础部分的视频,因为随堂录制,有的时候声音会有点小,点赞过3000就为大家重新录制一套声音和画面都更好的精讲版视频。
-2. [《Python零基础数据库可视化教程》](https://www.bilibili.com/video/BV1dA411w7tu)- 数据库部分的视频,随堂录制,数据库部分讲得比较简单,后面还讲了一些做数据可视化系统的内容,我自己对这套视频不是很满意,点赞过2000之后就重新做一套面向数据分析师精讲数据库的视频,重点放在SQL和业务查询知识上,解决数据分析师日常提数问题。
-3. [《Scrapy爬虫框架教学》](https://www.bilibili.com/video/BV1QY411F7Vt)- 爬虫框架Scrapy教学视频,随堂录制,讲解了一个爬取淘宝商品信息的项目,有一点实用价值,爬虫本身并不是我感兴趣的内容,就将就看看吧。
+视频在抖音和B站都可以找到,有兴趣的小伙伴可以关注我的抖音或B站账号,刚刚起号,还希望大家多多支持,非常感谢!
+
+> **说明**:抖音对学习类的视频并不友好,我自己也不懂抖音的账号运营,目前基本不做抖音更新了,大家想看我的视频还是关注B站账号(下图左边的二维码),感谢大家的点赞关注,有什么想看的内容可以给我留言。
+
+
### 文件资源
@@ -24,13 +26,8 @@
链接:,提取码:swg1。
-### 交流大群
-
-下面是我创建的学习交流群,欢迎加入一起学习共同进步。
-
-1. 骆昊的Python学习群-1,群号:789050736,2000人大群。
-2. 骆昊的Python学习群-2,群号:837590310,2000人大群。
-3. 骆昊的Python学习群-3,群号:784430256,1000人大群。
+### 付费学习
-
+之前创建的免费学习交流群(QQ群)都已经满员了,有学习意向的小伙伴可以加入付费交流群,新用户可以通过下方二维码付费之后添加我的私人微信(微信号:**jackfrued**),然后邀请大家进入付费学习打卡群,添加微信时请备注好自己的称呼和需求,我会为大家提供力所能及的帮助。
+
diff --git a/res/20211121225327.png b/res/20211121225327.png
new file mode 100755
index 0000000..6eee06a
Binary files /dev/null and b/res/20211121225327.png differ
diff --git a/res/pay_qr_code.png b/res/pay_qr_code.png
new file mode 100644
index 0000000..3a861df
Binary files /dev/null and b/res/pay_qr_code.png differ
diff --git a/res/qq_groups.JPG b/res/qq_groups.JPG
new file mode 100755
index 0000000..1d75d86
Binary files /dev/null and b/res/qq_groups.JPG differ
diff --git a/res/qrcode.JPG b/res/qrcode.JPG
new file mode 100755
index 0000000..8697cc0
Binary files /dev/null and b/res/qrcode.JPG differ
diff --git "a/\347\254\25404\350\257\276\357\274\232Python\350\257\255\350\250\200\345\205\203\347\264\240\344\271\213\350\277\220\347\256\227\347\254\246.md" "b/\347\254\25404\350\257\276\357\274\232Python\350\257\255\350\250\200\345\205\203\347\264\240\344\271\213\350\277\220\347\256\227\347\254\246.md"
index 2cd74c5..13537f5 100755
--- "a/\347\254\25404\350\257\276\357\274\232Python\350\257\255\350\250\200\345\205\203\347\264\240\344\271\213\350\277\220\347\256\227\347\254\246.md"
+++ "b/\347\254\25404\350\257\276\357\274\232Python\350\257\255\350\250\200\345\205\203\347\264\240\344\271\213\350\277\220\347\256\227\347\254\246.md"
@@ -17,7 +17,7 @@ Python语言支持很多种运算符,我们先用一个表格为大家列出
| `is` `is not` | 身份运算符 |
| `in` `not in` | 成员运算符 |
| `not` `or` `and` | 逻辑运算符 |
-| `=` `+=` `-=` `*=` `/=` `%=` `//=` `**=` `&=` `|=` `^=` `>>=` `<<=` | (复合)赋值运算符 |
+| `=` `+=` `-=` `*=` `/=` `%=` `//=` `**=` `&=` `\|=` `^=` `>>=` `<<=` | (复合)赋值运算符 |
>**说明:** 上面这个表格实际上是按照运算符的优先级从上到下列出了各种运算符。所谓优先级就是在一个运算的表达式中,如果出现了多个运算符,应该先执行哪个运算再执行哪个运算的顺序。在实际开发中,如果搞不清楚运算符的优先级,可以使用圆括号来确保运算的执行顺序。
diff --git "a/\347\254\25405\350\257\276\357\274\232\345\210\206\346\224\257\347\273\223\346\236\204.md" "b/\347\254\25405\350\257\276\357\274\232\345\210\206\346\224\257\347\273\223\346\236\204.md"
index d7908e5..9716940 100755
--- "a/\347\254\25405\350\257\276\357\274\232\345\210\206\346\224\257\347\273\223\346\236\204.md"
+++ "b/\347\254\25405\350\257\276\357\274\232\345\210\206\346\224\257\347\273\223\346\236\204.md"
@@ -31,7 +31,7 @@ else:
如果要构造出更多的分支,可以使用`if...elif...else...`结构或者嵌套的`if...else...`结构,下面的代码演示了如何利用多分支结构实现分段函数求值。
$$
-f(x) = \begin{cases} 3x - 5, & (x \gt 1) \\ x + 2, & (-1 \le x \le 1) \\ 5x + 3, & (x \lt -1) \end{cases}
+f(x) = \begin{cases} 3x - 5, & (x \gt 1) \\\\ x + 2, & (-1 \le x \le 1) \\\\ 5x + 3, & (x \lt -1) \end{cases}
$$
```Python
diff --git "a/\347\254\25410\350\257\276\357\274\232\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\345\255\227\347\254\246\344\270\262.md" "b/\347\254\25410\350\257\276\357\274\232\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\345\255\227\347\254\246\344\270\262.md"
index c872d40..56cda9a 100755
--- "a/\347\254\25410\350\257\276\357\274\232\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\345\255\227\347\254\246\344\270\262.md"
+++ "b/\347\254\25410\350\257\276\357\274\232\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\345\255\227\347\254\246\344\270\262.md"
@@ -9,9 +9,11 @@
### 字符串的定义
所谓**字符串**,就是**由零个或多个字符组成的有限序列**,一般记为:
+
$$
s = a_1a_2 \cdots a_n \,\,\,\,\, (0 \le n \le \infty)
$$
+
在Python程序中,如果我们把单个或多个字符用单引号或者双引号包围起来,就可以表示一个字符串。字符串中的字符可以是特殊符号、英文字母、中文字符、日文的平假名或片假名、希腊字母、[Emoji字符]()等。
```Python
diff --git "a/\347\254\25411\350\257\276\357\274\232\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\351\233\206\345\220\210.md" "b/\347\254\25411\350\257\276\357\274\232\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\351\233\206\345\220\210.md"
index 7bbf566..c19ff16 100755
--- "a/\347\254\25411\350\257\276\357\274\232\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\351\233\206\345\220\210.md"
+++ "b/\347\254\25411\350\257\276\357\274\232\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204\344\271\213\351\233\206\345\220\210.md"
@@ -111,7 +111,7 @@ print(set1) # {3, 6}
#### 比较运算
-两个集合可以用`==`和`!=`进行相等性判断,如果两个集合中的元素完全相同,那么`==`比较的结果就是`True`,否则就是`False`。如果集合`A`的任意一个元素都是集合`B`的元素,那么集合`A`称为集合`B`的子集,即对于$ \forall{a} \in {A}$,均有$ {a} \in {B} $,则$ {A} \subseteq {B} $,`A`是`B`的子集,反过来也可以称`B`是`A`的超集。如果`A`是`B`的子集且`A`不等于`B`,那么`A`就是`B`的真子集。Python为集合类型提供了判断子集和超集的运算符,其实就是我们非常熟悉的`<`和`>`运算符,代码如下所示。
+两个集合可以用`==`和`!=`进行相等性判断,如果两个集合中的元素完全相同,那么`==`比较的结果就是`True`,否则就是`False`。如果集合`A`的任意一个元素都是集合`B`的元素,那么集合`A`称为集合`B`的子集,即对于 $ \forall{a} \in {A}$ ,均有 $ {a} \in {B} $ ,则 $ {A} \subseteq {B} $ ,`A`是`B`的子集,反过来也可以称`B`是`A`的超集。如果`A`是`B`的子集且`A`不等于`B`,那么`A`就是`B`的真子集。Python为集合类型提供了判断子集和超集的运算符,其实就是我们非常熟悉的`<`和`>`运算符,代码如下所示。
```Python
set1 = {1, 3, 5}
diff --git "a/\347\254\25413\350\257\276\357\274\232\345\207\275\346\225\260\345\222\214\346\250\241\345\235\227.md" "b/\347\254\25413\350\257\276\357\274\232\345\207\275\346\225\260\345\222\214\346\250\241\345\235\227.md"
index 3db76c7..c405f5d 100755
--- "a/\347\254\25413\350\257\276\357\274\232\345\207\275\346\225\260\345\222\214\346\250\241\345\235\227.md"
+++ "b/\347\254\25413\350\257\276\357\274\232\345\207\275\346\225\260\345\222\214\346\250\241\345\235\227.md"
@@ -1,14 +1,18 @@
## 第13课:函数和模块
在讲解本节课的内容之前,我们先来研究一道数学题,请说出下面的方程有多少组正整数解。
+
$$
x_1 + x_2 + x_3 + x_4 = 8
$$
-你可能已经想到了,这个问题其实等同于将`8`个苹果分成四组且每组至少一个苹果有多少种方案,因此该问题还可以进一步等价于在分隔`8`个苹果的`7`个空隙之间插入三个隔板将苹果分成四组有多少种方案,也就是从`7`个空隙选出`3`个空隙放入隔板的组合数,所以答案是$ C_7^3=35 $。组合数的计算公式如下所示。
+
+你可能已经想到了,这个问题其实等同于将`8`个苹果分成四组且每组至少一个苹果有多少种方案,因此该问题还可以进一步等价于在分隔`8`个苹果的`7`个空隙之间插入三个隔板将苹果分成四组有多少种方案,也就是从`7`个空隙选出`3`个空隙放入隔板的组合数,所以答案是 $C_7^3=35$ 。组合数的计算公式如下所示。
+
$$
C_M^N = \frac {M!} {N!(M-N)!}
$$
-根据我们前面学习的知识,可以用循环做累乘的方式来计算阶乘,那么通过下面的Python代码我们就可以计算出组合数$ C_M^N $的值,代码如下所示。
+
+根据我们前面学习的知识,可以用循环做累乘的方式来计算阶乘,那么通过下面的 Python 代码我们就可以计算出组合数 $C_M^N$ 的值,代码如下所示。
```Python
"""
diff --git "a/\347\254\25416\350\257\276\357\274\232\345\207\275\346\225\260\347\232\204\351\253\230\347\272\247\345\272\224\347\224\250.md" "b/\347\254\25416\350\257\276\357\274\232\345\207\275\346\225\260\347\232\204\351\253\230\347\272\247\345\272\224\347\224\250.md"
index 6521533..7ca7bc9 100755
--- "a/\347\254\25416\350\257\276\357\274\232\345\207\275\346\225\260\347\232\204\351\253\230\347\272\247\345\272\224\347\224\250.md"
+++ "b/\347\254\25416\350\257\276\357\274\232\345\207\275\346\225\260\347\232\204\351\253\230\347\272\247\345\272\224\347\224\250.md"
@@ -168,7 +168,7 @@ upload('Python从新手到大师.pdf')
### 递归调用
-Python中允许函数嵌套定义,也允许函数之间相互调用,而且一个函数还可以直接或间接的调用自身。函数自己调用自己称为递归调用,那么递归调用有什么用处呢?现实中,有很多问题的定义本身就是一个递归定义,例如我们之前讲到的阶乘,非负整数`N`的阶乘是`N`乘以`N-1`的阶乘,即$ N! = N \times (N-1)! $,定义的左边和右边都出现了阶乘的概念,所以这是一个递归定义。既然如此,我们可以使用递归调用的方式来写一个求阶乘的函数,代码如下所示。
+Python中允许函数嵌套定义,也允许函数之间相互调用,而且一个函数还可以直接或间接的调用自身。函数自己调用自己称为递归调用,那么递归调用有什么用处呢?现实中,有很多问题的定义本身就是一个递归定义,例如我们之前讲到的阶乘,非负整数`N`的阶乘是`N`乘以`N-1`的阶乘,即 $ N! = N \times (N-1)! $ ,定义的左边和右边都出现了阶乘的概念,所以这是一个递归定义。既然如此,我们可以使用递归调用的方式来写一个求阶乘的函数,代码如下所示。
```Python
def fac(num):
diff --git "a/\347\254\25427\350\257\276\357\274\232\347\224\250Python\346\223\215\344\275\234PDF\346\226\207\344\273\266.md" "b/\347\254\25427\350\257\276\357\274\232\347\224\250Python\346\223\215\344\275\234PDF\346\226\207\344\273\266.md"
index 4177f54..6825f83 100755
--- "a/\347\254\25427\350\257\276\357\274\232\347\224\250Python\346\223\215\344\275\234PDF\346\226\207\344\273\266.md"
+++ "b/\347\254\25427\350\257\276\357\274\232\347\224\250Python\346\223\215\344\275\234PDF\346\226\207\344\273\266.md"
@@ -15,9 +15,9 @@ pip install PyPDF2
```Python
import PyPDF2
-reader = PyPDF2.PdfFileReader('test.pdf')
-page = reader.getPage(0)
-print(page.extractText())
+reader = PyPDF2.PdfReader('test.pdf')
+for page in reader.pages:
+ print(page.extract_text())
```
> **提示**:上面代码中使用的PDF文件“test.pdf”以及下面的代码中需要用到的PDF文件,也可以通过下面的百度云盘地址进行获取。链接:https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g 提取码:e7b4。
@@ -36,31 +36,18 @@ pdf2text.py test.pdf
上面的代码中通过创建`PdfFileReader`对象的方式来读取PDF文档,该对象的`getPage`方法可以获得PDF文档的指定页并得到一个`PageObject`对象,通过`PageObject`对象的`rotateClockwise`和`rotateCounterClockwise`方法可以实现页面的顺时针和逆时针方向旋转,通过`PageObject`对象的`addBlankPage`方法可以添加一个新的空白页,代码如下所示。
```Python
-import PyPDF2
+reader = PyPDF2.PdfReader('XGBoost.pdf')
+writer = PyPDF2.PdfWriter()
-from PyPDF2.pdf import PageObject
-
-# 创建一个读PDF文件的Reader对象
-reader = PyPDF2.PdfFileReader('resources/XGBoost.pdf')
-# 创建一个写PDF文件的Writer对象
-writer = PyPDF2.PdfFileWriter()
-# 对PDF文件所有页进行循环遍历
-for page_num in range(reader.numPages):
- # 获取指定页码的Page对象
- current_page = reader.getPage(page_num) # type: PageObject
- if page_num % 2 == 0:
- # 奇数页顺时针旋转90度
- current_page.rotateClockwise(90)
+for no, page in enumerate(reader.pages):
+ if no % 2 == 0:
+ new_page = page.rotate(-90)
else:
- # 偶数页反时针旋转90度
- current_page.rotateCounterClockwise(90)
- writer.addPage(current_page)
-# 最后添加一个空白页并旋转90度
-page = writer.addBlankPage() # type: PageObject
-page.rotateClockwise(90)
-# 通过Writer对象的write方法将PDF写入文件
-with open('resources/XGBoost-modified.pdf', 'wb') as file:
- writer.write(file)
+ new_page = page.rotate(90)
+ writer.add_page(new_page)
+
+with open('temp.pdf', 'wb') as file_obj:
+ writer.write(file_obj)
```
### 加密PDF文件
@@ -70,14 +57,16 @@ with open('resources/XGBoost-modified.pdf', 'wb') as file:
```Python
import PyPDF2
-reader = PyPDF2.PdfFileReader('resources/XGBoost.pdf')
-writer = PyPDF2.PdfFileWriter()
-for page_num in range(reader.numPages):
- writer.addPage(reader.getPage(page_num))
-# 通过encrypt方法加密PDF文件,方法的参数就是设置的密码
+reader = PyPDF2.PdfReader('XGBoost.pdf')
+writer = PyPDF2.PdfWriter()
+
+for page in reader.pages:
+ writer.add_page(page)
+
writer.encrypt('foobared')
-with open('resources/XGBoost-encrypted.pdf', 'wb') as file:
- writer.write(file)
+
+with open('temp.pdf', 'wb') as file_obj:
+ writer.write(file_obj)
```
### 批量添加水印
@@ -85,23 +74,17 @@ with open('resources/XGBoost-encrypted.pdf', 'wb') as file:
上面提到的`PageObject`对象还有一个名为`mergePage`的方法,可以两个PDF页面进行叠加,通过这个操作,我们很容易实现给PDF文件添加水印的功能。例如要给上面的“XGBoost.pdf”文件添加一个水印,我们可以先准备好一个提供水印页面的PDF文件,然后将包含水印的`PageObject`读取出来,然后再循环遍历“XGBoost.pdf”文件的每个页,获取到`PageObject`对象,然后通过`mergePage`方法实现水印页和原始页的合并,代码如下所示。
```Python
-import PyPDF2
+reader1 = PyPDF2.PdfReader('XGBoost.pdf')
+reader2 = PyPDF2.PdfReader('watermark.pdf')
+writer = PyPDF2.PdfWriter()
+watermark_page = reader2.pages[0]
+
+for page in reader1.pages:
+ page.merge_page(watermark_page)
+ writer.add_page(page)
-from PyPDF2.pdf import PageObject
-
-reader1 = PyPDF2.PdfFileReader('resources/XGBoost.pdf')
-reader2 = PyPDF2.PdfFileReader('resources/watermark.pdf')
-writer = PyPDF2.PdfFileWriter()
-# 获取水印页
-watermark_page = reader2.getPage(0)
-for page_num in range(reader1.numPages):
- current_page = reader1.getPage(page_num) # type: PageObject
- current_page.mergePage(watermark_page)
- # 将原始页和水印页进行合并
- writer.addPage(current_page)
-# 将PDF写入文件
-with open('resources/XGBoost-watermarked.pdf', 'wb') as file:
- writer.write(file)
+with open('temp.pdf', 'wb') as file_obj:
+ writer.write(file_obj)
```
如果愿意,还可以让奇数页和偶数页使用不同的水印,大家可以自己思考下应该怎么做。
diff --git "a/\347\254\25430\350\257\276\357\274\232\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\347\232\204\345\272\224\347\224\250.md" "b/\347\254\25430\350\257\276\357\274\232\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\347\232\204\345\272\224\347\224\250.md"
index 940daa2..3b9d4bc 100755
--- "a/\347\254\25430\350\257\276\357\274\232\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\347\232\204\345\272\224\347\224\250.md"
+++ "b/\347\254\25430\350\257\276\357\274\232\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\347\232\204\345\272\224\347\224\250.md"
@@ -31,7 +31,7 @@
| `{N}` | 匹配N次 | `\w{3}` | |
| `{M,}` | 匹配至少M次 | `\w{3,}` | |
| `{M,N}` | 匹配至少M次至多N次 | `\w{3,6}` | |
-| `|` | 分支 | `foo|bar` | 可以匹配foo或者bar |
+| `\|` | 分支 | `foo\|bar` | 可以匹配foo或者bar |
| `(?#)` | 注释 | | |
| `(exp)` | 匹配exp并捕获到自动命名的组中 | | |
| `(?exp)` | 匹配exp并捕获到名为name的组中 | | |
diff --git "a/\347\254\25431\350\257\276\357\274\232\347\275\221\347\273\234\346\225\260\346\215\256\351\207\207\351\233\206\346\246\202\350\277\260.md" "b/\347\254\25431\350\257\276\357\274\232\347\275\221\347\273\234\346\225\260\346\215\256\351\207\207\351\233\206\346\246\202\350\277\260.md"
index cc101a1..cf5db48 100755
--- "a/\347\254\25431\350\257\276\357\274\232\347\275\221\347\273\234\346\225\260\346\215\256\351\207\207\351\233\206\346\246\202\350\277\260.md"
+++ "b/\347\254\25431\350\257\276\357\274\232\347\275\221\347\273\234\346\225\260\346\215\256\351\207\207\351\233\206\346\246\202\350\277\260.md"
@@ -40,7 +40,7 @@ Disallow: /
图1. 百度搜索淘宝的结果
-
+
下面是豆瓣网的[`robots.txt`](https://www.douban.com/robots.txt)文件,大家可以自行解读,看看它做出了什么样的限制。
@@ -89,19 +89,19 @@ Disallow: /j/
图2. HTTP请求
-
+
HTTP 请求通常是由请求行、请求头、空行、消息体四个部分构成,如果没有数据发给服务器,消息体就不是必须的部分。请求行中包含了请求方法(GET、POST 等,如下表所示)、资源路径和协议版本;请求头由若干键值对构成,包含了浏览器、编码方式、首选语言、缓存策略等信息;请求头的后面是空行和消息体。
-
+
图3. HTTP响应
-
+
HTTP 响应通常是由响应行、响应头、空行、消息体四个部分构成,其中消息体是服务响应的数据,可能是 HTML 页面,也有可能是JSON或二进制数据等。响应行中包含了协议版本和响应状态码,响应状态码有很多种,常见的如下表所示。
-
+
#### 相关工具
@@ -115,11 +115,11 @@ HTTP 响应通常是由响应行、响应头、空行、消息体四个部分构
- 网络(Network):用于 HTTP 请求、HTTP 响应以及与网络连接相关的信息。
- 应用(Application):用于查看浏览器本地存储、后台任务等内容,本地存储主要包括Cookie、Local Storage、Session Storage等。
- 
+ 
2. Postman:功能强大的网页调试与 RESTful 请求工具。Postman可以帮助我们模拟请求,非常方便的定制我们的请求以及查看服务器的响应。
- 
+ 
3. HTTPie:命令行HTTP客户端。
@@ -187,7 +187,7 @@ HTTP 响应通常是由响应行、响应头、空行、消息体四个部分构
一个基本的爬虫通常分为数据采集(网页下载)、数据处理(网页解析)和数据存储(将有用的信息持久化)三个部分的内容,当然更为高级的爬虫在数据采集和处理时会使用并发编程或分布式技术,这就需要有调度器(安排线程或进程执行对应的任务)、后台管理程序(监控爬虫的工作状态以及检查数据抓取的结果)等的参与。
-
+
一般来说,爬虫的工作流程包括以下几个步骤:
diff --git "a/\347\254\25432\350\257\276\357\274\232\347\224\250Python\350\216\267\345\217\226\347\275\221\347\273\234\350\265\204\346\272\220.md" "b/\347\254\25432\350\257\276\357\274\232\347\224\250Python\350\216\267\345\217\226\347\275\221\347\273\234\350\265\204\346\272\220.md"
index e182131..0152cee 100755
--- "a/\347\254\25432\350\257\276\357\274\232\347\224\250Python\350\216\267\345\217\226\347\275\221\347\273\234\350\265\204\346\272\220.md"
+++ "b/\347\254\25432\350\257\276\357\274\232\347\224\250Python\350\216\267\345\217\226\347\275\221\347\273\234\350\265\204\346\272\220.md"
@@ -6,7 +6,7 @@
要使用 Python 获取网络数据,我们推荐大家使用名为`requests` 的三方库,这个库我们在之前的课程中其实已经使用过了。按照官方网站的解释,`requests`是基于 Python 标准库进行了封装,简化了通过 HTTP 或 HTTPS 访问网络资源的操作。上课我们提到过,HTTP 是一个请求响应式的协议,当我们在浏览器中输入正确的 [URL](https://developer.mozilla.org/zh-CN/docs/Learn/Common_questions/What_is_a_URL)(通常也称为网址)并按下 Enter 键时,我们就向网络上的 [Web 服务器](https://developer.mozilla.org/zh-CN/docs/Learn/Common_questions/What_is_a_web_server)发送了一个 HTTP 请求,服务器在收到请求后会给我们一个 HTTP 响应。在 Chrome 浏览器中的菜单中打开“开发者工具”切换到“Network”选项卡就能够查看 HTTP 请求和响应到底是什么样子的,如下图所示。
-
+
通过`requests`库,我们可以让 Python 程序向浏览器一样向 Web 服务器发起请求,并接收服务器返回的响应,从响应中我们就可以提取出想要的数据。浏览器呈现给我们的网页是用 [HTML](https://developer.mozilla.org/zh-CN/docs/Web/HTML) 编写的,浏览器相当于是 HTML 的解释器环境,我们看到的网页中的内容都包含在 HTML 的标签中。在获取到 HTML 代码后,就可以从标签的属性或标签体中提取内容。下面例子演示了如何获取网页 HTML 代码,我们通过`requests`库的`get`函数,获取了搜狐首页的代码。
@@ -54,7 +54,7 @@ with open('baidu.png', 'wb') as file:
接下来,我们以“豆瓣电影”为例,为大家讲解如何编写爬虫代码。按照上面提供的方法,我们先使用`requests`获取到网页的HTML代码,然后将整个代码看成一个长字符串,这样我们就可以使用正则表达式的捕获组从字符串提取我们需要的内容。下面的代码演示了如何从[豆瓣电影](https://movie.douban.com/)获取排前250名的电影的名称。[豆瓣电影Top250](https://movie.douban.com/top250)的页面结构和对应代码如下图所示,可以看出,每页共展示了25部电影,如果要获取到 Top250 数据,我们共需要访问10个页面,对应的地址是,这里的`xxx`如果为`0`就是第一页,如果`xxx`的值是`100`,那么我们可以访问到第五页。为了代码简单易读,我们只获取电影的标题和评分。
-
+
```Python
import random
@@ -92,7 +92,7 @@ for page in range(1, 11):
下面以[蘑菇代理](http://www.moguproxy.com/)为例,为大家讲解商业 IP 代理的使用方法。首先需要在该网站注册一个账号,注册账号后就可以[购买](http://www.moguproxy.com/buy)相应的套餐来获得商业 IP 代理。作为商业用途,建议大家购买不限量套餐,这样可以根据实际需要获取足够多的代理 IP 地址;作为学习用途,可以购买包时套餐或根据自己的需求来决定。蘑菇代理提供了两种接入代理的方式,分别是 API 私密代理和 HTTP 隧道代理,前者是通过请求蘑菇代理的 API 接口获取代理服务器地址,后者是直接使用统一的入口(蘑菇代理提供的域名)进行接入。
-
+
下面,我们以HTTP隧道代理为例,为大家讲解接入 IP 代理的方式,大家也可以直接参考蘑菇代理官网提供的代码来为爬虫设置代理。
diff --git "a/\347\254\25433\350\257\276\357\274\232\347\224\250Python\350\247\243\346\236\220HTML\351\241\265\351\235\242.md" "b/\347\254\25433\350\257\276\357\274\232\347\224\250Python\350\247\243\346\236\220HTML\351\241\265\351\235\242.md"
index 309471d..f893b88 100755
--- "a/\347\254\25433\350\257\276\357\274\232\347\224\250Python\350\247\243\346\236\220HTML\351\241\265\351\235\242.md"
+++ "b/\347\254\25433\350\257\276\357\274\232\347\224\250Python\350\247\243\346\236\220HTML\351\241\265\351\235\242.md"
@@ -36,7 +36,7 @@ XPath 是在 XML(eXtensible Markup Language)文档中查找信息的一种
29.99
- Learning XML
+ Learning XML
39.95
diff --git "a/\347\254\25445\350\257\276.\347\264\242\345\274\225.md" "b/\347\254\25445\350\257\276.\347\264\242\345\274\225.md"
index a38bb64..85fed90 100755
--- "a/\347\254\25445\350\257\276.\347\264\242\345\274\225.md"
+++ "b/\347\254\25445\350\257\276.\347\264\242\345\274\225.md"
@@ -1,6 +1,6 @@
## 第45课:索引
-索引是关系型数据库中用来提升查询性能最为重要的手段。关系型数据库中的索引就像一本书的目录,我们可以想象一下,如果要从一本书中找出某个知识点,但是这本书没有目录,这将是意见多么可怕的事情!我们估计得一篇一篇的翻下去,才能确定这个知识点到底在什么位置。创建索引虽然会带来存储空间上的开销,就像一本书的目录会占用一部分篇幅一样,但是在牺牲空间后换来的查询时间的减少也是非常显著的。
+索引是关系型数据库中用来提升查询性能最为重要的手段。关系型数据库中的索引就像一本书的目录,我们可以想象一下,如果要从一本书中找出某个知识点,但是这本书没有目录,这将是一件多么可怕的事情!我们估计得一篇一篇的翻下去,才能确定这个知识点到底在什么位置。创建索引虽然会带来存储空间上的开销,就像一本书的目录会占用一部分篇幅一样,但是在牺牲空间后换来的查询时间的减少也是非常显著的。
MySQL 数据库中所有数据类型的列都可以被索引。对于MySQL 8.0 版本的 InnoDB 存储引擎来说,它支持三种类型的索引,分别是 B+ 树索引、全文索引和 R 树索引。这里,我们只介绍使用得最为广泛的 B+ 树索引。使用 B+ 树的原因非常简单,因为它是目前在基于磁盘进行海量数据存储和排序上最有效率的数据结构。B+ 树是一棵[平衡树](https://zh.wikipedia.org/zh-cn/%E5%B9%B3%E8%A1%A1%E6%A0%91),树的高度通常为3或4,但是却可以保存从百万级到十亿级的数据,而从这些数据里面查询一条数据,只需要3次或4次 I/O 操作。
diff --git "a/\347\254\25446\350\257\276.\350\247\206\345\233\276+\345\207\275\346\225\260+\350\277\207\347\250\213.md" "b/\347\254\25446\350\257\276.\350\247\206\345\233\276+\345\207\275\346\225\260+\350\277\207\347\250\213.md"
index 6d32e09..8a48074 100755
--- "a/\347\254\25446\350\257\276.\350\247\206\345\233\276+\345\207\275\346\225\260+\350\277\207\347\250\213.md"
+++ "b/\347\254\25446\350\257\276.\350\247\206\345\233\276+\345\207\275\346\225\260+\350\277\207\347\250\213.md"
@@ -329,7 +329,7 @@ drop procedure if exists sp_upgrade_salary;
ANSI/ISO SQL 92标准定义了4个等级的事务隔离级别,如下表所示。需要说明的是,事务隔离级别和数据访问的并发性是对立的,事务隔离级别越高并发性就越差。所以要根据具体的应用来确定到底使用哪种事务隔离级别,这个地方没有万能的原则。
-
+
### 总结
diff --git "a/\347\254\25447\350\257\276.MySQL\346\226\260\347\211\271\346\200\247.md" "b/\347\254\25447\350\257\276.MySQL\346\226\260\347\211\271\346\200\247.md"
index 5b729a9..1a19c2f 100755
--- "a/\347\254\25447\350\257\276.MySQL\346\226\260\347\211\271\346\200\247.md"
+++ "b/\347\254\25447\350\257\276.MySQL\346\226\260\347\211\271\346\200\247.md"
@@ -159,14 +159,14 @@ select * from (
) `temp` where `rank` between 4 and 6;
```
-> **说明**:上面使用的函数`row_number()`可以为每条记录生成一个行号,在实际工作中可以根据需要将其替换为`rank()`或`dense_rank()`函数,三者的区别可以参考官方文档或阅读[《通俗易懂的学会:SQL窗口函数》](https://zhuanlan.zhihu.com/p/92654574)进行了解。在MySQL 8以前的版本,我们可以通过下面的方式来完成类似的操作。
->
-> ```SQL
-> select `rank`, `ename`, `sal` from (
-> select @a:=@a+1 as `rank`, `ename`, `sal`
-> from `tb_emp`, (select @a:=0) as t1 order by `sal` desc
-> ) as `temp` where `rank` between 4 and 6;
-> ```
+上面使用的函数`row_number()`可以为每条记录生成一个行号,在实际工作中可以根据需要将其替换为`rank()`或`dense_rank()`函数,三者的区别可以参考官方文档或阅读[《通俗易懂的学会:SQL窗口函数》](https://zhuanlan.zhihu.com/p/92654574)进行了解。在MySQL 8以前的版本,我们可以通过下面的方式来完成类似的操作。
+
+ ```SQL
+select `rank`, `ename`, `sal` from (
+ select @a:=@a+1 as `rank`, `ename`, `sal`
+ from `tb_emp`, (select @a:=0) as t1 order by `sal` desc
+) as `temp` where `rank` between 4 and 6;
+ ```
例子2:查询每个部门月薪最高的两名的员工的姓名和部门名称。
@@ -180,13 +180,13 @@ from (
) as `temp` natural join `tb_dept` where `rank`<=2;
```
-> 说明:在MySQL 8以前的版本,我们可以通过下面的方式来完成类似的操作。
->
-> ```SQL
-> select `ename`, `sal`, `dname` from `tb_emp` as `t1`
+说明:在MySQL 8以前的版本,我们可以通过下面的方式来完成类似的操作。
+
+```SQL
+select `ename`, `sal`, `dname` from `tb_emp` as `t1`
natural join `tb_dept`
where (
- select count(*) from `tb_emp` as `t2`
- where `t1`.`dno`=`t2`.`dno` and `t2`.`sal`>`t1`.`sal`
+ select count(*) from `tb_emp` as `t2`
+ where `t1`.`dno`=`t2`.`dno` and `t2`.`sal`>`t1`.`sal`
)<2 order by `dno` asc, `sal` desc;
-> ```
+```
\ No newline at end of file