python网络爬虫开发实战之数据的存储

目录

1 TXT文本文件存储

1.1 实例

1.2 打开方式

1.3 简化写法

2 JSON文件存储

2.1 对象和数组

2.2 读取JSON

2.3 输出JSON

3 CSV文件存储

3.1 写入

3.2 读取

4 MySQL存储

4.1 准备工作

4.2 链接数据库

4.3 创建表

4.4 插入数据

4.5 更新数据

4.6 删除数据

4.7 查询数据

5 MongoDB 文档存储

​编辑

5.1 准备工作

5.2 连接MongoDB

5.3 指定数据库

5.4 指定集合

5.5 插入数据集

5.6 查询

5.7 计数

5.8 排序

5.9 偏移

​编辑5.10 更新

5.11 删除

5.12 其他操作

6 Redis缓存存储

6.1 准备工作

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 准备工作

7.4 创建索引

7.5 删除索引

7.6 插入数据

7.7 更新数据

7.8 删除数据

7.9 查询数据

8  RabbitMQ的使用

8.1 RabbitMQ的介绍

8.2 准备工作

8.3 基本使用

8.4 随用随取

8.5 优先级队列

8.6 队列持久化

8.7 实战


爬取到相关数据后,需要将其保存起来,以下是相关保存方法:

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}')

来源:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值