记录学习「Python 编程快速上手」一书的过程,主要涉及正则表达式、文件处理、网络爬虫等。
正则表达式
?
匹配零次或一次前面的分组。*
匹配零次或多次前面的分组。+
匹配一次或多次前面的分组。{n}
匹配 n 次前面的分组。{n,}
匹配 n 次或更多前面的分组。{,m}
匹配零次到 m 次前面的分组。{n,m}
匹配至少 n 次、至多 m 次前面的分组。{n,m}?
或*?
或+?
对前面的分组进行非贪心匹配。^spam
意味着字符串必须以 spam 开始。spam$
意味着字符串必须以 spam 结束。.
匹配所有字符,换行符除外。\d
、\w
和\s
分别匹配数字、单词和空格。\D
、\W
和\S
分别匹配出数字、单词和空格外的所有字符。[abc]
匹配方括号内的任意字符(诸如 a、b 或 c)。[^abc]
匹配不在方括号内的任意字符。
正则表达式的基本使用
1 | import re |
findall()
findall()不是返回一个 Match 对象,而是返回一个字符串列表1
2
3
4
5
6
7r'\d\d\d-\d\d\d-\d\d\d\d') phone = re.compile(
'Cell: 415-555-9999 Work: 212-555-0000') mo = phone.search(
mo.group()
'415-555-9999'
'Cell: 415-555-9999 Work: 212-555-0000') mo = phone.findall(
mo
['415-555-9999', '212-555-0000']
读写文件
文件路径 os.path
在 Windows 上,路径书写使用倒斜杠作为文件夹之间的分隔符。但在 OS X 和Linux 上,使用正斜杠作为它们的路径分隔符。1
2
3
4
5
6
7
8
9import os
Windows下执行
'usr','bin','test') os.path.join(
'usr\\bin\\test'
# linux下执行
'usr','bin','test') os.path.join(
'usr/bin/test'
获取当前目录 getcwd()
1 | # 获取当前目录 |
创建目录 makedirs()
1 | 'liuhao/test/') os.makedirs( |
os.path()模块
os.path.getsize(path)
返回 path 参数中文件的字节数os.listdir(path)
返回文件名字符串的列表
查看文件大小和文件夹内容
os.path.getsize(path)
将返回 path 参数中文件的字节数。os.listdir(path)
将返回文件名字符串的列表,包含 path 参数中的每个文件
想知道这个目录下所有文件的总字节数,就可以同时使用os.path.getsize()
和os.listdir()
。1
2
3
4
5
60 totalSize =
for file in os.listdir('/mnt/e/liuhao_data/OneDrive/code'):
'/mnt/e/liuhao_data/OneDrive/code', file)) totalSize += os.path.getsize(os.path.join(
...
print(totalSize)
31180
检查路径有效性
os.path.exists(path)
path 参数所指的文件或文件夹存在os.path.isfile(path)
path 参数存在,并且是一个文件os.path.isdir(path)
path 参数存在,并且是一个文件夹1
2
3
4
5
6
7
8
9
10
11
12
13
14'/mnt/e/liuhao_data/OneDrive/code') os.path.exists(
True
'/mnt/e/liuhao_data/OneDrive/code2') os.path.exists(
False
'/mnt/e/liuhao_data/OneDrive/code') os.path.isfile(
False
'/mnt/e/liuhao_data/OneDrive/code/xen.py') os.path.isfile(
True
'/mnt/e/liuhao_data/OneDrive/code/xen.py') os.path.isdir(
False
'/mnt/e/liuhao_data/OneDrive/code/') os.path.isdir(
True
'/mnt/e/liuhao_data/OneDrive/code') os.path.isdir(
True
读写文件过程
在 Python 中,读写文件有 3 个步骤:
1.调用open()
函数,返回一个File
对象。
2.调用File
对象的read()
或write()
方法。
3.调用File
对象的close()
方法,关闭该文件。
打开并读取文件
1 | 'sonnet29.txt') sonnet = open( |
写入文件
写模式将覆写原有的文件,从头开始,将'w'
作为第二个参数传递给open()
,以写模式打开该文件。不同的是,添加模式将
在已有文件的末尾添加文本,而不是完全覆写该变量。将'a'
作为第二个参数传递给open()
,以添加模式打开该文件。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17'sonnet29.txt', 'w') sonnet = open(
'test write file.\n') sonnet.write(
17
sonnet.close()
'sonnet29.txt') sonnet = open(
sonnet.read()
'test write file.\n'
'sonnet29.txt', 'a') sonnet = open(
'this is line 2.\n') sonnet.write(
16
sonnet.close()
'sonnet29.txt') sonnet = open(
sonnet.read()
'test write file.\nthis is line 2.\n'
组织文件
shutil 模块
复制文件和目录
shutil.copy(sourceFile, destination)
复制文件,返回目标文件名shutil.copytree(sourceDir, destination)
复制目录,包括它的所有文件和子文件夹,返回目录目录名
1 | os.getcwd() |
文件和目录的移动与重命名
shutil.move(source, destination)
如果destination
指向一个文件夹,source
文件将移动到destination
中,并保持原来的文件名。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19os.listdir()
['hello.copy', 'hello.txt', 'randomQuizGen.py', 'sonnet29.txt', 'test', 'test.t']
# 文件重命名
'hello.copy', 'hello.bak') shutil.move(
'hello.bak'
os.listdir()
['hello.bak', 'hello.txt', 'randomQuizGen.py', 'sonnet29.txt', 'test', 'test.t']
# 目录重命名
'test', 'test-bak') shutil.move(
'test-bak'
os.listdir()
['hello.bak', 'hello.txt', 'randomQuizGen.py', 'sonnet29.txt', 'test-bak', 'test.t']
# 文件移动
'test.t', 'test-bak') shutil.move(
'test-bak/test.t'
os.listdir()
['hello.bak', 'hello.txt', 'randomQuizGen.py', 'sonnet29.txt', 'test-bak']
永久删除文件和文件夹
os.unlink(path)
删除 path 处的文件os.rmdir(path)
删除 path 处的文件夹。该文件夹必须为空shutil.rmtree(path)
递归删除 path 处的文件夹
1 | # 删除文件 |
遍历目录树 os.walk()
os.walk()
函数被传入一个字符串值,即一个文件夹的路径。你可以在一个for循环语句中使用os.walk()
函数,遍历目录树,就像使用range()
函数遍历一个范围的数字一样。os.walk()
在循环的每次迭代中,返回 3 个值:
1.当前文件夹名称的字符串。
2.当前文件夹中子文件夹的字符串的列表。
3.当前文件夹中文件的字符串的列表。
1 | import os |
在子目录时,直接操作文件是有问题的,通过打印绝对路径,发现依然是处在当前路径下。需要通过os.path.join(root, name)
的方式来获取文件的全路径。1
2
3
4
5
6
7
8
9import os, re, shutil
filereg = re.compile(r'(.jpg|.pdf)$')
for foldername, subfolders, filenames in os.walk('.'):
for filename in filenames:
if filereg.search(filename) != None:
print(os.getcwd())
print(filename)
shutil.move(filename, '/mnt/e/liuhao_data/OneDrive/code/Python/')
运行结果:1
2
3
4
5
6
7
8
9
10
11
12
13liuhao@liuhao-pc:/mnt/e/liuhao_data/OneDrive/code/Python/liuhao$ python mvfile.py
The current folder is: .
Subfolders: ['test']
The current folder is: ./test
Subfolders: []
/mnt/e/liuhao_data/OneDrive/code/Python/liuhao
1.jpg
Traceback (most recent call last):
File "mvfile.py", line 14, in <module>
shutil.move(filename, '/mnt/e/liuhao_data/OneDrive/code/Python/')
File "/usr/lib/python3.5/shutil.py", line 536, in move
raise Error("Destination path '%s' already exists" % real_dst)
shutil.Error: Destination path '/mnt/e/liuhao_data/OneDrive/code/Python/1.jpg' already exists
从 Web 抓取信息
webbrowser
:是 Python 自带的,打开浏览器获取指定页面。requests
:从因特网上下载文件和网页。Beautiful Soup
:解析 HTML,即网页编写的格式。selenium
:启动并控制一个 Web 浏览器。selenium
能够填写表单,并模拟鼠标在这个浏览器中点击。
webbrowser模块打开URL
open()
函数可以启动一个新浏览器,打开指定的 URL。
1 | import webbrowser |
requests 模块
requests.get()
requests.get()
函数接受一个要下载的 URL 字符串,返回一个Response
对象。1
2
3
4
5
6
7
8
9
10
11
12
13
14import requests
'http://www.gutenberg.org/cache/epub/1112/pg1112.txt') response = requests.get(
response.status_code
200
len(response.text)
178981
100] response.text[:
'\ufeffThe Project Gutenberg EBook of Romeo and Juliet, by William Shakespeare\r\n\r\nThis eBook is for the us'
'baidu.txt', 'wb') file = open(
for chunk in res.iter_content(100000):
file.write(chunk)
...
2381
file.close()
用 BeautifulSoup 模块解析 HTML
bs4.BeautifulSoup()
函数调用时需要一个字符串,其中包含将要解析的 HTML。函数返回一个BeautifulSoup
对象。1
2
3
4
5import requests,bs4
'http://www.gutenberg.org/cache/epub/1112/pg1112.txt') res = requests.get(
noSoup = bs4.BeautifulSoup(res.text)
type(noSoup)
<class 'bs4.BeautifulSoup'>
也可以向bs4.BeautifulSoup()
传递一个File
对象,从硬盘加载一个 HTML 文件。1
2
3
4'example.html') file = open(
soup = bs4.BeautifulSoup(file)
type(soup)
<class 'bs4.BeautifulSoup'>
- 新建测试html
1
2
3
4
5
6
7
8<!-- This is the example.html example file. -->
<html><head><title>The Website Title</title></head>
<body>
<p>Download my <strong>Python</strong> book from <a href="http://
inventwithpython.com">my website</a>.</p>
<p class="slogan">Learn Python the easy way!</p>
<p>By <span id="author">Al Sweigart</span></p>
</body></html>
用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 的元素 |
select()
方法将返回一个Tag对象
的列表,这是 Beautiful Soup 表示一个 HTML元素的方式。针对 BeautifulSoup 对象中的 HTML 的每次匹配,列表中都有一个Tag对象
。Tag值可以传递给str()
函数,显示它们代表的HTML标签。Tag值也可以有attrs
属性,它将该 Tag 的所有 HTML 属性作为一个字典。
1 | 'example.html') file = open( |
通过元素的属性获取数据
Tag 对象的 get()方法让我们很容易从元素中获取属性值。向该方法传入一个属性名称的字符串,它将返回该属性的值。1
2
3
4
5
6
7
8
9
10
11
12'span') elems = soup.select(
elems
[<span id="author">Al Sweigart</span>]
'span')[0] elems = soup.select(
elems
<span id="author">Al Sweigart</span>
str(elems)
'<span id="author">Al Sweigart</span>'
'id') elems.get(
'author'
elems.attrs
{'id': 'author'}
抓取网站示例
下载网站http://xkcd.com/
所有的漫画,首页有一个Prev
按钮,让用户导航到前面的漫画。手工下载每张漫画要花较长的时间,但你可以写一个脚本,在几分钟内完成这件事。
代码需要做下列事情:
- 利用 requests 模块下载页面。
- 利用 Beautiful Soup 找到页面中漫画图像的 URL。
- 利用 iter_content()下载漫画图像,并保存到硬盘。
- 找到前一张漫画的链接 URL,然后重复。
打开一个新的文件编辑器窗口,将它保存为 downloadXkcd.py。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40# --coding=utf-8--
#! python3
import requests, os, bs4
url = 'http://xkcd.com'
os.makedirs("xkcd", exist_ok=True)
while not url.endswith('#'):
# download the page
print("Downloading page %s ..." % url)
response = requests.get(url)
print("the return code : " + str(response.status_code))
soup = bs4.BeautifulSoup(response.text, "lxml")
# find the url of the comic image
comic = soup.select('#comic img') #获取id属性为comic内的img元素
if comic == []:
print('cannot get the comic image')
else:
imageUrl = url + (comic[0].get('src'))
print("downing the image %s..." %imageUrl)
response = requests.get(imageUrl)
print("the return code : " + str(response.status_code))
# download the image
print(os.path.basename(imageUrl))
# save the image to ./xkcd
image = open(os.path.join('xkcd', os.path.basename(imageUrl)), 'wb')
for i in response.iter_content(1000):
image.write(i)
image.close()
# get the prev button'url
prev = soup.select('a[rel="prev"]')[0] # 所有名为`<a>`,并有一个rel属性,其值为prev的元素
url = 'http://xkcd.com' + prev.get('href')
print('Done.')