Python爬虫从零开始(五)

Python 基础

Python 输入输出

读文件

读写文件是最常见的IO操作。Python内置了读写文件的函数,用法和C是兼容的。
要以读文件的模式打开一个文件对象,使用Python内置的open()函数,传入文件名和标示符:

1
>>> f = open('/Users/michael/test.txt', 'r')

标示符’r’表示读,这样,我们就成功地打开了一个文件。

如果文件不存在,open()函数就会抛出一个IOError的错误,并且给出错误码和详细的信息告诉你文件不存在:

1
2
3
4
>>> f=open('/Users/michael/notfound.txt', 'r')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: '/Users/michael/notfound.txt'

如果文件打开成功,接下来,调用read()方法可以一次读取文件的全部内容,Python把内容读到内存,用一个str对象表示:

1
2
>>> f.read()
'Hello, world!'

最后一步是调用close()方法关闭文件。文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的:

1
>>> f.close()

由于文件读写时都有可能产生IOError,一旦出错,后面的f.close()就不会调用。所以,为了保证无论是否出错都能正确地关闭文件,我们可以使用try ... finally来实现:

1
2
3
4
5
6
try:
f = open('/path/to/file', 'r')
print(f.read())
finally:
if f:
f.close()

但是每次都这么写实在太繁琐,所以,Python引入了with语句来自动帮我们调用close()方法:

1
2
with open('/path/to/file', 'r') as f:
print(f.read())

这和前面的try ... finally是一样的,但是代码更佳简洁,并且不必调用f.close()方法。

调用read()会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以,要保险起见,可以反复调用read(size)方法,每次最多读取size个字节的内容。另外,调用readline()可以每次读取一行内容,调用readlines()一次读取所有内容并按行返回list。因此,要根据需要决定怎么调用。

如果文件很小,read()一次性读取最方便;如果不能确定文件大小,反复调用read(size)比较保险;如果是配置文件,调用readlines()最方便:

1
2
3
for line in f.readlines():
print(line.strip()) # 把末尾的'\n'删掉
file-like Object

open()函数返回的这种有个read()方法的对象,在Python中统称为file-like Object。除了file外,还可以是内存的字节流,网络流,自定义流等等。file-like Object不要求从特定类继承,只要写个read()方法就行。

StringIO就是在内存中创建的file-like Object,常用作临时缓冲。

二进制文件

前面讲的默认都是读取文本文件,并且是UTF-8编码的文本文件。要读取二进制文件,比如图片、视频等等,用'rb'模式打开文件即可:

1
2
3
>>> f = open('/Users/michael/test.jpg', 'rb')
>>> f.read()
b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六进制表示的字节

字符编码

要读取非UTF-8编码的文本文件,需要给open()函数传入encoding参数,例如,读取GBK编码的文件:

1
2
3
>>> f = open('/Users/michael/gbk.txt', 'r', encoding='gbk')
>>> f.read()
'测试'

遇到有些编码不规范的文件,你可能会遇到UnicodeDecodeError,因为在文本文件中可能夹杂了一些非法编码的字符。遇到这种情况,open()函数还接收一个errors参数,表示如果遇到编码错误后如何处理。最简单的方式是直接忽略:

1
>>> f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')

写文件

写文件和读文件是一样的,唯一区别是调用open()函数时,传入标识符'w'或者'wb'表示写文本文件或写二进制文件:

1
2
3
>>> f = open('/Users/michael/test.txt', 'w')
>>> f.write('Hello, world!')
>>> f.close()

你可以反复调用write()来写入文件,但是务必要调用f.close()来关闭文件。当我们写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。只有调用close()方法时,操作系统才保证把没有写入的数据全部写入磁盘。忘记调用close()的后果是数据可能只写了一部分到磁盘,剩下的丢失了。所以,还是用with语句来得保险:

1
2
with open('/Users/michael/test.txt', 'w') as f:
f.write('Hello, world!')

要写入特定编码的文本文件,请给open()函数传入encoding参数,将字符串自动转换成指定编码。

细心的童鞋会发现,以'w'模式写入文件时,如果文件已存在,会直接覆盖(相当于删掉后新写入一个文件)。如果我们希望追加到文件末尾怎么办?可以传入'a'以追加(append)模式写入。

所有模式的定义及含义可以参考Python的官方文档。

读写CSV文件

csv文件的读取

前期工作:在定义的py文件里边创建一个excel文件,并另存为csv文件,放入三行数据,我这里是姓名+年龄(可以自己随意写)

首先我们要在python环境里导入csv板块

1
2
# -*- coding: utf-8 -*-
import csv

然后我们定义一个csv文件的变量csv_file,然后通过open对此文件进行打开,打开模式采用‘r’(read:读模式)

1
2
3
4
5
with open("my.csv", "r")as f:
print(f)
csv_file = csv.reader(f)
for stu in csv_file:
print(stu)

输出结果如下

1
2
3
4
<_csv.reader object at 0x000001D3A1A1B730>
['zhangsan','32']
['wangwu','34']
['zhaoliu','35']

csv文件的写入

在开始前我们要定义两组数据,进行下面的写入

1
2
stu1 = ['marry',26]
stu2 = ['bob',23]

1.写入的第一步同样也是打开文件,因为我们是要写入,所以我们用的模式就是'a'模式,追加内容,至于"newline="就是说因为我们的csv文件的类型,如果不加这个东西,当我们写入东西的时候,就会出现空行。

1
out = open('Stu_csv.csv','a', newline='')

2.下面我们定义一个变量进行写入,将刚才的文件变量传进来,dialect就是定义一下文件的类型,我们定义为excel类型

1
csv_write = csv.writer(out,dialect='excel')

3.然后进行数据的写入啦,写入的方法是writerow,通过写入模式对象,调用方法进行写入

1
2
csv_write.writerow(stu1)
csv_write.writerow(stu2)

4.最后各位可以用你们最熟悉的一句语法进行漂亮的收尾.

1
print ("write over")

具体的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
import csv

#csv 写入
stu1 = ['marry',26]
stu2 = ['bob',23]
#打开文件,追加a
out = open('Stu_csv.csv','a', newline='')
#设定写入模式
csv_write = csv.writer(out,dialect='excel')
#写入具体内容
csv_write.writerow(stu1)
csv_write.writerow(stu2)
print ("write over")

mongoDB

安装MongoDB

首先到 MongoDB官网 下载安装MongoDB。并且到 MongoChef 官网下载安装可视化管理工具MongoChef studio3t

参考文件

安装PyMongo 驱动

用可视化管理工具新建mydb数据库并添加student集合
使用以下命令安装pymongo 驱动

1
python -m pip install pymongo

也可以指定安装的版本:

1
$ python -m pip install pymongo==3.5.1

更新 pymongo 命令:

1
$ python -m pip install --upgrade pymongo

测试 PyMongo

接下来我们可以创建一个测试文件 demo_test_mongodb.py,代码如下:

demo_test_mongodb.py 文件代码:

1
2
3
#!/usr/bin/python3

import pymongo

执行以上代码文件,如果没有出现错误,表示安装成功。

创建数据库

创建一个数据库
创建数据库需要使用 MongoClient 对象,并且指定连接的 URL 地址和要创建的数据库名。

如下实例中,我们创建的数据库 runoobdb :

实例

1
2
3
4
5
6
#!/usr/bin/python3

import pymongo

myclient = pymongo.MongoClient("mongodb://localhost:27017/")
mydb = myclient["runoobdb"]

注意: 在 MongoDB 中,数据库只有在内容插入后才会创建! 就是说,数据库创建后要创建集合(数据表)并插入一个文档(记录),数据库才会真正创建。

判断数据库是否已存在

我们可以读取 MongoDB 中的所有数据库,并判断指定的数据库是否存在:

实例

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/python3

import pymongo

myclient = pymongo.MongoClient('mongodb://localhost:27017/')

dblist = myclient.list_database_names()
# dblist = myclient.database_names()
if "runoobdb" in dblist:
print("数据库已存在!")

注意:database_names 在最新版本的 Python 中已废弃,Python3.7+ 之后的版本改为了 list_database_names()。

创建集合

MongoDB 中的集合类似 SQL 的表。

创建一个集合
MongoDB 使用数据库对象来创建集合,实例如下:

实例

1
2
3
4
5
6
7
#!/usr/bin/python3
import pymongo

myclient = pymongo.MongoClient("mongodb://localhost:27017/")
mydb = myclient["runoobdb"]

mycol = mydb["sites"]

注意: 在 MongoDB 中,集合只有在内容插入后才会创建! 就是说,创建集合(数据表)后要再插入一个文档(记录),集合才会真正创建。

判断集合是否已存在

我们可以读取 MongoDB 数据库中的所有集合,并判断指定的集合是否存在:

实例

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/python3
import pymongo

myclient = pymongo.MongoClient('mongodb://localhost:27017/')
mydb = myclient['runoobdb']

collist = mydb. list_collection_names()
# collist = mydb.collection_names()
if "sites" in collist: # 判断 sites 集合是否存在
print("集合已存在!")

添加文档操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pymongo import MongoClient

# 连接服务器
conn = MongoClient("localhost", 27017)

# 连接数据库
db = conn.mydb

# 获取集合
collection = db.student

# 添加文档
# collection.insert({"name":"abc", "age":19, "gender":1,"address":"北京", "isDelete":0})
collection.insert_many([{"name": "abc1", "age": 19, "gender": 1, "address": "北京", "isDelete": 0},
{"name": "abc2", "age": 19, "gender": 1, "address": "北京", "isDelete": 0}])

# 断开
conn.close()

查询文档操作

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
57
58
import pymongo
from pymongo import MongoClient
from bson.objectid import ObjectId # 用于ID查询

# 连接服务器
conn = MongoClient("localhost", 27017)

# 连接数据库
db = conn.mydb

# 获取集合
collection = db.student

# 查询文档

# 查询部分文档
'''
res = collection.find({"age":{"$gt":18}})
for row in res:
print(row)
print(type(row))
'''

# 查询所有文档
'''
res = collection.find()
for row in res:
print(row)
print(type(row))
'''

# 统计查询
'''
res = collection.find({"age":{"$gt":18}}).count()
print(res)
'''

# 根据id查询
'''
res = collection.find({"_id":ObjectId("5995084b019723fe2a0d8d14")})
print(res[0])
'''

# 排序
'''
# res = collection.find().sort("age")#升序
res = collection.find().sort("age", pymongo.DESCENDING)
for row in res:
print(row)
'''

# 分页查询
res = collection.find().skip(3).limit(5)
for row in res:
print(row)

# 断开
conn.close()

更新文档操作

1
2
3
4
5
6
7
8
9
10
11
12
13
from pymongo import MongoClient

# 连接服务器
conn = MongoClient("localhost", 27017)
# 连接数据库
db = conn.mydb
# 获取集合
collection = db.student

collection.update({"name": "lilei"}, {"$set": {"age": 25}})

# 断开
conn.close()

删除文档操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pymongo import MongoClient

# 连接服务器
conn = MongoClient("localhost", 27017)
# 连接数据库
db = conn.mydb
# 获取集合
collection = db.student

collection.remove({"name": "lilei"})
# 全部删除
collection.remove()
# 断开
conn.close()