爬虫工作量由小到大的思维转变---<第十一章 Scrapy之sqlalchemy模版和改造(番外)>

前言:

正常的pymysql当然问题不大,但是我个人还是建议:sqlalchemy!  因为他更能让我们把精力放在表单设计上,而不执着于代码本身了.

(-----版权所有。未经作者书面同意,不得转载或用于任何商业用途!----)

正文:

先提供一个基础模版:

表图:
创建表的sql:
CREATE TABLE match_info (id INT PRIMARY KEY,home_team VARCHAR(30), full_score VARCHAR(8),  half_score VARCHAR(8), away_team VARCHAR(30),  match_time DATETIME,   
#比赛时间如 '2023-12-15 14:30:00'包括年、月、日、时、分、秒league VARCHAR(10),   corners VARCHAR(10),   zhuangtai INT,    #状态,1(完成收录) 0(未开始) -1(数据待补)created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP#修改时间
);
代码:
from datetime import datetime
from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, DateTime
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import sessionmakerclass MatchInfoCRUD:# 初始化类并建立数据库连接def __init__(self):self.db_uri = 'mysql+pymysql://user:password@localhost/mydatabase'  #填入自己的信息:user:password@localhost/mydatabaseself.engine = create_engine(self.db_uri)  # 使用数据库URI创建引擎self.metadata = MetaData()  # 元数据对象用于收集表对象# 定义match_info表结构self.match_info = Table('match_info', self.metadata,Column('id', Integer, primary_key=True),  # 主键不自增Column('zhuangtai', Integer),  # 整型状态列Column('league', String(10)),  # 长度为10的字符串类型的联赛列Column('match_time', DateTime),  # 日期时间类型的比赛时间列Column('home_team', String(30)),  # 长度为30的字符串类型的主队列Column('full_score', String(8)),  # 长度为8的字符串类型的全场比分列Column('half_score', String(8)),  # 长度为8的字符串类型的半场比分列Column('away_team', String(30)),  # 长度为30的字符串类型的客队列Column('corners', String(10)),  # 长度为10的字符串类型的角球数列)self.metadata.create_all(self.engine)  # 在数据库中创建表self.Session = sessionmaker(bind=self.engine)  # 创建与数据库会话的会话工厂# 创建新的比赛记录def create_match(self, match_data):session = self.Session()  # 开启新的会话try:# 创建插入对象并插入数据insert_object = self.match_info.insert().values(match_data)session.execute(insert_object)  # 执行插入操作session.commit()  # 提交事务print("数据插入成功。")except SQLAlchemyError as e:  # 捕获并处理SQLAlchemy异常print(f"插入数据时出现问题: {e}")finally:session.close()  # 关闭会话# 读取比赛记录def read_match(self, match_id):session = self.Session()  # 开启新的会话try:query = session.query(self.match_info).filter_by(id=match_id)  # 创建查询对象match = query.first()  # 获取查询结果的第一条记录if match:return match  # 返回那条记录else:return None  # 如果没找到记录,返回Noneexcept SQLAlchemyError as e:print(f"读取数据时出现问题: {e}")finally:session.close()  # 关闭会话# 更新比赛记录def update_match(self, match_id, update_data):session = self.Session()  # 开启新的会话try:query = session.query(self.match_info).filter_by(id=match_id)  # 创建查询对象query.update(update_data)  # 执行更新操作session.commit()  # 提交事务print("数据更新成功。")except SQLAlchemyError as e:print(f"更新数据时出现问题: {e}")finally:session.close()  # 关闭会话# 删除比赛记录def delete_match(self, match_id):session = self.Session()  # 开启新的会话try:query = session.query(self.match_info).filter_by(id=match_id)  # 创建查询对象match = query.first()  # 获取查询结果的第一条记录if match:query.delete()  # 如果找到记录则执行删除操作session.commit()  # 提交事务print("数据删除成功。")else:print("未找到相应比赛。")except SQLAlchemyError as e:print(f"删除数据时出现问题: {e}")finally:session.close()  # 关闭会话# 创建MatchInfoCRUD的一个实例
crud = MatchInfoCRUD()# 创建并插入新的比赛记录
match_data = {'id': 1,'zhuangtai': 1,'league': '联赛数据','match_time': datetime(2023, 12, 15, 14, 30),'home_team': 'Team A','full_score': '2-1','half_score': '1-0','away_team': 'Team B','corners': '5-4',
}
crud.create_match(match_data)# 读取id为1的比赛记录
match_record = crud.read_match(1)
if match_record:print(f"读取到比赛记录: {match_record}")
else:print("没有找到对应的比赛记录。")
说明:

这里是4个基本属性,增删改查!!!  直接调用就好了...

潜在改进点,往下看


优化方案:

  • 1. 异常处理:
    •    - 可以更精细地管理异常。目前代码中出现任何错误都执行同样的处理,实际应用中可能需要对不同的异常类型进行不同的处理。
  • 2. 封装会话管理:
    •    - 代码中反复出现创建和关闭会话的模式,这可以通过上下文管理器或装饰器来优化,减少代码重复并自动管理资源。
  • 3. 返回信息:
    •    - `create_match` 方法和其他修改操作只是简单地打印了结果,现实场景中可能需要将操作结果(如新创建的对象)返回给调用者。
  • 4. 优化查询:
    •    - 在 `delete_match` 方法中,无需先查询再删除。可以直接使用 `.delete()`,如果有必要确保记录存在,可以在删除后检查 `result.rowcount`。
  • 5. 输入检验:
    •    - 创建和更新数据前进行输入有效性检查,防止无效或恶意数据被写入数据库。
  • 6. 代码组织:
    •    - 根据 Python 的约定,长的导入语句可以分行。
    •    - ORM 映射通常使用更高级的 `declarative_base` 系统进行,这有助于简化模型定义。
  • 7. SQLAlchemy ORM 的使用:
    •    - 目前代码使用了 `Table` 对象和底层的 `insert` 方法。可以让SQLAlchemy ORM 的能力进行映射,并且允许使用会话直接操作对象模型!
案例:
from datetime import datetime
from sqlalchemy import create_engine, Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import sessionmaker, scoped_session# 定义基类
Base = declarative_base()# 定义 MatchInfo ORM 模型
class MatchInfo(Base):__tablename__ = 'match_info'id = Column(Integer, primary_key=True)home_team = Column(String(30))full_score = Column(String(8))half_score = Column(String(8))away_team = Column(String(30))match_time = Column(DateTime)league = Column(String(10))corners = Column(String(10))zhuangtai = Column(Integer)created_time = Column(DateTime, default=datetime.now)updated_time = Column(DateTime, default=datetime.now, onupdate=datetime.now)# MatchInfoCRUD 类使用 ORM 模型和会话管理
class MatchInfoCRUD:def __init__(self):self.db_uri = 'mysql+pymysql://user:password@localhost/mydatabase'self.engine = create_engine(self.db_uri)Base.metadata.create_all(self.engine)self.Session = scoped_session(sessionmaker(bind=self.engine))def create_match(self, match_data):"""创建新的比赛记录"""try:match = MatchInfo(**match_data)self.Session.add(match)self.Session.commit()print("数据插入成功。")except SQLAlchemyError as e:self.Session.rollback()print(f"插入数据时出现问题: {e}")finally:self.Session.remove()def read_match(self, match_id):"""读取比赛记录"""try:match = self.Session.query(MatchInfo).get(match_id)return matchexcept SQLAlchemyError as e:print(f"读取数据时出现问题: {e}")finally:self.Session.remove()def update_match(self, match_id, update_data):"""更新比赛记录"""try:match = self.Session.query(MatchInfo).get(match_id)for key, value in update_data.items():setattr(match, key, value)self.Session.commit()print("数据更新成功。")except SQLAlchemyError as e:self.Session.rollback()print(f"更新数据时出现问题: {e}")finally:self.Session.remove()def delete_match(self, match_id):"""删除比赛记录"""try:match = self.Session.query(MatchInfo).get(match_id)if match:self.Session.delete(match)self.Session.commit()print("数据删除成功。")else:print("未找到相应比赛记录。")except SQLAlchemyError as e:self.Session.rollback()print(f"删除数据时出现问题: {e}")finally:self.Session.remove()
  1. 使用 declarative_base 来创建 ORM 基础类并定义表结构;
  2. 采用了 scoped_session 以自动管理会话的生命周期,避免手动关闭会话;
  3. 更新 delete_match 方法,现在它会首先尝试获取记录,如果找到则删除,这样还是需要先查询再删除,但这确保了操作的准确性;
  4. 删掉了直接操作 Table 对象,改为使用 ORM 映射的类和实例来管理数据。

接下来对提供的MatchInfoCRUD类进行几个关键方面的优化,包括封装会话管理、优化查询处理,以及使用 SQLAlchemy ORM 更优雅地定义和交互数据库模型。这里需要使用 SQLAlchemy 的声明式基类declarative_base来简化模型定义,以及使用上下文管理器来自动化会话的生命周期管理。

from datetime import datetime
from sqlalchemy import create_engine, Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import sessionmaker, scoped_session# 使用declarative_base创建ORM模型的基类
Base = declarative_base()# 定义MatchInfo ORM模型
class MatchInfo(Base):__tablename__ = 'match_info'id = Column(Integer, primary_key=True)zhuangtai = Column(Integer)league = Column(String(10))match_time = Column(DateTime)home_team = Column(String(30))full_score = Column(String(8))half_score = Column(String(8))away_team = Column(String(30))corners = Column(String(10))created_time = Column(DateTime)updated_time = Column(DateTime)# 自定义上下文管理器,管理数据库会话的生命周期
class DBSessionManager:def __init__(self, db_uri):self.engine = create_engine(db_uri)self.Session = scoped_session(sessionmaker(bind=self.engine, autocommit=False, autoflush=False))def __enter__(self):self.session = self.Session()return selfdef __exit__(self, exc_type, exc_val, exc_tb):self.session.close()class MatchInfoCRUD:# 初始化类并建立数据库连接def __init__(self, db_uri):self.db_manager = DBSessionManager(db_uri)Base.metadata.create_all(self.db_manager.engine)# 创建新的比赛记录def create_match(self, match_data):with self.db_manager as db:try:match = MatchInfo(**match_data)db.session.add(match)db.session.commit()print("数据插入成功。")except SQLAlchemyError as e:db.session.rollback()print(f"插入数据时出现了问题: {e}")# 查询等其他方法同理可以通过db_manager动态管理会话# 使用新的CRUD接口进行操作
db_uri = 'mysql+pymysql://user:password@localhost/mydatabase'  # 请填入数据库URI
crud = MatchInfoCRUD(db_uri)match_data = {'id': 1,'zhuangtai': 1,'league': '联赛数据','match_time': datetime(2023, 12, 15, 14, 30),'home_team': 'Team A','full_score': '2-1','half_score': '1-0','away_team': 'Team B','corners': '5-4','created_time': datetime.now(),'updated_time': datetime.now(),
}crud.create_match(match_data)# 后续其他增删改查操作可以类似地实现
  1. 封装会话管理:通过DBSessionManager上下文管理器类来管理会话的开启和关闭,使得对于每个数据库会话,无需重复编写打开和关闭的代码。
  2. 优化查询:利用ORM的能力来直接添加、查询和更新数据,没有使用底层的表和查询语句。
  3. SQLAlchemy ORM 的使用:使用了declarative_base来定义SQLAlchemy ORM模型,从而提供ORM的完全功能,并写了一个ORM类MatchInfo来映射match_info表。

总结:

    一个强大的 Python SQL 工具包和 ORM(对象关系映射器),来改善数据库操作的效率和代码的整洁性。首先定义了一个 ORM 模型来映射数据库表,然后构建了一个管理数据库会话生命周期的上下文管理器。在实际的 CRUD(创建、读取、更新、删除)操作中,直接对 ORM 对象进行操作,而不是执行原始 SQL 语句。这样使得代码更加简洁、容易理解和维护,也更加面向对象。通过这种方式,我们将耗时的数据库管理工作交给 SQLAlchemy,自己就能专注于业务逻辑和数据的设计上了。简而言之,就是让代码更加简洁、高效,同时也降低了出错的几率。

(-----版权所有。未经作者书面同意,不得转载或用于任何商业用途!----)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/218897.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023-12-14 二叉树的最大深度和二叉树的最小深度以及完全二叉树的节点个数

二叉树的最大深度和二叉树的最小深度以及完全二叉树的节点个数 104. 二叉树的最大深度 思想:可以使用迭代法或者递归!使用递归更好,帮助理解递归思路!明确递归三部曲–①确定参数以及返回参数 ②递归结束条件 ③单层逻辑是怎么样…

C#中的封装、继承和多态

1.引言 在面向对象的编程中,封装、继承和多态是三个重要的概念。它们是C#语言中的基本特性,用于设计和实现具有高内聚和低耦合的代码。本文将详细介绍C#中的封装、继承和多态的相关知识。 目录 1.引言2. 封装2.1 类2.2 访问修饰符 3. 继承4. 多态4.1 虚方…

中文星期几十二时辰

输入年月日输出中文星期败,输入时间字符串,输出十二时辰。 (笔记模板由python脚本于2023年12月16日 23:39:04创建,本篇笔记适合熟悉python字符串类型str,并可以熟练应用的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&…

毅速:3D打印随形水路 提高良品率和生产效率的新利器

随着科技的不断发展,3D打印技术已经成为模具制造领域的一种重要技术。其中,模具随形水路的设计和制造是提高注塑产品良品率和生产效率的关键环节。 模具随形水路是一种根据产品形状设计的水路,可以更靠近产品,并在模具内热点集中区…

数据库系列之简要对比下GaussDB和OpenGauss数据库

GaussDB作为一款企业级的数据库产品,和开源数据库OpenGauss之间又是什么样的关系,刚开始接触的时候是一头雾水,因此本文简要对比下二者的区别,以加深了解。 1、GaussDB和OpenGauss数据库简要对比 GaussDB是华为基于PostgreSQL数据…

vue3 element-plus 日期选择器 el-date-picker 汉化

vue3 项目中,element-plus 的日期选择器 el-date-picker 默认是英文版的,如下: 页面引入: //引入汉化语言包 import locale from "element-plus/lib/locale/lang/zh-cn" import { ElDatePicker, ElButton, ElConfigP…

WPF-UI HandyControl 控件简单实战

文章目录 前言UserControl简单使用新建项目直接新建项目初始化UserControlGeometry:矢量图形额外Icon导入最优解决方案 按钮Button切换按钮ToggleButton默认按钮图片可切换按钮加载按钮切换按钮 单选按钮和复选按钮没有太大特点,就不展开写了总结 DataGrid数据表格G…

浅谈MapReduce

MapReduce是一个抽象的分布式计算模型,主要对键值对进行运算处理。用户需要提供两个自定义函数: map:用于接受输入,并生成中间键值对。reduce:接受map输出的中间键值对集合,进行sorting后进行合并和数据规…

〖大前端 - 基础入门三大核心之JS篇(55)〗- 内置对象

说明:该文属于 大前端全栈架构白宝书专栏,目前阶段免费,如需要项目实战或者是体系化资源,文末名片加V!作者:哈哥撩编程,十余年工作经验, 从事过全栈研发、产品经理等工作,目前在公司…

什么是前端国际化(internationalization)和本地化(localization)?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

Flink中的时间和窗口

在批处理统计中,我们可以等待一批数据都到齐后,统一处理。但是在实时处理统计中,我们是来一条就得处理一条,那么我们怎么统计最近一段时间内的数据呢?引入“窗口”。 所谓的“窗口”,一般就是划定的一段时…

数据通信网络基础

数据通信网络基础(1) 一.前言 • 在人类社会的起源和发展过程中,通信就一直伴随着我们。从20世纪七、八十年代开始, 人类社会已进入到信息时代,对于生活在信息时代的我们,通信的必要性更是不言而喻 的。…

System作为系统进程陔如何关闭?

一、简介 system进程是不可以关闭的,它是用来运行一些系统命令的,比如reboot、shutdown等,以及用来运行一些后台程序,比如ntfs-3g、v4l2loopback等。system进程也被用于运行一些内核模块,比如nvidia、atd等。system进程…

pl_vio线特征

pl_vio线特征 0.引言1.LineFeatureTracker核心逻辑解读2.estimator_node中线段的处理2.1.订阅信息解压2.2.线特征管理 3.线段三角化3.1.普吕克线坐标3.2.正交表示 4.线段残差对位姿的导数4.1.直线的观测模型和误差4.2.误差雅克比推导 0.引言 PL-VIO,本文关注线段。…

web服务器之——www服务器的基本配置

目录 一、www简介 1、什么是www 2、www所用的协议 3、WEB服务器 4、主要数据 5、浏览器 二、 网址及HTTP简介 1、HTTP协议请求的工作流程 三、www服务器的类型(静态网站(HTML), 动态网站(jsp python,php,perl)) 1、 仅提供…

VM虚拟机打不开原来保存的虚拟机文件夹ubuntu

VMWare虚拟机打不开原来保存的虚拟机文件夹ubuntu 换了电脑把之前的虚拟机克隆的文件夹直接拿来用 报这个错: 指定的文件不是虚拟磁盘 打不开磁盘“D:\ubuntu_iso\ubuntu_location\Ubuntu 64 位-s002.vmdk”或它所依赖的某个快照磁盘。 模块“Disk”启动失败。 未…

docker部署go gin框架 Windows环境

目录 文章目的是什么 环境介绍 Windows 环境下 docker 部署 go gin 详细步骤 运行容器时因为挂载文件可能会出现的问题 直接部署gin(跳过运行容器时因为挂载文件可能会出现的问题) 文章目的是什么 假设我们学习了 go 语言,在 Windows(本…

Redis List类型

列表类型是用来存储多个有序的字符串,如图所示,a、b、c、d、e 五个元素从左到右组成了一个有序的列表,列表中的每个字符串称为元素 (element),一个列表最多可以存储2的32次方 -1个元素。在 Redis 中,可以对列表两端插入…

智能优化算法应用:基于松鼠算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用:基于松鼠算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于松鼠算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.松鼠算法4.实验参数设定5.算法结果6.参考文献7.MA…

虚幻学习笔记14—重叠和碰撞事件

一、前言 在开发应用当中两个物体的重叠和碰撞事件会经常用到,在虚幻中哲两个有很大的区别,在官方文档碰撞概述其实已经讲了怎样发生碰撞和重叠,但是还是遗漏不少注意事项合细节,主要文档写的太粗糙了,这也让我在使用的…