Python爬虫:保姆级教你完成数据存储

2024-04-17 07:08

本文主要是介绍Python爬虫:保姆级教你完成数据存储,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

数据存储

在前面的几篇文章中,我分别总结了:

  • 什么是爬虫

  • requests模块总结

  • 正则表达式提取数据

  • XPath解析数据

  • Beautiful Soup解析数据

  • pyquery解析数据

  • jsonpath提取json数据

在上面的几篇文章当中都有实战项目进行配合,帮助各位看我的文章的小伙伴可以亲切的感受到爬虫的乐趣。在实战的过程当中很多时候也会将数据保存起来放在Excel文件或者是文本文件当中,但是却没有对数据的存储做详细的介绍,因此本次文章我就打算为大家带来数据存储的保姆级教程!

文件存储

文件储存的形式多种多样,比如说保存成TXT纯文本形式,也可以保存为JSON格式、CSV格式等等。

TXT文本存储

将数据保存到TXT文件的操作是非常简单的,而且TXT文本几乎兼容任何平台,但是也是存在缺点的,那就是不利于检索。所以如果对检索数据的要求不高,追求第一的话,可以采用TXT文本存储。

基本示例

爬取小说网,链接如下:

https://www.soxscc.com/BianShenJueSeShaoNv/1001322.html

首先可以使用requests将网页源码获取下来,然后使用pyquery解析库解析,提取其中的小说内容。

具体代码如下所示:

import requests
from pyquery import PyQuery as pqurl = 'https://w、w.soxscc.com/BianShenJueSeShaoNv/1001322.html'
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36'
}
html = requests.get(url, headers=headers).text
doc = pq(html)
text = doc('#con1001322').text()
file = open('都市仙尊.txt', 'w', encoding='utf-8')
file.write(text)
print('获取完毕')

通过pyquery将小说内容全部提取出来,然后利用Python提供的open( )方法打开文本文件,获取一个文件操作对象,这里赋值为file,接着利用file对象的write ()方法将提取的内容写入文本文件。

这里的open()方法的第一个参数即要保存的目标文件的名称,第二个参数为w,代表写入。另外还指定了文本的编码格式为utf-8。最后,还需要调用close()方法是来关闭文件内容。

运行程序,可以发现本地生成了一个都市仙尊.txt的文件,其内容如下所示:

就是这样简单的将小说内容成功的保存成了文本形式。

打开方式

在上面的示例中,open()方法的第二个参数设置成了w,这样写入文本时都是以写入的方式打开一个文件,如果文件已经存在,就将其覆盖,如果文件不存在,则创建新的文件。

关于文件的打开方式,其实还有其他几种,这里做简单的介绍。

  • [ ] r:以只读的方式打开文件。文件的指针将会放在文件的开头。这是默认模式。

  • [ ] rb:以二进制只读方式。文件指针将会放在文件的开头

  • [ ] r+:以读写的方式打开一个文件。文件指针将放在文件的开头。

  • [ ] rb+:以二进制读写方式打开一个文件。文件指针将会放在文件的开头。

  • [ ] w:以写入方式打开一个文件。如果该文件已经存在,则将其覆盖。如果该文件不存在,则创建新的文件。

  • [ ] wb:以二进制写入方式打开一个文件。如果该文件已经存在,则将其覆盖。如果该文件不存在,则创建新的文件。

  • [ ] w+:以读写方式打开一个文件。如果该文件已经存在,则将其覆盖。如果该文件不存在,则创建新的文件。

  • [ ] wb+:以二进制读写格式打开一个文件。如果该文件已经存在,则将其覆盖。如果该文件不存在,则创建新的文件。

  • [ ] a:以追加方式打开一个文件。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容会被写入到已有内容之后。如果该文件不存在,则创建新的文件。

  • [ ] ab:以二进制追加方式打开一个文件。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容会被写入到已有内容之后。如果该文件不存在,则创建新的文件。

  • [ ] a+:以读写方式打开一个文件。如果文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果文件不存在,则创建新文件来读写。

  • [ ] ab+:以二进制追加方式打开一个文件。如果该文件已存在,则文件指针将会放在文件的结尾。如果该文件不存在,则创建新文件用于读写。

JSON文件存储

JSON,全称是javascript Object Notation,也就是javascript对象标记,它通过对象和数组来表示数据,构造简洁但是结构化程度高,是一种轻量级的数据交互格式。

对象和数组
  • [ ] 对象:它在javascript中是使用花括号{ }包裹起来的内容,数据结构为{key1:value1,key2:value2,...}的键值对结构。

  • [ ] 数组:数组在javascript中是方括号[ ]包裹起来的内容,数据结构为["java","python","C++"]的索引结构

读取JSON

Python为我们提供了简单易用的JSON库来实现JSON文件的读写操作,我们可以调用json库的loads()方法将JSON文本字符串JSON对象,可以通过dumps()方法将JSON对象转为文本字符串。

例如,这里有一段JSON形式的字符串,它是str类型,我们用Python将其转为可操作的数据结构。

具体代码如下所示:

import jsonstring = '''
{ "store": {"book": [ { "category": "reference","author": "Nigel Rees","title": "Sayings of the Century","price": 8.95},{ "category": "fiction","author": "Evelyn Waugh","title": "Sword of Honour","price": 12.99},{ "category": "fiction","author": "Herman Melville","title": "Moby Dick","isbn": "0-553-21311-3","price": 8.99},{ "category": "fiction","author": "J. R. R. Tolkien","title": "The Lord of the Rings","isbn": "0-395-19395-8","price": 22.99}],"bicycle": {"color": "red","price": 19.95}}
}
'''
print(type(string))
data = json.loads(string)
print(data)
print(type(data))

运行结果:

<class 'str'>
{'store': {'book': [{'category': 'reference', 'author': 'Nigel Rees', 'title': 'Sayings of the Century', 'price': 8.95}, {'category': 'fiction', 'author': 'Evelyn Waugh', 'title': 'Sword of Honour', 'price': 12.99}, {'category': 'fiction', 'author': 'Herman Melville', 'title': 'Moby Dick', 'isbn': '0-553-21311-3', 'price': 8.99}, {'category': 'fiction', 'author': 'J. R. R. Tolkien', 'title': 'The Lord of the Rings', 'isbn': '0-395-19395-8', 'price': 22.99}], 'bicycle': {'color': 'red', 'price': 19.95}}}
<class 'dict'>

这里使用loads()方法将字符串转为JSON对象。由于最外层是大括号,所以最终的类型是字典类型。

值得注意的是,JSON的数据需要用双引号来包围,不能使用单引号。

否则会出现JSON解析错误提示。

如果json文本中读取内容,假如这里有一个data.json这个文件,其内容就是上面所定义的json字符串,我们可以将文本内容读出,再通过json.loads()方法将其转换为Python的JSON对象。

具体代码如下所示:

import jsonwith open('data.json', 'r') as file:text_json = file.read()
data_json = json.loads(text_json)
print(data_json)
print(type(data_json))

运行上面的代码你会发现,逻辑其实是一样的。

写入JSON

另外,可以调用dumps()方法,可以将JSON对象转换为字符串。

具体代码如下所示:

import jsonstring = '''
{ "store": {"book": [ { "category": "reference","author": "Nigel Rees","title": "Sayings of the Century","price": 8.95},{ "category": "fiction","author": "Evelyn Waugh","title": "Sword of Honour","price": 12.99},{ "category": "fiction","author": "Herman Melville","title": "Moby Dick","isbn": "0-553-21311-3","price": 8.99},{ "category": "fiction","author": "J. R. R. Tolkien","title": "The Lord of the Rings","isbn": "0-395-19395-8","price": 22.99}],"bicycle": {"color": "red","price": 19.95}}
}
'''
with open('data2.json', 'w', encoding='utf-8') as file:file.write(json.dumps(string, ensure_ascii=False))

csv文件存储

CSV,全称为Comma-Separated Values,中文可以叫做逗号分隔值或字符分隔值,其文件以纯文件形式存储表格数据。该文件是一个字符序列,可以由任意数目的记录组成,记录间以某种换行符号分隔。每条记录由字段组成,字段间的分隔符是其他字符或字符串,最常见的逗号或制表符。不过所有记录都有完全相同的字段序列,相当于一个结构化表的纯文本形式。

它比Excel文件更加简洁,XLS文本是电子表格,它包含了文本、数值、公式和格式等内容 ,而CSV中不包含这些内容,就是特定字符字符分隔的纯文本,结构简单清晰,所以,有时候用CSV来保存数据是比较方便的。

  • 写入

先看一个简单的例子:

import csvwith open('data.csv', 'w', newline='') as file:writer = csv.writer(file)writer.writerow(['id', 'name', 'age'])writer.writerow(['10001', 'Mike', 20])writer.writerow(['10002', 'Bob', 23])writer.writerow(['10003', 'Jordan', 25])

接下来就对上面的代码做简单的概述:

首先打开data.csv文件,然后指定打开模式为w(即写入),newline参数为空,否则会出现多出一个空行,获得文件句柄,随后调用csv库的writer()方法初始化写入对象,传入该句柄,然后调用writerow()方法传入每行的数据即可完成写入。

如果想要修改列与列之间的分隔符,只需要传入一个delimiter参数。

writer = csv.writer(file, delimiter=' ')

另外,还可以调用writerows()方法同时写入多行,此时参数就需要为二维列表。

具体代码如下所示:

import csvwith open('data1.csv', 'w', newline='') as file:writer = csv.writer(file)writer.writerow(['id', 'name', 'age'])writer.writerows([['10001', 'Mike', 20],['10002', 'Bob', 22], ['10003', 'Jordan', 21]])

运行上面的代码你会发现,运行结果与上面是一样的。

但是一般情况下,爬虫爬取的都是结构化数据,我们一般会用字典来表示。在CSV库中也提供了字典的写入方式,具体代码如下所示:

import csvwith open('data2.csv', 'w', newline='') as file:fieldnames = ['id','name','age']writer = csv.DictWriter(file, 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 csvwith open('data2.csv', 'a') as file:fieldnames = ['id','name','age']writer = csv.DictWriter(file, fieldnames=fieldnames)writer.writerow({'id':'10004', 'name':'Durant','age':25})
  • 读取

我们同样可以通过CSV库来读取CSV文件。例如,将刚才写入的文件内容读取出来,具体代码如下所示:

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

另外,如果接触过pandas的话,可以利用read_csv()方法将数据从CSV读读取出来,例如:

import pandas as pddf = pd.read_csv('data.csv')
print(df)

关系型数据库存储

关系型数据库是基于关系型数据库,而关系模型是通过二维表来保存的,但是它的存储方式就是行列组成的表,每一列是一个字段,每行是一条记录。表可以是某个实体的集合,而实体之间存在关系,这就需要表与表之间的关联关系来体现,如主键外键的关联关系。多个表组成一个数据库,也就是关系型数据库。

关系型数据库有SQLite,MySQL,Oracle,SQL Server,DB2等,下面重点讲解MySQL的用法。

准备工作

在开始之前,请确保已经安装好MySQL数据库并保证它可以正常运行,而且需要安装好PyMySQL。

安装MySQL可以自行百度。

安装PyMySQL的方法如下所示:

pip install pymysql
连接数据库

首先尝试一下连接数据库。当假设MySQL运行在本地,运行端口为3306。

具体代码如下所示:

import pymysqlhost = 'localhost'
user = 'root'
password = '密码'
port = 3306
db = pymysql.connect(host=host, user=user, password=password, port=port)
cursor = db.cursor()
cursor.execute('select version()')
data = cursor.fetchone()
print('database version:',data)
cursor.execute('create database spiders default character set utf8')
db.close()
print('创建成功')

接下来对上面的代码做简单的解释:

首先通过pymysql的connect()方法声明一个MySQL连接对象db,此时需要传入MySQL运行时的host(即IP)。由于MySQL在本地运行,所以传入的是localhost,如果MySQL运行在远程,则需要传入公网的IP地址。

连接成功之后,再调用cursor()获取到MySQL的操作游标,利用游标来执行MySQL语句。这里我们执行了两条SQL语句,直接用execute()方法执行即可。第一句是获取MySQL的版本信息,然后调用fetchone()方法获取第一条数据,也就是版本号。第二句SQL执行创建数据库名为spiders,默认编码是utf-8。

如何查看是否创建成功,可以参考下面的方法。

从上图可以看到databases里面成功创建了一个数据库:spiders。

创建表

一般来说,创建数据库的操作只需要执行一次就可以了。

接下来要操作数据库还需要额外指定一个参数db。

接下来,创建一个数据表students,此时需要执行创建表SQL语句即可。这里指定三个字段,如下表所示:

字段名含义类型
id学号varchar
name姓名varchar
age年龄int

创建students表的具体代码如下所示:

import pymysqlhost = 'localhost'
user = 'root'
password = '密码'
port = 3306
db = pymysql.connect(host=host, user=user, password=password, port=port, 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()
print('创建成功')

运行之后,我们便创建了一个名为students的数据表。

从上图可以看到,我们成功的创建了数据表:students。

同样的,也可以查看该表的字段有哪些,如下图所示:

插入数据

下一步就是向数据库中插入数据了,例如这里爬取了一个学生的信息,学号为2020001,名字为Bob,年龄是18,那么应该怎么样将数据插入数据库呢?

具体代码如下所示:

import pymysqlhost = 'localhost'
user = 'root'
password = '密码'
port = 3306data = {'id':'2020001','name':'Bob','age':18
}
table = 'students'
keys = ','.join(data.keys())    # id,name,age
values = ','.join(['%s']*len(data)) # %s,%s,%sdb = pymysql.connect(host=host, user=user, password=password, port=port, db='spiders')
cursor = db.cursor()sql = 'insert into {table}({keys}) values ({values})'.format(table=table, keys=keys, values=values)
try:if cursor.execute(sql, tuple(data.values())):print('插入成功')db.commit()
except:print('插入失败')db.rollback()
db.close()

从上面的代码以及图片可以看到,成功的将数据插入到了students表当中。

在上面的代码中值得注意的是,需要执行db对象的commit()方法才可以实现数据插入,这个方法才是真正将语句提交到数据库执行的方法,对于数据的插入、更新、删除操作,都需要调用该方法才能生效。

接下来,我们加一层异常处理,如果执行失败,则调用rollback()执行数据回滚,相当于什么都没有发生过。

删除数据

删除操作相对简单,直接用delete语句即可,只需要指定要删除的表名和删除的条件。

在删除之前,我们可以再往数据库里面多插入几条数据,在插入的时候要注意,id是主键,因此不能重复。

如上图所示:我们额外的插入了3条数据。

删除数据的代码如下所示:

import pymysqlhost = 'localhost'
user = 'root'
password = '密码'
port = 3306
table = 'students'
condition = 'age > 20'
db = pymysql.connect(host=host, user=user, password=password, port=port, db='spiders')
cursor = db.cursor()
sql = 'delete from {table} where {condition}'.format(table=table, condition=condition)
try:cursor.execute(sql)db.commit()print('删除成功')
except:db.rollback()print('删除失败')db.close()

看了上面的图片之后相信你就明白了,代码的含义了吧。条件是删除年龄大于20岁的学生,并执行该语句。

查询数据

查询会用到select语句。

具体代码如下所示:

import pymysqlhost = 'localhost'
user = 'root'
password = '密码'
port = 3306
table = 'students'db = pymysql.connect(host=host, user=user, password=password, port=port, db='spiders')
cursor = db.cursor()
sql = 'select * from students'
try:cursor.execute(sql)result = cursor.fetchall()print('results:', result)for row in result:print(row)
except:print('查询失败')
db.close()

运行结果:

results: (('2020001', 'Bob', 18), ('2020003', 'Mike', 16), ('2020004', 'Mark', 19))
('2020001', 'Bob', 18)
('2020003', 'Mike', 16)
('2020004', 'Mark', 19)

调用fetchall()方法可以将全部数据获取下来。

当然,也可以根据条件来获取数据,比如说接下来要获取小于19岁学生的信息。

具体代码如下所示:

import pymysqlhost = 'localhost'
user = 'root'
password = '密码'
port = 3306
table = 'students'db = pymysql.connect(host=host, user=user, password=password, port=port, db='spiders')
cursor = db.cursor()
sql = 'select * from students where age < 19'
try:cursor.execute(sql)result = cursor.fetchall()for row in result:print(row)
except:print('查询失败')
db.close()

运行结果:

('2020001', 'Bob', 18)
('2020003', 'Mike', 16)
更新数据

上面讲完了删除、插入和查询,还剩下一个非常重要的操作那就是修改数据,修改数据需要用到update语句。

具体代码如下所示:

import pymysqlhost = 'localhost'
user = 'root'
password = '698350As?'
port = 3306
table = 'students'db = pymysql.connect(host=host, user=user, password=password, port=port, db='spiders')
cursor = db.cursor()
sql = 'update students set age = %s where name = %s'
try:cursor.execute(sql, (20, 'Bob'))db.commit()print('修改成功')
except:db.rollback()print('修改失败')
db.close() 

通过上面的图片你会发现成功的将Bob的年龄从18改成了20。

但是在抓取数据的过程中,大多数都是需要插入数据,我们更关心的是会不会出现重复的数据,如果出现了,我们希望的是更新数据,而不是再保存一个。那么就需要我们动态的构造SQL语句了。

具体方法如下所示:

import pymysqlhost = 'localhost'
user = 'root'
password = '698350As?'
port = 3306
table = 'students'db = pymysql.connect(host=host, user=user, password=password, port=port, db='spiders')
cursor = db.cursor()
data = {'id':'2020001','name':'Bob','age':28
}
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('更新成功')db.commit()
except:print('更新失败')db.rollback()
db.close()

这里其实是构造了一个插入语句,但是我们在后面加了on duplicate key update。这行代码的意思是如果主键已经存在,就执行更新操作。因此数据就不会被重复插入。

至此,关于关系型数据库MySQL的讲解到这里就结束了,在下一篇文章中就会重点讲解关于非关系型数据库,例如Redis和MongoDB。本次的文章篇幅有点大,就不再写实战内容,实战内容我会在写完非关系型数据库之后一并分享给大家!!

啃书君说

文章的每一个字都是我用心敲出来的,只希望对得起每一位关注我的人。

点个【在看】,让我知道,你们也在为自己的学习拼搏和努力。

路漫漫其修远兮,吾将上下而求索

我是啃书君,一个专注于学习的人,你懂得越多,你不懂得越多。更多精彩内容,我们下期再见!

技术交流群
为了大家更快速的学习知识,掌握技术,随时沟通交流问题,特组建了技术交流群,大家在群里可以分享自己的技术栈,抛出日常问题,群里会有很多大佬及时解答的,这样我们就会结识很多志同道合的人,长按下图可加我微信,备注:Python即可进群。扫码加群????微信公众号长按扫一扫关注公众号????

回复:[学习资料]获取最新Python学习资料

这篇关于Python爬虫:保姆级教你完成数据存储的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/911117

相关文章

Python结合PyWebView库打造跨平台桌面应用

《Python结合PyWebView库打造跨平台桌面应用》随着Web技术的发展,将HTML/CSS/JavaScript与Python结合构建桌面应用成为可能,本文将系统讲解如何使用PyWebView... 目录一、技术原理与优势分析1.1 架构原理1.2 核心优势二、开发环境搭建2.1 安装依赖2.2 验

一文详解如何在Python中从字符串中提取部分内容

《一文详解如何在Python中从字符串中提取部分内容》:本文主要介绍如何在Python中从字符串中提取部分内容的相关资料,包括使用正则表达式、Pyparsing库、AST(抽象语法树)、字符串操作... 目录前言解决方案方法一:使用正则表达式方法二:使用 Pyparsing方法三:使用 AST方法四:使用字

Python列表去重的4种核心方法与实战指南详解

《Python列表去重的4种核心方法与实战指南详解》在Python开发中,处理列表数据时经常需要去除重复元素,本文将详细介绍4种最实用的列表去重方法,有需要的小伙伴可以根据自己的需要进行选择... 目录方法1:集合(set)去重法(最快速)方法2:顺序遍历法(保持顺序)方法3:副本删除法(原地修改)方法4:

Python运行中频繁出现Restart提示的解决办法

《Python运行中频繁出现Restart提示的解决办法》在编程的世界里,遇到各种奇怪的问题是家常便饭,但是,当你的Python程序在运行过程中频繁出现“Restart”提示时,这可能不仅仅是令人头疼... 目录问题描述代码示例无限循环递归调用内存泄漏解决方案1. 检查代码逻辑无限循环递归调用内存泄漏2.

Python中判断对象是否为空的方法

《Python中判断对象是否为空的方法》在Python开发中,判断对象是否为“空”是高频操作,但看似简单的需求却暗藏玄机,从None到空容器,从零值到自定义对象的“假值”状态,不同场景下的“空”需要精... 目录一、python中的“空”值体系二、精准判定方法对比三、常见误区解析四、进阶处理技巧五、性能优化

使用Python构建一个Hexo博客发布工具

《使用Python构建一个Hexo博客发布工具》虽然Hexo的命令行工具非常强大,但对于日常的博客撰写和发布过程,我总觉得缺少一个直观的图形界面来简化操作,下面我们就来看看如何使用Python构建一个... 目录引言Hexo博客系统简介设计需求技术选择代码实现主框架界面设计核心功能实现1. 发布文章2. 加

SpringBoot集成Milvus实现数据增删改查功能

《SpringBoot集成Milvus实现数据增删改查功能》milvus支持的语言比较多,支持python,Java,Go,node等开发语言,本文主要介绍如何使用Java语言,采用springboo... 目录1、Milvus基本概念2、添加maven依赖3、配置yml文件4、创建MilvusClient

python logging模块详解及其日志定时清理方式

《pythonlogging模块详解及其日志定时清理方式》:本文主要介绍pythonlogging模块详解及其日志定时清理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录python logging模块及日志定时清理1.创建logger对象2.logging.basicCo

Python如何自动生成环境依赖包requirements

《Python如何自动生成环境依赖包requirements》:本文主要介绍Python如何自动生成环境依赖包requirements问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录生成当前 python 环境 安装的所有依赖包1、命令2、常见问题只生成当前 项目 的所有依赖包1、

如何将Python彻底卸载的三种方法

《如何将Python彻底卸载的三种方法》通常我们在一些软件的使用上有碰壁,第一反应就是卸载重装,所以有小伙伴就问我Python怎么卸载才能彻底卸载干净,今天这篇文章,小编就来教大家如何彻底卸载Pyth... 目录软件卸载①方法:②方法:③方法:清理相关文件夹软件卸载①方法:首先,在安装python时,下