一、知识点
知识点1:Iterable和Iterator
|-------------------|-------------------------------------------|------------------------------------------|
| | 可迭代对象 Iterable | 迭代器 Iterator |
| 定义 | 可以一次返回其成员的对象,这意味着这些对象可以通过for...in循环进行遍历 | 是表示数据流的对象,通过重复调用迭代器的*next()*方法返回数据流中的连续项 |
| 核心方法 | iter() | iter()和__next__() |
| 获取方式 | 直接使用可迭代对象本身 | 通过对可迭代对象调用 iter() 或 __iter__() 获得 |
| 状态 | 通常不保存迭代状态 | 保存当前的迭代状态(位置) |
| 遍历 | 不能直接使用 next() 遍历 | 可以使用 next() 逐个获取元素 |
| 遍历次数 | 可被多次遍历(每次创建新的迭代器) | 通常只能遍历一次(消耗型) |
| 示例类型 | 列表、元组、字符串、字典、集合 | iter(list) 返回的对象、生成器对象 |
| StopIteration | 不会直接引发 | 当元素耗尽时引发 StopIteration 异常 |
知识点2:map()和reduce()
python
map() 函数:
1. 两个参数: function and Iterable
2. 作用: 将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回
3. 示例1: f(x)=x**2, 把f(x)作用到list[1,2,3,4,5,6,7,8,9],代码如下:
>>> def f(x):
return x*x
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
4、示例2:把list[1,2,3,4,5,6,7,8,9]转换为字符串,代码如下:
>>> list(map(str,[1, 2, 3, 4, 5, 6, 7, 8, 9]))
['1', '2', '3', '4', '5', '6', '7', '8', '9']
python
reduce()函数:
1、两个参数:function和iterable
2、作用:reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
3、示例1:对一个序列求和
>>> from functools import reduce
>>> def add(x, y):
return x + y
>>> reduce(add, [1, 3, 5, 7])
16
4、str转换为int
>>> from functools import reduce
>>> def f(x, y):
return x*10+y
>>> def strToInt(s):
num = {'0':0, '1':1, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9}
return num[s]
>>> reduce(f,map(strToInt,'1357'))
1357
相关练习:
1、利用map()函数,把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:['adam', 'LISA', 'barT'],输出:['Adam', 'Lisa', 'Bart']:
python
def f(name):
return name[0].upper() + name[1:].lower()
print(list(map(f,['adam','LISA','barT'])))
2、请编写一个prod()函数,可以接受一个list并利用reduce()求积:
python
from functools import reduce
def prod(x, y):
return x * y
print('3 * 5 * 7 * 9 =', reduce(prod,[3, 5, 7, 9]))
if reduce(prod,[3, 5, 7, 9]) == 945:
print('测试成功!')
else:
print('测试失败!')
3、利用map和reduce编写一个str2float函数,把字符串'123.456'转换成浮点数123.456:
python
from functools import reduce
def strToInt(s):
num = {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}
return num[s]
def f(x,y):
return x*10+y
def str2float(s):
return reduce(f,map(strToInt,s.split('.')[0]))+reduce(f,map(strToInt,s.split('.')[1]))/10**len(s.split('.')[1])
print('str2float(\'123.456\') =', str2float('123.456'))
if abs(str2float('123.456') - 123.456) < 0.00001:
print('测试成功!')
else:
print('测试失败!')
知识点3:filter()
python
filter()函数:
1、两个参数:函数和序列
2、作用:过滤
3、示例:保留奇数
>>> def is_odd(n):
return n % 2 == 1
>>> list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
[1, 5, 9, 15]
相关练习:
1、回数是指从左向右读和从右向左读都是一样的数,例如12321,909。请利用filter()筛选出回数:
python
def is_palindrome(n):
s = str(n)
return s == s[::-1]
# 测试:
output = filter(is_palindrome, range(1, 1000))
print('1~1000:', list(output))
知识点4:闭包
使用闭包,就是内层函数引用了外层函数的局部变量。如果只是读外层变量的值,我们会发现返回的闭包函数调用一切正常;但如果是对其赋值,则会把x当做函数counter()的局部变量
python
def createCounter():
x = 0
def counter():
nonlocal x
x = x + 1
return x
return counter
# 测试:
counterA = createCounter()
print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5
知识点5:slots
通过__slots__来限制实例能添加的属性。
python
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
s = Student() # 创建新的实例
s.name = 'Michael' # 绑定属性'name'
s.age = 25 # 绑定属性'age'
s.score = 99 # 绑定属性'score' #报错:AttributeError: 'Student' object has no attribute 'score'
slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。除非在子类中也定义__slots,这样,子类允许定义的属性就是自身的__slots__加上父类的__slots__。
python
class Stu2(Stu):
pass
ss=Stu2()
ss.name='bbb'
ss.age=21
ss.score=98
print(ss.score) #输出:98
class Stu3(Stu):
__slots__ = ('name','gender')
sss=Stu3()
sss.name='ccc'
sss.gender='m'
sss.age=20
print(sss.name) #输出:ccc
print(sss.gender) #输出:m
print(sss.age) #输出:20
知识点6:@property
@property装饰器就是负责把一个方法变成属性调用的。
python
class Screen(object):
@property
def width(self):
return self.width
@width.setter #负责把该方法变成属性赋值
def width(self, value):
self._width = value
@property
def height(self):
return self.height
@height.setter
def height(self, value):
self._height = value
@property #只读属性,只定义getter方法,不定义setter方法就是一个只读属性
def resolution(self):
return self._width * self._height
# 测试:
s = Screen()
s.width = 1024
s.height = 768
print('resolution =', s.resolution)
知识点7:调试
错误信息:https://docs.python.org/3/library/exceptions.html#exception-hierarchy
1、print():把可能有问题的变量打印出来。
2、断言(assert):如果断言失败,assert语句本身就会抛出异常。(可通过-O参数关闭assert,如:python -O err.py)。
python
def foo(s):
n = int(s)
assert n != 0, 'n is zero!' #表达式n!=0应该是True,否则,后面代码出错。
return 10 / n
def main():
foo('0')
3、logging:和assert相比,logging不会抛出错误,而且可以输出到文件中。logging允许你指定记录信息的级别,有debug、info、warning、error等几个级别,当我们指定level=INFO时,其他级别的信息就不起作用了。这样一来,你可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。另一个好处就是可以通过简单的配置,一条语句同时输出到不同的地方,如console和文件。
python
import logging
logging.basicConfig(level=logging.INFO)
4、调试器pdb:让程序单步运行,可随时查看运行状态。
以参数 -m pdb 启动后,pdb定位到下一步要执行的代码;输入命令 l 查看代码;输入命令 n 单步执行代码;任何时候都可以输入命令 p 变量名 来查看变量;输入命令 q 结束调试,退出程序。
pdb.set_trace() #在可能出错的地方放一个,就可以设置一个断点,运行代码时,程序会自动在该语句暂停并进入pdb调试环境,可以使用命令p 来查看变量,或用命令 c 继续运行。
5、IDE:支持调试的IDE
知识点8:单元测试
对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
编写单元测试时,我们需要编写一个测试类,从unittest.TestCase继承。
以test开头的方法就是测试方法,不以test开头的方法不被认为是测试方法,测试的时候不会被执行。
对每一类测试都需要编写一个test_xxx()方法。由于unittest.TestCase提供了很多内置的条件判断,我们只需要调用这些方法就可以断言输出是否是我们所期望的。
python
import unittest
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def get_grade(self):
if self.score >= 60 and self.score < 80:
return 'B'
elif self.score >= 80 and self.score <= 100:
return 'A'
elif self.score < 0 or self.score > 100:
raise ValueError
return 'C'
class TestStudent(unittest.TestCase):
def test_80_to_100(self):
s1 = Student('Bart', 80)
s2 = Student('Lisa', 100)
self.assertEqual(s1.get_grade(), 'A')
self.assertEqual(s2.get_grade(), 'A')
def test_60_to_80(self):
s1 = Student('Bart', 60)
s2 = Student('Lisa', 79)
self.assertEqual(s1.get_grade(), 'B')
self.assertEqual(s2.get_grade(), 'B')
def test_0_to_60(self):
s1 = Student('Bart', 0)
s2 = Student('Lisa', 59)
self.assertEqual(s1.get_grade(), 'C')
self.assertEqual(s2.get_grade(), 'C')
def test_invalid(self):
s1 = Student('Bart', -1)
s2 = Student('Lisa', 101)
with self.assertRaises(ValueError):
s1.get_grade()
with self.assertRaises(ValueError):
s2.get_grade()
if __name__ == '__main__':
unittest.main()

知识点9:正则表达式
在正则表达式中,如果直接给出字符,就是精确匹配。
search()方法用于在字符串中搜索正则表达式模式第一次出现的位置。
第一个参数是正则表达式模式,也就是你要描述的搜索规则。
找到后返回的范围是以下标0开始的。
python
import re
re.search(r"Fish","I Love Fish!")
<re.Match object; span=(7, 11), match='Fish'>
\d可以匹配一个数字
python
>>> re.search(r'\d', 'I love 123 FishC.com!')
<re.Match object; span=(7, 8), match='1'>
\w可以匹配一个字母或数字
.可以匹配任意字符
python
>>> re.search(r'.', 'I love FishC.com!')
<re.Match object; span=(0, 1), match='I'>
>>> re.search(r'co.', 'I love FishC.com!')
<re.Match object; span=(13, 16), match='com'>
反斜杠剥夺元字符(.)的特殊能力。
python
>>> re.search(r'\.', 'I love FishC.com!')
<re.Match object; span=(12, 13), match='.'>
*表示任意个字符(包括0个)
+表示至少一个字符
?表示0个或1个字符
{n}表示n个字符
{n,m}表示n-m个字符:
'00\d'可以匹配'007',但无法匹配'00A';'\d\d\d'可以匹配'010';'\w\w\d'可以匹配'py3';'py.'可以匹配'pyc'、'pyo'、'py!'等等- eg:\d{3}\s+\d{3,8}
\d{3}表示匹配3个数字,例如'010';\s可以匹配一个空格(也包括Tab等空白符),所以\s+表示至少有一个空格;\d{3,8}表示3-8个数字,例如'1234567'
要做更精确地匹配,可以用[]表示范围,比如:
0-9a-zA-Z\\_可以匹配一个数字、字母或者下划线;
0-9a-zA-Z\\_+可以匹配至少由一个数字、字母或者下划线组成的字符串,比如'a100','0_Z','Py3000'等等;
a-zA-Z\\_0-9a-zA-Z\\_*可以匹配由字母或下划线开头,后接任意个由一个数字、字母或者下划线组成的字符串,也就是Python合法的变量;
a-zA-Z\\_0-9a-zA-Z\\_{0, 19}更精确地限制了变量的长度是1-20个字符(前面1个字符+后面最多19个字符)。
A|B可以匹配A或B,所以(P|p)ython可以匹配'Python'或者'python'。
^表示行的开头,^\d表示必须以数字开头。
表示行的结束,\\d表示必须以数字结束。
使用 将任何内容包起来就是一个字符类,它的含义是你只要匹配这个字符类中的任何字符,结果就算作匹配。默认区分大小写。
python
>>> re.search(r'[aeiou]', 'I love 123 FishC.com!')
<re.Match object; span=(3, 4), match='o'>
>>> re.search(r'[aeiouAEIOU]', 'I love 123 FishC.com!')
<re.Match object; span=(0, 1), match='I'>
>>> re.search(r'[a-z]', 'I love 123 FishC.com!')
<re.Match object; span=(2, 3), match='l'>
>>> re.search(r'[0-2][0-5][0-5]', 'I love 123 FishC.com!')
<re.Match object; span=(7, 10), match='123'>
# 重复匹配
>>> re.search(r'ab{3}c', 'abbbc')
<re.Match object; span=(0, 5), match='abbbc'>
# 超过3个或者少于3个都不能匹配
>>> re.search(r'ab{3,5}c', 'abbbc')
<re.Match object; span=(0, 5), match='abbbc'>
>>> re.search(r'ab{3,5}c', 'abbbbc')
<re.Match object; span=(0, 6), match='abbbbc'>
>>> re.search(r'ab{3,5}c', 'abbbbbc')
<re.Match object; span=(0, 7), match='abbbbbc'>
用法:
正则表达式所有的元字符 . ^ $ * + ? {} \[\] \ | ()
| 符号 | 含义 |
|---|---|
| . | 表示匹配除了换行符外的任何字符。注:通过设置 re.DOTALL 标志可以使 . 匹配任何字符(包含换行符) |
| | | A | B,表示匹配正则表达式 A 或者 B |
| ^ | (脱字符)匹配输入字符串的开始位置。如果设置了 re.MULTILINE 标志,^ 也匹配换行符之后的位置 |
| $ | 匹配输入字符串的结束位置。如果设置了 re.MULTILINE 标志,$ 也匹配换行符之前的位置 |
| \ | 将一个普通字符变成特殊字符,例如 \d 表示匹配所有十进制数字。 解除元字符的特殊功能,例如 . 表示匹配点号本身。引用序号对应的子组所匹配的字符串 |
| ... | 字符类,匹配所包含的任意一个字符。注:连字符 - 如果出现在字符串中间表示字符范围描述;如果出现在首位则仅作为普通字符。特殊字符仅有反斜线 \ 保持特殊含义,用于转义字符。其它特殊字符如 *、+、? 等均作为普通字符匹配。脱字符 ^ 如果出现在首位则表示匹配不包含其中的任意字符;如果 ^ 出现在字符串中间就仅作为普通字符匹配 |
| {M,N} | M 和 N 均为非负整数,其中 M <= N,表示前边的 RE 匹配 M ~ N 次。注:{M,} 表示至少匹配 M 次;{,N} 等价于 {0,N};{N} 表示需要匹配 N 次 |
| * | 匹配前面的子表达式零次或多次,等价于 {0,} |
| + | 匹配前面的子表达式一次或多次,等价于 {1,} |
| ? | 匹配前面的子表达式零次或一次,等价于 {0,1} |
| *?, +?, ?? | 默认情况下 、+ 和 ? 的匹配模式是贪婪模式(即会尽可能多地匹配符合规则的字符串);?、+? 和 ?? 表示启用对应的非贪婪模式。举个例子:对于字符串 "FishCCC",正则表达式 FishC+ 会匹配整个字符串,而 FishC+? 则匹配 "FishC" |
| {M,N}? | 同上,启用非贪婪模式,即只匹配 M 次 |
| (...) | 匹配圆括号中的正则表达式,或者指定一个子组的开始和结束位置。注:子组的内容可以在匹配之后被 \数字 再次引用 。举个例子:(\w+) \1 可以字符串 "FishC FishC.com" 中的 "FishC FishC"(注意有空格) |
| (?...) | (? 开头的表示为正则表达式的扩展语法(下边这些是 Python 支持的所有扩展语法) |
| (?aiLmsux) | 1. (? 后可以紧跟着 'a','i','L','m','s','u','x' 中的一个或多个字符,只能在正则表达式的开头使用。2. 每一个字符对应一种匹配标志:re-A(只匹配 ASCII 字符),re-I(忽略大小写),re-L(区域设置),re-M(多行模式), re-S(. 匹配任何符号),re-X(详细表达式),包含这些字符将会影响整个正则表达式的规则。3. 当你不想通过 re.compile() 设置正则表达式标志,这种方法就非常有用啦。注意,由于 (?x) 决定正则表达式如何被解析,所以它应该总是被放在最前边(最多允许前边有空白符)。如果 (?x) 的前边是非空白字符,那么 (?x) 就发挥不了作用了 |
| (?:...) | 非捕获组,即该子组匹配的字符串无法从后边获取 |
| (?P<name>...) | 命名组,通过组的名字(name)即可访问到子组匹配的字符串 |
| (?P=name) | 反向引用一个命名组,它匹配指定命名组匹配的任何内容 |
| (?#...) | 注释,括号中的内容将被忽略 |
| (?=...) | 前向肯定断言。如果当前包含的正则表达式(这里以 ... 表示)在当前位置成功匹配,则代表成功,否则失败。一旦该部分正则表达式被匹配引擎尝试过,就不会继续进行匹配了;剩下的模式在此断言开始的地方继续尝试。举个例子:love(?=FishC) 只匹配后边紧跟着 "FishC" 的字符串 "love" |
| (?!...) | 前向否定断言。这跟前向肯定断言相反(不匹配则表示成功,匹配表示失败)。举个例子:FishC(?!.com) 只匹配后边不是 ".com" 的字符串 "FishC" |
| (?<=...) | 后向肯定断言。跟前向肯定断言一样,只是方向相反。举个例子:(?<=love)FishC 只匹配前边紧跟着 "love" 的字符串 "FishC" |
| (?<!...) | 后向否定断言。跟前向肯定断言一样,只是方向相反。举个例子:(?<!FishC).com 只匹配前边不是 "FishC" 的字符串 ".com" |
| (?(id/name)yes-pattern|no-pattern) | 1. 如果子组的序号或名字存在的话,则尝试 yes-pattern 匹配模式;否则尝试 no-pattern 匹配模式。2. no-pattern 是可选的。举个例子:(<)?(\w+@\w+(?:.\w+)+)(?(1)>|$) 是一个匹配邮件格式的正则表达式,可以匹配 user@fishc.com 和 'user@fishc.com',但是不会匹配 '<user@fishc.com' 或 'user@fishc.com>' |
| \ | 下边列举了由字符 '' 和另一个字符组成的特殊含义。注意,'' + 元字符的组合可以解除元字符的特殊功能 |
| \序号 | 1. 引用序号对应的子组所匹配的字符串,子组的序号从 1 开始计算。2. 如果序号是以 0 开头,或者 3 个数字的长度。那么不会被用于引用对应的子组,而是用于匹配八进制数字所表示的 ASCII 码值对应的字符。举个例子:(.+) \1 会匹配 "FishC FishC" 或 "55 55",但不会匹配 "FishCFishC"(注意,因为子组后边还有一个空格) |
| \A | 匹配输入字符串的开始位置 |
| \Z | 匹配输入字符串的结束位置 |
| \b | 匹配一个单词边界,单词被定义为 Unidcode 的字母数字或下横线字符。举个例子:\bFishC\b 会匹配字符串 "love FishC"、FishC." 或 "(FishC)" |
| \B | 匹配非单词边界,其实就是与 \b 相反。举个例子:py\B 会匹配字符串 "python"、"py3" 或 "py2",但不会匹配 "py "、"py." 或 "py!" |
| \d | 1. 对于 Unicode(str 类型)模式:匹配任何一个数字,包括 0-9 和其他数字字符;如果开启了 re.ASCII 标志,就只匹配 0-9。2. 对于 8 位(bytes 类型)模式:匹配 0-9 中任何一个数字 |
| \D | 匹配任何非 Unicode 的数字,其实就是与 \d 相反;如果开启了 re.ASCII 标志,则相当于匹配 \^0-9 |
| \s | 1. 对于 Unicode(str 类型)模式:匹配 Unicode 中的空白字符(包括 \\t\\n\\r\\f\\v 以及其他空白字符);如果开启了 re.ASCII 标志,就只匹配 \\t\\n\\r\\f\\v。2. 对于 8 位(bytes 类型)模式:匹配 ASCII 中定义的空白字符,即 \\t\\n\\r\\f\\v |
| \S | 匹配任何非 Unicode 中的空白字符,其实就是与 \s 相反;如果开启了 re.ASCII 标志,则相当于匹配 \^ \\t\\n\\r\\f\\v |
| \w | 1. 对于 Unicode(str 类型)模式:匹配任何 Unicode 的单词字符,基本上所有语言的字符都可以匹配,当然也包括数字和下横线;如果开启了 re.ASCII 标志,就只匹配 a-zA-Z0-9_。2. 对于 8 位(bytes 类型)模式:匹配 ASCII 中定义的字母数字,即 a-zA-Z0-9_ |
| \W | 匹配任何非 Unicode 的单词字符,其实就是与 \w 相反;如果开启了 re.ASCII 标志,则相当于 \^a-zA-Z0-9_ |
| 转义符号 | 正则表达式还支持大部分 Python 字符串的转义符号:\a,\b,\f,\n,\r,\t,\u,\U,\v,\x,\\。注:\b 通常用于匹配一个单词边界,只有在字符类中才表示"退格";\u 和 \U 只有在 Unicode 模式下才会被识别;八进制转义(\数字)是有限制的,如果第一个数字是 0,或者如果有 3 个八进制数字,那么就被认为是八进制数;其他情况则被认为是子组引用;至于字符串,八进制转义总是最多只能是 3 个数字的长度 |
二、练习
练习1
请修改列表生成式,通过添加if语句保证列表生成式能正确地执行:
python
L1 = ['Hello', 'World', 18, 'Apple', None]
L2 = [i.lower() for i in L1 if isinstance(i,str)]
# 测试:
print(L2)
if L2 == ['hello', 'world', 'apple']:
print('测试通过!')
else:
print('测试失败!')
练习2
杨辉三角定义如下:
1
/ \
1 1
/ \ / \
1 2 1
/ \ / \ / \
1 3 3 1
/ \ / \ / \ / \
1 4 6 4 1
/ \ / \ / \ / \ / \
1 5 10 10 5 1
把每一行看做一个list,试写一个generator,不断输出下一行的list:
python
def triangles():
t=[1]
while True:
yield t
t=[t[i-1]+t[i] if i>0 else t[i] for i in range(len(t))]+[1]
pass
# 期待输出:
# [1]
# [1, 1]
# [1, 2, 1]
# [1, 3, 3, 1]
# [1, 4, 6, 4, 1]
# [1, 5, 10, 10, 5, 1]
# [1, 6, 15, 20, 15, 6, 1]
# [1, 7, 21, 35, 35, 21, 7, 1]
# [1, 8, 28, 56, 70, 56, 28, 8, 1]
# [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
n = 0
results = []
for t in triangles():
results.append(t)
n = n + 1
if n == 10:
break
for t in results:
print(t)
if results == [
[1],
[1, 1],
[1, 2, 1],
[1, 3, 3, 1],
[1, 4, 6, 4, 1],
[1, 5, 10, 10, 5, 1],
[1, 6, 15, 20, 15, 6, 1],
[1, 7, 21, 35, 35, 21, 7, 1],
[1, 8, 28, 56, 70, 56, 28, 8, 1],
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
]:
print('测试通过!')
else:
print('测试失败!')
练习3
把Student的gender属性改造为枚举类型
python
from enum import Enum, unique
@unique
class Gender(Enum):
Male = 0
Female = 1
class Student(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
# 测试:
bart = Student('Bart', Gender(0))
if bart.gender == Gender.Male:
print('测试通过!')
else:
print('测试失败!')
三、实践
1、下载一张图像
urllib.request用于打开和读取URL
python
import urllib.request
rep = urllib.request.urlopen("http://placecats.com/louie/300/300")
cat_img = rep.read()
with open('D:\Desktop\cat_300_300.jpg','wb') as f:
f.write(cat_img)
2、用有道来翻译文本
urllib.parse用于解析URL
所有来自URL解析函数的返回值都会提供encode()方法和decode()方法。
python
import urllib.request
import urllib.parse
import json
content = input("请输入需要翻译的内容:")
url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule&smartresult=ugc&sessionFrom=http://www.youdao.com"
data = {}
data['type'] = 'AUTO'
data['i'] = content
data['doctype'] = 'json'
data['version'] = '2.1'
data['keyfrom'] = 'fanyi.web'
data['ue'] = 'UTF-8'
data['typoResult'] = 'true'
data = urllib.parse.urlencode(data).encode('utf-8') # 转换格式并编码为utf-8
response = urllib.request.urlopen(url, data) # post请求必须带上data参数
html = response.read().decode('utf-8')
target = json.loads(html)
print("翻译结果:%s" % (target['translateResult'][0][0]['tgt'])) # 一层一层剥掉
设置headers参数有两种方法:
- 实例化Request参数时将headers参数传进去
python
import urllib.request
import urllib.parse
import json
content = input("请输入需要翻译的内容:")
url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule&smartresult=ugc&sessionFrom=http://www.youdao.com"
head = {}
head['Referer'] = 'http://fanyi.youdao.com/'
head['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
data = {}
data['type'] = 'AUTO'
data['i'] = content
data['doctype'] = 'json'
data['version'] = '2.1'
data['keyfrom'] = 'fanyi.web'
data['ue'] = 'UTF-8'
data['typoResult'] = 'true'
data = urllib.parse.urlencode(data).encode('utf-8')
req = urllib.request.Request(url, data, headers=head)
response = urllib.request.urlopen(req)
html = response.read().decode('utf-8')
target = json.loads(html)
print("翻译结果:%s" % (target['translateResult'][0][0]['tgt']))
print(req.headers)
- 通过add_header()方法往Request对象添加headers
python
import urllib.request
import urllib.parse
import json
content = input("请输入需要翻译的内容:")
url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule&smartresult=ugc&sessionFrom=http://www.youdao.com"
data = {}
data['type'] = 'AUTO'
data['i'] = content
data['doctype'] = 'json'
data['version'] = '2.1'
data['keyfrom'] = 'fanyi.web'
data['ue'] = 'UTF-8'
data['typoResult'] = 'true'
data = urllib.parse.urlencode(data).encode('utf-8')
req = urllib.request.Request(url, data)
req.add_header('Referer', 'http://fanyi.youdao.com')
req.add_header('User-Agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36')
response = urllib.request.urlopen(req)
html = response.read().decode('utf-8')
target = json.loads(html)
print("翻译结果:%s" % (target['translateResult'][0][0]['tgt']))
print(req.headers)
如果同一IP在短时间内对服务器频繁访问,一旦超过服务器设置的访问频率,就会返回一个验证码页面,爬虫就不能正常获取信息,所以被拒绝,可通过以下两种方法解决:
- 延迟提交的时间
- 使用代理
3、