第六周
python学习笔记和做的一些习题 (python编程快速上手——让繁琐工作自动化)
第十章
断言
它是要确保代码没有什么明显错误的地方,由assert语句执行,如果检查失败,就会抛出异常。assert语句包含一下部分:
• assert 关键字;
• 条件(即求值为 True 或 False 的表达式);
• 逗号;
• 当条件为 False 时显示的字符串。
断言针对的是程序员的错误,而不是用户的错误。对于那些可以恢复的(诸如文件没有找到,或用户输入了无效的数据),请抛出异常,而不是用assert 语句检测它。
在交通灯模拟中使用断言:
假定你在编写一个交通信号灯的模拟程序。
# 编写路口字典
market_2nd = {'ns': 'green', 'ew': 'red'}
mission_16th = {'ns': 'red', 'ew': 'green'}
#编写一个 switchLights() 函数,它接受一个路口字典作为参数,并切换红绿灯。
def switchLights(stoplight):
#添加断言 确保至少一个交通灯是红色
assert 'red' in stoplight.values(), 'Neither light is red! ' + str(stoplight)
for key in stoplight.keys():
#如果信号灯为绿色,则切换为黄色
if stoplight[key] == 'green':
stoplight[key] = 'yellow'
#黄色切换红色
elif stoplight[key] == 'yellow':
stoplight[key] = 'red'
#红色切换绿色
elif stoplight[key] == 'red':
stoplight[key] = 'green'
switchLights(market_2nd)
禁用断言:在运行 Python 时传入-O 选项,可以禁用断言。
日志
我们经常在代码中加入 print() 语句,在程序运行时输出某些变量的值,这时候我们就使用了记日志的方式来调试代码。通过它,我们可以理解程序中
发生的事,以及事情发生的顺序。
Python 的 logging 模块使得你很容易创建自定义的消息记录。这些日志消息将描述程序执行何时到达日志函数调用,并列出你指定的任何变量当时的值。另一方面,缺失日志信息表明有一部分代码被跳过,从未执行。
使用日志模块:
import logging
#启用 logging 模块,在程序运行时将日志信息显示在屏幕上
logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s - %(message)s')
logging.debug('Start of program')
#编写一个函数,计算一个数的阶乘
def factorial(n):
logging.debug('Start of factorial(%s)' % (n))
total = 1
for i in range(n + 1):
total *= i
logging.debug('i is ' + str(i) + ', total is ' + str(total))
logging.debug('End of factorial(%s)' % (n))
return total
print(factorial(5))
logging.debug('End of program')
结果为:
可以看出,阶乘是从0开始的,应将for i in range(n + 1),改为for i in range(1,n + 1),结果为:
日志级别:“日志级别”提供了一种方式,按重要性对日志消息进行分类。5 个日志级别下表所示,从最不重要到最重要。利用不同的日志函数,消息可以按某个级别记入日志。
级别 | 日志函数 | 描述 |
DEBUG | logging.debug() | 最低级别。用于小细节。通常只有在诊断问题时,你才会关心这些消息。 |
INFO | logging.info() | 用于记录程序中一般事件的信息,或确认一切工作正常。 |
WARNING | logging.warning() | 用于表示可能的问题,他不会阻止程序的工作,但将来可能会。 |
ERROR | loggong.error() | 用于记录错误,他导致程序做某事失败。 |
CRITICAL | logging.critical() | 最高级别。用于表示知名的错误,他导致或将要导致程序完全停止。 |
禁用日志:在调试完程序后,你可能不希望所有这些日志消息出现在屏幕上。logging.disable() 函数禁用了这些消息。
将日志记录到文件:除了将日志消息显示在屏幕上,还可以将它们写入文本文件。logging.basicConfig() 函数接受 filename 关键字参数,
IDLE的调试器
“调试器”是 IDLE 的一项功能,让你每次执行一行程序。调试器将运行一行代
码,然后等待你告诉它继续。
要启用 IDLE 的调试器,就在交互式环境窗口中点击 Debug>>Debugger。这将打开调试控制(Debug Control)窗口:
当调试控制窗口出现后,勾选全部 4 个复选框:Stack、Locals、Source 和 Globals。这样窗口将显示全部的调试信息。调试控制窗口显示时,只要你从文件编辑器运行程序,调试器就会在第一条指令之前暂停执行,并显示下面的信息:
• 将要执行的代码行;
• 所有局部变量及其值的列表;
• 所有全局变量及其值的列表。你会发现旁边还有这些键:
Go:点击 Go 按钮将导致程序正常执行至终止,或到达一个“断点”。如果你完成了调试,希望程序正常继续,就点击 Go 按钮。
Step:点击 Step 按钮将导致调试器执行下一行代码,然后再次暂停。如果变量的值发生了变化,调试控制窗口的全局变量和局部变量列表就会更新。如果下一行代码是一个函数调用,调试器就会“步入”那个函数,跳到该函数的第一行代码。
Over:点击 Over 按扭将执行下一行代码,与 Step 按钮类似。但是,如果下一行代码是函数调用,Over 按钮将“跨过”该函数的代码。该函数的代码将以全速执行,调试器将在该函数返回后暂停。
Out:点击 Out 按钮将导致调试器全速执行代码行,直到它从当前函数返回。如果你用 Step 按钮进入了一个函数,现在只想继续执行指令,直到该函数返回,那就点击Out 按钮,从当前的函数调用“走出来”。
Quit:如果你希望完全停止调试,不必继续执行剩下的程序,就点击 Quit 按钮。
调试一个数字相加的程序:
print('Enter the first number to add:')
first = input()
print('Enter the second number to add:')
second = input()
print('Enter the third number to add:')
third = input()
print('The sum is ' + first + second + third)
我们可以很明显的看出这个程序虽然运行出来,但得到的结果并不是我们想要的.
我们在启用调试控制窗口,再次运行上面的程序:
程序启动时将暂停在第一行,点击Over,执行第一个print()调用:
调试控制窗口将更新到第几行,文件编辑窗口的第几行就会高亮显示:
继续点击Over,输入值,直到调试器运行到最后一行:
可以看到,在全局变量的部分,第一个、第二个和第三个变量设置为字符串值,而不是整型值。当最后一行执行时,这些字符串连接起来,而不是加起来,导致了这个缺陷。
用调试器单步执行程序很有用,但也可能很慢。你常常希望程序正常运行,直到它到达特定的代码行。你可以使用断点,让调试器做到这一点。
断点:“断点”可以设置在特定的代码行上,当程序执行到达该行时,它迫使调试器暂停。
#模拟投掷1000次硬币
import random
heads = 0
for i in range(1, 1001):
if random.randint(0, 1) == 1:
heads = heads + 1
if i == 500:
print('Halfway done!')
print('Heads came up ' + str(heads) + ' times.')
运行结果:
设置断点:右键>>set breakpoint
带有断点的代码行会在文件编辑器中以黄色高亮显示。如果在调试器下运行该程序,开始它会暂停在第一行,像平时一样。但如果点击 Go,程序将全速运行,直到设置了断点的代码行。然后可以点击 Go、Over、Step 或 Out,正常继续。
第十章节习题
1.写一条 assert 语句,如果变量 spam 是一个小于 10 的整数,就触发AssertionError。
assert(spam>=10,'The spam is not less than 10')
2.编写一条 assert 语句,如果 eggs 和 bacon 包含的字符串彼此相同,而且不论大小写如何,就触发 AssertionError(也就是说,‘hello’ 和 ‘hello’ 被认为相同,‘goodbye’ 和 ‘GOODbye’ 也被认为相同)。
assert(eggs.lower()!=bacon.lower().'The eggs and bacon variables are the same.')
3.编写一条 assert 语句,总是触发 AssertionError。
assert(False,'This assertion always triggers.')
4.为了能调用 logging.debug(),程序中必须加入哪两行代码?
import logging
logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s - %(message)s')
5.为了让 logging.debug() 将日志消息发送到名为 programLog.txt 的文件中,程序必须加入哪两行代码?
import logging
logging.basicConfig(filename=programLog.txt,level=logging.DEBUG, format=' %(asctime)s - %(levelname)s - %(message)s')
6.5 个日志级别是什么?
级别 | 日志函数 | 描述 |
DEBUG | logging.debug() | 最低级别。用于小细节。通常只有在诊断问题时,你才会关心这些消息。 |
INFO | logging.info() | 用于记录程序中一般事件的信息,或确认一切工作正常。 |
WARNING | logging.warning() | 用于表示可能的问题,他不会阻止程序的工作,但将来可能会。 |
ERROR | loggong.error() | 用于记录错误,他导致程序做某事失败。 |
CRITICAL | logging.critical() | 最高级别。用于表示知名的错误,他导致或将要导致程序完全停止。 |
7.你可以加入哪一行代码,禁用程序中所有的日志消息?
logging.disable(logging.CRITICAL)
8.显示同样的消息,为什么使用日志消息比使用 print() 要好?
可以禁用日志消息,不必删除日志函数调用。可以选择禁用低级别日志消
息。可以创建日志消息。日志消息提供了时间戳。
9.调试控制窗口中的 Step、Over 和 Out 按钮有什么区别?
Step 按扭让调试器进入函数调用。Over 按钮将快速执行函数调用,不会单
步进入其中。Out 按钮将快速执行余下的代码,直到走出当前所处的函数.
10.在点击调试控制窗口中的 Go 按钮后,调试器何时会停下来?
点击 Go,程序将全速运行,直到设置了断点的代码行。
11.什么是断点?
“断点”可以设置在特定的代码行上,当程序执行到达该行时,它迫使调试器暂停。
12.在 IDLE 中,如何在一行代码上设置断点?
右键>>set breakpoint
第十章节 实践项目
下面程序的意图是一个简单的硬币抛掷猜测游戏。玩家有两次猜测机会(这
是一个简单的游戏)。但是,程序中有一些缺陷。让程序运行几次,找出缺陷,使该程序能正确运行,下面是修改过的程序:
import random
guess = ''
while guess not in ('heads', 'tails'):
print('Guess the coin toss! Enter heads or tails:')
guess = input()
n = random.randint(0, 1) # 0 is tails, 1 is heads
toss = {0:'heads',1:'tails'}
if toss[n] == guess:
print('You got it!')
else:
print('Nope! Guess again!')
guesss = input()
if toss[n] == guess:
print('You got it!')
else:
print('Nope. You are really bad at this game.')
第十一章 从Web抓取信息
使用webbrowser模块
可以在浏览器中打开你想要打开的网址.
用request模块从Web下载文件
requests.get()函数:接受一个要下载的URL字符串。通过在 requests.get()的返回值上调用 type(),你可以看到它返回一个 Response 对象,其中包含了 Web 服务器对你的请求做出的响应。
在Response对象上调用raise_for_status():检查文件是否下载成功。
下载并保存到文件的完整过程如下:
1.调用 requests.get()下载该文件。
2.用’wb’调用 open(),以写二进制的方式打开一个新文件。
3.利用 Respose 对象的 iter_content()方法做循环。iter_content()方法在循环的每次迭代中,返回一段内容。每一段都是 bytes 数据类型,你需要指定一段包含多少字节。
4.在每次迭代中调用 write(),将内容写入该文件。
5.调用 close()关闭该文件。
用BeautifulSoup模块解析网址
从HTML创建一个BeautifulSoup对象:
bs4.BeautifulSoup()函数调用时需要一个字符串,其中包含将要解析的HTML。bs4.BeautifulSoup()函数返回一个 BeautifulSoup 对象。
用select()方法寻找元素:
传递给select()方法的选择器 | 将匹配 |
soup.select(‘div’) | 所有名为< div >的元素 |
soup.select(’#author’) | 带有id属性为author的元素 |
soup.select(’.notice’) | 所有使用CSS class属性名为notice的元素 |
soup.select(‘div span’) | 所有在< div >元素之内的< span >元素 |
soup.select(‘div > span’) | 所有直接在< div >元素之内的< span >元素,中间没有其他元素 |
soup.select(‘input[name]’) | 所有名为< input >,并有一个name属性,其值无所谓的元素 |
soup.select(‘input[type=“button”]’) | 所有名为< input >,并有一个type属性,其值为button的元素 |
用selenium模块控制浏览器
导入selenium 的模块需要一点技巧。不是import selenium,而是要运行from selenium import webdrive
在页面中寻找元素:WebDriver 对象有好几种方法,用于在页面中寻找元素。它们被分成find_element_*和find_elements_*方法。
selenium的WebDriver方法,用于寻找元素:
方法名 | 返回的WebElement对象/列表 |
browser.find_element_by_class_name(name) browser.find_elements_by_class_name(name) | 使用CSS类name的元素 |
browser.find_element_by_css_selector(selector) browser.find_elements_by_css_selector(selector) | 匹配 CSS selector 的元素 |
browser.find_element_by_id(id) browser.find_elements_by_id(id) | 匹配 id 属性值的元素 |
browser.find_element_by_link_text(text) browser.find_elements_by_link_text(text) | 完全匹配提供的 text 的< a >元素 |
browser.find_element_by_partial_link_text(text) browser.find_elements_by_partial_link_text(text) | 包含提供的 text 的< a >元素 |
browser.find_element_by_name(name) browser.find_elements_by_name(name) | 匹配 name 属性值的元素 |
browser.find_element_by_tag_name(name) browser.find_elements_by_tag_name(name) | 匹配标签 name 的元素(大小写无关,< a >元素匹配’a’和’A’) |
WebElement的属性和方法:
属性或方法 | 描述 |
tag_name | 标签名,例如 'a’表示< a >元素 |
get_attribute(name) | 该元素 name 属性的值 |
text | 该元素内的文本,例如< span >hello</ span>中的’hello’ |
clear() | 对于文本字段或文本区域元素,清除其中输入的文本 |
is_displayed() | 如果该元素可见,返回 True,否则返回 False |
is_enabled() | 对于输入元素,如果该元素启用,返回 True,否则返回 False |
is_selected() | 对于复选框或单选框元素,如果该元素被选中,选择 True,否则返回 False |
location | 一个字典,包含键’x’和’y’,表示该元素在页面上的位置 |
点击页面: find_element_*和 find_elements_*方法返回的 WebElement 对象有一个 click()方法,模拟鼠标在该元素上点击。这个方法可以用于链接跳转,选择单选按钮,点击提交按钮,或者触发该元素被鼠标点击时发生的任何事情。
填写并提交表单:向 Web 页面的文本字段发送击键,只要找到那个文本字段的< input >或< textarea >元素,然后调用 send_keys()方法。
发送特殊键:selenium 有一个模块,针对不能用字符串值输入的键盘击键。它的功能非常类似于转义字符。这些值保存在selenium.webdriver.common.keys 模块的属性中。由于这个模块名非常长,所以在程序顶部运行 from selenium.webdriver. common.keys import Keys 就比较容易。如果这么做,原来需要写 from selenium.webdriver.common.keys 的地方,就只要写 Keys。
selenium.webdriver. common.keys模块中的常用变量:
属性 | 含义 |
Keys.DOWN, Keys.UP, Keys.LEFT,Keys.RIGHT | 键盘箭头键 |
Keys.ENTER, Keys.RETURN | 回车和换行键 |
Keys.HOME, Keys.END, Keys.PAGE_DOWN,Keys.PAGE_UP | Home 键、End 键、PageUp 键和 Page Down 键 |
Keys.ESCAPE, Keys.BACK_SPACE,Keys.DELETE | Esc、Backspace 和字母键 |
Keys.F1, Keys.F2, . . . , Keys.F12 | 键盘顶部的 F1到 F12键 |
Keys.TAB | Tab 键 |
点击浏览器按钮:
方法 | 浏览器按钮 |
browser.back() | 点击“返回”按钮。 |
browser.forward() | 点击“前进”按钮。 |
browser.refresh() | 点击“刷新”按钮。 |
browser.quit() | 点击“关闭窗口”按钮。 |