Joeyos's Blog Software Engineer

爬虫数据存储

2019-05-11
Quan Zhang

用解析器解析出数据之后,接下来就是存储数据。保存数据的形式可以多种多样,最简单的形式是直接保存为文本文件,如txt,json,csv等。另外,还可以保存到数据库中,如关系数据库mysql,非关系数据库MongoDB、Redis等。

txt文件存储

import requests
from pyquery import PyQuery as pq
url='https://www.zhihu.com/explore'
headers={
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"
}
html = requests.get(url,headers=headers).text
doc=pq(html)
items = doc('.explore-tab .feed-item').items()
for item in items:
    question = item.find('h2').text()
    author = item.find('.author-link-line').text()
    answer = pq(item.find('.content').html()).text()
    file = open('explore.txt','a',encoding='utf-8')#a表示在文件末尾追加
    file.write('\n'.join([question,author,answer]))
    file.write('\n'+'='*50+'\n')
    file.close()

JSON文件存储

输出JSON

import json
data=[{
    'name':'张三',
    'gender':'男',
    'birthday':'1995-10-10'
}]
with open('data.json','w') as file:
    file.write(json.dumps(data,indent=2,ensure_ascii=False))
#indent=2,以缩进2格式存储
#ensure_ascii=False可以存储汉字

打开JSON

import json
with open('data.json','r') as file:
    str = file.read()
    data=json.loads(str)
    print(data)

CSV文件

写文件

import csv
#py3不加newline='',每行会多一行空行
#w换成a可以追加
with open('data.csv','w',newline='') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerow(['id','name','age'])
    writer.writerow(['10001','Mike',20])
    writer.writerow(['10002','Bob',22])
    writer.writerow(['10003','张三','22'])
id,name,age
10001,Mike,20
10002,Bob,22
10003,张三,22

读文件

import csv
with open('data.csv','r') as csvfile:
    reader = csv.reader(csvfile)
    for row in reader:
        print(row)

Mysql存储

数据库的四属性:

  • 原子性
  • 一致性:一个状态到另一个状态
  • 隔离性:事务之间不干扰
  • 持久性:事务一旦提交,永久改变

连接数据库

import pymysql
db = pymysql.connect(host='localhost',user='root',password='zhang110',port=3306)
cursor = db.cursor()
cursor.execute('SELECT VERSION()')
data = cursor.fetchone()
print('版本:',data)
cursor.execute("CREATE DATABASE spiders DEFAULT CHARACTER SET utf8")
db.close()

创建表

import pymysql
db = pymysql.connect(host='localhost',user='root',password='zhang110',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()

插入数据

import pymysql
db = pymysql.connect(host='localhost',user='root',password='zhang110',port=3306,db='spiders')
cursor = db.cursor()

id='0001'
user='张三'
age=20
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
db = pymysql.connect(host='localhost',user='root',password='zhang110',port=3306,db='spiders')
cursor = db.cursor()
data={
    'id':'0002',
    'name':'李四',
    '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:
    db.rollback()
db.close()

更新数据

对于关键字相同的,不添加新数据,只更新数据。

import pymysql
db = pymysql.connect(host='localhost',user='root',password='zhang110',port=3306,db='spiders')
cursor = db.cursor()
data={
    'id':'0002',
    'name':'李四',
    '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([" {key}=%s".format(key=key) 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()

删除数据

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()

查询数据

import pymysql
db = pymysql.connect(host='localhost',user='root',password='zhang110',port=3306,db='spiders')
cursor = db.cursor()
sql='select * from students where age>20'
try:
    cursor.execute(sql)
    print('Count:',cursor.rowcount)
    results=cursor.fetchall()
    for row in results:
        print(row)
except:
    print('Error')
db.close()

fetall返回的是全部数据,内存开销很大,可以使用fetone+while逐条取数据:

sql='select * from students where age>20'
try:
    cursor.execute(sql)
    print('Count:',cursor.rowcount)
    row=cursor.fetchone()
    while row:
        print(row)
        row=cursor.fetchone()#指针后移
except:
    print('Error')
db.close()

非关系数据库

对于爬虫的数据存储来说,一条数据可能存在某些字段提取失败而缺失的情况,而且数据可能随时调整,数据之间还存在嵌套关系。

如果使用关系型数据存储,一是需要提前建表,而是如果存在数据嵌套关系的话,需要进行序列化操作才可以进行存储,非常不方便。

更多详细方法,参见http://api.mongodb.com/python/current/api/pymongo/collection.html

MongoDB存储

MongoDB是C++编写的非关系数据库,是一个基于分布式文件存储的开源数据库系统。其内容存储形式类似JSON。

import pymongo
#client=pymongo.MongoClient('mongodb://localhost:27017/')
client = pymongo.MongoClient(host='localhost',port=27017)
#指定test数据库
db=client.test
#指定集合
collection=db.students
#删除全部数据
collection.delete_many({})
#插入数据
student={
    'id':'0001',
    'name':'张三',
    'age':20,
    'gender':'male'
}
result=collection.insert_one(student)
#插入多条数据
student1={
    'id':'0002',
    'name':'李四',
    'age':21,
    'gender':'male'   
}
student2={
    'id':'0003',
    'name':'王五',
    'age':20,
    'gender':'male'
}
result=collection.insert_many([student1,student2])
#查询数据
result=collection.find_one({'name':'李四'})
print(result)
#多数据查询
result=collection.find({'age':20})
for it in result:
    print(it)

使用正则表达式查询:

#查询以M开头的学生
result=collection.find({'name':{'$regex':'^M.*'}})
{'name':{'$regex':'^M.*'}} #name以M开头
{'name':{'$exists':True}} #name属性是否存在
{'age':{'$type':'int'}} #age类型为int
{'age':{'$mod':[5,0]}} #age取模5余数为0
{'$text':{'$search':'Mike'}} #包含Mike字符的文本
{'$where':'obj.fans_count==obj.follows_count'} #自身粉丝数等于关注数

统计

要统计多少条数据:

count=collection.find().count()

统计符合某条件的数据条数:

count=collection.find({'age':20}).count()

排序

result = collection.find().sort('name',pymongo.ASCENDING)#升序排列

偏移

忽略前几个条目:

#忽略前两条
result = collection.find().sort('name',pymongo.ASCENDING).skip(2)

限制条目数量

result = collection.find().limit(2)

更新数据

更新’张三’的年龄为25:

condition = {'name':'张三'}
student = collection.find_one(condition)
student['age'] = 25
result = collection.update_one(condition,{'$set':student})

查询年龄大于20的数据,进行加1处理:

condition = {'age':{'$gt':20}}
result = collection.update_mang(condition,{'$inc':{'age':1}})

删除数据

result = collection.remove('name':'张三')

推荐方法:

result = collection.delete_one({'name':'张三'})

result = collection.delete_many({'age':{'$lt':25}})

删除全部条目数据:

#删除全部数据
collection.delete_many({})

Redis存储

Redis是一个基于内存的高效的键值型非关系型数据库,存储效率高,而且支持多种存储数据结构。

from redis import StrictRedis
redis = StrictRedis(host='localhost',port=6379,db=0,password='')
redis.set('name','Tom')
print(redis.get('name'))

可以通过Redis管理工具查看添加的数据。


Similar Posts

上一篇 爬虫解析库

Comments