目录
爬取到相关数据后,需要将其保存起来,以下是相关保存方法:
1 TXT文本文件存储
1.1 实例
从https://ssr1.scrape.center/中爬取首页10部电影的数据,将相关信息存储为填系统文本格式。
import requests
from pyquery import PyQuery as pq
import re
url = 'https://ssr1.scrape.center/'
html = requests.get(url).text
doc = pq(html)
items = doc('.el-card').items()
file = open('movies.txt', 'w', encoding='utf-8')
for item in items:
# 电影名称
name = item.find('a > h2').text()
file.write(f'名称: {name}\n')
# 类别
categories = [item.text() for item in item.find('.categories button span').items()]
file.write(f'类别: {categories}\n')
# 上映时间
published_at = item.find('.info:contains(上映)').text()
published_at = re.search(r'\d{4}-\d{2}-\d{2}', published_at).group(1) if published_at and re.search(r'\d{4}-\d{2}-\d{2}', published_at) else None
file.write(f'上映时间: {published_at}\n')
# 评分
score = item.find('p.score').text()
file.write(f'评分: {score}\n')
file.write('=' * 50 + '\n')
file.close()
1.2 打开方式
1.3 简化写法
with open('movies.txt', 'w', encoding='utf-8') as file:
file.write(f'名称: {name}\n')
file.write(f'类别: {categories}\n')
file.write(f'上映时间: {published_at}\n')
file.write(f'评分: {score}\n')
2 JSON文件存储
JSON,是JavaScript对象标记,通过对象和数组的格式来表示数据。
2.1 对象和数组
对象指的是用{}包围起来的内容,数据格式是{key1:value1,key2:value2,……}。其中,key可以使用整数和字符串,后者可以是任意类型。
数组指的是用[]包围起来的内容,数据格式是["java","scriot",……],值可以是任意类型。
示例如下:
[{
"name": "Bob",
"gender": "male",
"birthday": "1992-10-18"
},{
"name": "Selina",
"gender": "female",
"birthday": "1995-10-18"
}]
2.2 读取JSON
代码:
import json
str = '''
[
{
"name": "Bob",
"gender": "male",
"birthday": "1992-10-18"
},
{
"name": "Selina",
"gender": "female",
"birthday": "1995-10-18"
}
]
'''
print(type(str))
data = json.loads(str)
print(data)
print(type(data))
如果想要获取具体内容,可以采用索引的方式:data[0]['name']或者data[0].get('name')。
比较推荐get方法,因为即使键名不存在,也不会报错,而是会返回None,另外get方法还可以传入第二个参数。
从JSON文件中读取内容,代码:
import json
with open('data.json', encoding='utf-8') as file:
str = file.read()
data = json.loads(str)
print(data)
此外,还可以直接使用load方法传入文件操作对象,将文本转化为JSON对象,代码:
import json
data = json.load(open('data.json', encoding='utf-8'))
print(data)
2.3 输出JSON
可以调用dumps方法将JSON对象转化为字符串:
import json
data = [
{
'name': 'Bob',
'gender':'male',
'birthday': '1992-10-18'
}
]
with open('data.json', 'w', encoding='utf-8') as file:
file.write(json.dumps(data))
此外,如果想要保存JSON对象的缩进格式,可以再往dumps方法中添加一个参数indent,代表缩进字符的个数。代码:
import json
data = [
{
'name': 'Bob',
'gender':'male',
'birthday': '1992-10-18'
}
]
with open('data.json', 'w', encoding='utf-8') as file:
file.write(json.dumps(data))
如果JSON对象中包含中文字符,需要添加ensure_ascii=False,结果如下:
import json
data = [
{
'name': '王伟',
'gender': '男',
'birthday': '1992-10-18'
}
]
with open('data.json', 'w', encoding='utf-8') as file:
file.write(json.dumps(data, indent=2,ensure_ascii=False))
此外,dumps也有对应的dump方法,可以直接将JSON对象全部写入文件中,代码如下:
json.dump(data,open('data.json', 'w', encoding='utf-8'),indent=2,ensure_ascii=False)
3 CSV文件存储
csv,中文叫做逗号分隔值或字符分隔值,其文件以纯为本的形式存储表格数据。CSV文件是一个字符序列,可以有任意数目的记录组成,各条记录以某种换行符分隔开。每条记录都由若干字段组成,字段之间的分隔符是其他字符或字符串,最常见的是逗号或制表符。
3.1 写入
代码:
import csv
with open('data.csv', 'w') as csvfile:
writer = csv.DictWriter(csvfile)
writer.writerow(['id', 'name', 'age'])
writer.writerow(['10001', 'Mike', 20])
writer.writerow(['10002', 'Bob', 22])
writer.writerow(['10003', 'Jordan', 21])
如果想修改列与列之间的分隔符,可以传入delimiter参数,代码:
import csv
with open('data.csv', 'w') as csvfile:
writer = csv.DictWriter(csvfile, delimiter=' ')
writer.writerow(['id', 'name', 'age'])
writer.writerow(['10001', 'Mike', 20])
writer.writerow(['10002', 'Bob', 22])
writer.writerow(['10003', 'Jordan', 21])
另外,也可以同时传入多行,代码:
import csv
with open('data.csv', 'w') as csvfile:
writer = csv.DictWriter(csvfile, delimiter=' ')
writer.writerow(['id', 'name', 'age'])
writer.writerow(['10001', 'Mike', 20],['10002', 'Bob', 22],['10003', 'Jordan', 21])
但是一般情况下,爬虫爬取的数据都是结构化的数据,一般会用字典表示:
import csv
with open('data.csv', 'w') as csvfile:
fieldnames = ['id', 'name', 'age']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
writer.writerow({'id': '10001', 'name': 'Mike', 'age': 20})
writer.writerow({'id': '10002', 'name': 'Bob', 'age': 22})
writer.writerow({'id': '10003', 'name': 'Jordan', 'age': 21})
如果想要追加写入,可以修改文件的打开模式,即把open函数的第二个参数改为a,代码:
import csv
with open('data.csv', 'a') as csvfile:
fieldnames = ['id', 'name', 'age']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writerow({'id': '10004', 'name': 'Durant', 'age': 22})
如果需要写入一行包含中文的数据,需要用open参数指定编码格式,代码:
import csv
with open('data.csv', 'a', encoding='utf-8') as csvfile:
fieldnames = ['id', 'name', 'age']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writerow({'id': '10004', 'name': '王伟', 'age': 22})
此外,也可以使用pandas库将数据保存为csv文件,代码:
import pandas as pd
data = [
{'id': '10001', 'name': 'Mike', 'age': 20},
{'id': '10002', 'name': 'Bob', 'age': 22},
{'id': '10003', 'name': 'Jordan', 'age': 21}
]
df = pd.DataFrame(data)
df.to_csv('data.csv', index=False)
3.2 读取
使用csv库读取CSV文件,代码:
import csv
with open('data.csv', 'r', encoding='utf-8') as csvfile:
reader = csv.reader(csvfile)
for row in reader:
print(row)
另外,也可以用pandas的read_csv方法将数据从CSV文件中读取出来,代码:
import pandas as pd
df = pd.read_csv('data.csv')
print(df)
如果只想读取文件里面的数据,可以把df再进一步站华为列表或者元组,代码:
import pandas as pd
df = pd.read_csv('data.csv')
data = df.values.tolist()
print(data)
另外,直接对df进行逐行遍历,同样能得到列表类型的结果,代码:
import pandas as pd
df = pd.read_csv('data.csv')
for index,row in df.iterrows():
print(row.tolist())
4 MySQL存储
关系型数据库是基于关系模型的数据库,而关系模型是通过二维表来保存的,所以关系型数据库中的数据的存储方式就是行列组成的表,每一列代表一个字段,每一行代表一条记录。
4.1 准备工作
pip3 install pymysql
4.2 链接数据库
import pymysql
db = pymysql.connect(host='localhost', user='root', password='123456', port=3306)
cursor = db.cursor()
cursor.execute('SELECT VERSION()')
data = cursor.fetchone()
print('Database version:', data)
cursor.execute("CREATE DATABASE spiders DEFAULT CHARACTER SET utf8mb4")
db.close()
这里通过PyMySQL的connect方法声明了一个MySQL连接对象db,传入的第一个参数是MySQL运行的host(IP),由于MySQL运行在本地,所以传入的是localhost,如果MySQL在远程运行,则传入其公网IP地址。后续参数分别为user、password贺port(端口,默认为3306)。
连接成功后,调用cursor方法获得了MySQL的操作游标,利用游标可以执行SQL语句。这里我们执行了两个SQL语句,直接调用execute方法即可执行。第一个SQL语句用于获得MySQL的当前版本,然后调用fetchone方法就得到了第一条数据,即版本号。第二个SQL语句用于创建数据库spiders,默认编码为UTF-8,由于该语句不是查询语句,所以执行后就成功创建了数据库spiders,可以利用这个数据库完成后续的操作。
4.3 创建表
一般来讲,创建数据库的操作执行一次就可以了。当然,也可以手动创建数据库。
以下是创建一个数据表的示例:
import pymysql
# 创建数据库后,在连接时需要额外指定一个参数 db
db = pymysql.connect(host='localhost', user='root', password='123456', port=3306, db='spiders')
cursor = db.cursor()
sql = 'CREATE TABLE IF NOT EXISTS students (id VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, age INT NOT NULL, PRIMARY KEY (id))'
cursor.execute(sql)
db.close()
4.4 插入数据
创建好数据库之后,就是往数据里插入数据了。以下是将爬取到的数据插入数据库示例:
import pymysql
id = '20120001'
user = 'Bob'
age = 20
db = pymysql.connect(host='localhost', user='root', password='123456', port=3306, db='spiders')
cursor = db.cursor()
sql = 'INSERT INTO students(id, name, age) values(%s, %s, %s)'
try:
cursor.execute(sql, (id, user, age))
db.commit() # 提交至数据库
except:
db.rollback()
db.close()
这里添加了一层异常处理 ,涉及事务的问题,以下是事务相关属性:
此外,如果另外添加字段,我们需要传入一个动态变化的字典,以下是相关代码:
import pymysql
data = {
'id': '20120001',
'name': 'Bob',
'age': 20
}
table ='students'
keys = ','.join(data.keys())
values = ','.join(['%s'] * len(data))
sql = 'INSERT INTO {table}({keys}) VALUES ({values})'.format(table=table, keys=keys, values=values)
try:
if cursor.execute(sql, tuple(data.values())):
print('Successful')
db.commit()
except:
print('Failed')
db.rollback()
db.close()
4.5 更新数据
执行SQL语句,相关代码如下:
sql = 'UPDATE students SET age = %ss WHERE name = %s'
try:
cursor.execute(sql,(25,'Bob'))
db.commit()
except:
db.rollback()
db.close()
但在实际过程中,大部分情况下需要插入数据。如果出现重复数据,则更新数据,而不是重复保存;如果数据不存在,则插入数据。
import pymysql
data = {
'id': '20120001',
'name': 'Bob',
'age': 21
}
table ='students'
keys = ','.join(data.keys())
values = ','.join(['%s'] * len(data))
sql = 'INSERT INTO {table}({keys}) VALUES ({values}) ON DUPLICATE KEY UPDATE'.format(table=table, keys=keys, values=values)
update = ','.join([f"{key} = %s" for key in data])
sql += update
try:
if cursor.execute(sql, tuple(data.values())*2):
print('Successful')
db.commit()
except:
print('Failed')
db.rollback()
db.close()
4.6 删除数据
删除数据可以直接使用DELETE语句,需要制定要删除的目标表名和删除条件,并且仍然需要使用db的commit方法才能生效,代码:
table ='students'
condition = 'age > 20'
sql = 'DELETE FROM {table} WHERE {condition}'.format(table=table, condition=condition)
try:
cursor.execute(sql)
db.commit()
except:
db.rollback()
db.close()
4.7 查询数据
查询会用到SELECT语句,代码:
sql = 'SELECT * FROM students WHERE age >= 20'
try:
cursor.execute(sql)
print('Count:', cursor.rowcount)
one = cursor.fetchone()
print('One:', one)
results = cursor.fetchall()
print('Results:', results)
print('Results Type:', type(results))
for row in results:
print(row)
except:
print('Error')
db.close()
此外,还可以用while循环加fetchone方法的组合来获取所有数据。fetchall会将结果以元组形式全部返回,如果数据量很大,占用的开销也会非常高。因此,建议使用以下方法:
sql = 'SELECT * FROM students WHERE age >= 20'
try:
cursor.execute(sql)
print('Count:', cursor.rowcount)
row = cursor.fetchone()
while row:
print('Row:', row)
row = cursor.fetchone()
except:
print('Error')
db.close()
5 MongoDB 文档存储
5.1 准备工作
pip3 install pymongo
5.2 连接MongoDB
5.3 指定数据库
5.4 指定集合
5.5 插入数据集
5.6 查询
5.7 计数
count=collection.find().count()
print(count)
count=collection.find({'age':20}).count()
ptint(count)
5.8 排序
results=collection.find().sort('name',pymongo.ASCENDING)
print([result['name'] for result in results])
5.9 偏移
5.10 更新
5.11 删除
5.12 其他操作
6 Redis缓存存储
6.1 准备工作
pip3 install redis
6.2 Redis和StrictRedis
6.3 连接Redis
6.4 键操作
6.5 字符串操作
6.6 列表操作
6.7 集合操作
6.8 有序集合操作
6.9 散列操作
7 Elasticsearch搜索引擎存储
7.1 Elasticsearch介绍
7.2 Elasticsearch相关概念
7.3 准备工作
pip3 install elasticsearch
7.4 创建索引
7.5 删除索引
7.6 插入数据
7.7 更新数据
7.8 删除数据
7.9 查询数据
8 RabbitMQ的使用
8.1 RabbitMQ的介绍
8.2 准备工作
pip3 install pika
8.3 基本使用
8.4 随用随取
8.5 优先级队列
8.6 队列持久化
8.7 实战
import pika
QUEUE_NAME='scrape'
connection=pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel=connection.channel()
channel.queue_declare(queue=QUEUE_NAME)
channel.basic_publish(exchange='',routing_key=QUEUE_NAME,body='Hello World!')
import pika
QUEUE_NAME='scrape'
connection=pika.BlockingConnetion(pika.ConnectionParameters('localhost'))
channel=connection.channel()
channel.queue_declare(queue=QUEUE_NAME)
def callback(ch,method,properties,body):
print(f'Get{body}')
channel.basic_consume(queue='scrape',auto_ack=True,on_message_callback=callback)
channel.start_consuming()
import pika
QUEUE_NAME='scrape'
connetion=pika.BlockingConnetion(pika.ConnectionParameters(host='localhost'))
channel=connection.channel()
channel.queue_declare(queue=QUEE_NAME)
while True:
data=input()
channel.basic_publish(exchange='',routing_key=QUEUE_NAME,body=data)
print(f'Put {data}')
import pika
QUEUE_NAME='scrape'
connetion=pika.BlockingConnetion(pika.ConnectionParameters(host='localhost'))
channel=connection.channel()
channel.queue_declare(queue=QUEE_NAME)
while True:
input()
method_frame,header,body=channel.basic_get(queue=QUEE_NAME,quto_ack=True)
if body:
print(f'Get{body}')
import pika
import requests
import pickle
MAX_PRIORITY=100
TOTAL_NAME='scrape_queue'
connection=pika.BlockingConnection(pika.ConnetionParameters(host='localhost'))
channel=connetion.channel()
channel.queue_declare(queue+QUEUE_NAME,durable=True)
for i in range(1,TOTAL+1):
url=f'https://ssr1.scrape.center/detail/{i}'
request=requests.Request('GET;,url')
channel.bascil_publish(exchange='',routing_key=QUEUE_NAME,properties=pika.BasciProperties(deliveivery_mo_mode=2),body=pickle.dumps(request))
print(f'Put request of {url}')
来源: