爬虫系列:使用 MySQL 存储数据

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 爬虫系列:使用 MySQL 存储数据

上一篇文章我们讲解了爬虫如何存储 CSV 文件,这篇文章,我们讲解如何将采集到的数据保存到 MySQL 数据库中。

MySQL 是目前最受欢迎的开源关系型数据库管理系统。一个开源项目具有如此之竞争力实在是令人意外,它的流行程度正在不断地接近两外两个闭源的商业数据库系统:微软的 SQL Server 和甲骨文的 Oracle 数据库(MySQL 在2010年被甲骨文收购)。

它的流行程度名符其实。对于大多数应用来说,MySQL 都是不二选择。他是一种非常灵活、稳定、功能齐全的 DBMS,许多顶级的网站都在使用它:Youtube、Twitter 和 Facebook 等。

因为它受众广泛,免费,开箱即用,所以它是网络数据采集项目中常用的数据库,这篇文章我们介绍如何通过 MySQL 存储采集到的数据。

安装 MySQL
如果你第一次接触 MySQL,可能会觉得有点麻烦。其实,安装方法和安装其他软件一样简单。归根结底,MySQL 就是由一系列数据文件构成的,存储在你远端服务器或者本地电脑上,里面包含了数据库存储的所有信息。

Windows 安装 MySQL、Ubuntu 安装 MySQL、MAC 安装 MySQL 具体步骤在此:全平台安装 MySQL

在此不做过多说明,按照视频操作就可以了。

基本命令
MySQL 服务器启动之后,有很多种方法可以与数据库服务器交互。因为有很多工具是图形界面,所以你可以不用 MySQL 的命令行(或是很少用命令行)也能管理数据库。像 phpMyAdmin 和 MySQL Workbench 这类工具可以很容易地实现数据库查看、排序和新建等工作。但是,掌握命令行操作数据库还是很重要的。

除了用户自定义变量名,MySQL 是不区分大小写的。例如,SELECT 和 select 是一样的,不过习惯上写 MySQL 语句的时候所有的 MySQL 关键词都用大写。大多数开发者还喜欢用小写字母表示数据库和数据表的名称。

首先登入 MySQL 数据库的时候,里面是没有数据库存放数据的。我们需要创建一个数据库:

CREATE DATABASE scraping_article DEFAULT CHARACTER SET UTF8 COLLATE UTF8_GENERAL_CI;

因为每个 MySQL 实例可以有多个数据库,所以使用某个数据库之前需要指定数据库的名称:

USE scraping_article

从现在开始(直到关闭 MySQL 链接或切换到另一个数据库之前),所有的命令都运行在这个新的“scraping_article”数据库里面。

所有操作都看起来非常简单。那么在数据库里面新建表的操作方法也应该类似吧?我们在库里面新建一张表来存储采集的网页文章数据:

CREATE TABLE articles;

结果显示错误:

ERROR 1113 (42000): A table must have at least 1 column

和数据库不同,MySQL 数据表必须有一列,否则不能创建。为了在 MySQL 里定义字段(数据列),我们还必须在 CREATE TABLE 语句后面,把字段定义放进一个带括号的、内部由逗号分隔的列表中:

create table articles
(

id                   int auto_increment
    primary key,
title                varchar(64)                        null,
body                 text                               null,
summary              varchar(256)                       null,
body_html            text                               null,
create_time          datetime default CURRENT_TIMESTAMP null,
time_updated         datetime                           null,
link_text            varchar(128)                       null

);
每个字段定义由三部分组成:

名称(id、title、body 等)

数据类型(INT、VARCHAR、TEXT)

其他可选属性(NOT NULL AUTO_INCREMENT)

在字段定义列表的最后,还要定义一个“主键”(key)。MySQL 用这个主键来组织表的内容,便于后面快速查询。在以后的文章中,我将介绍如果通过这些主键以提高数据库的查询速度,但是现在,我们使用表的 id 列作为主键就可以。

语句执行之后,我们可以使用 DESCRIBE 查看数据表的结构:

Field Type Null Key Default Extra
id int(11) NO PRI NULL auto_increment
title varchar(64) YES NULL
body text YES NULL
summary varchar(256) YES NULL
body_html text YES NULL
create_time datetime YES CURRENT_TIMESTAMP
time_updated datetime YES NULL
link_text varchar(128) YES NULL

8 rows in set (0.03 sec)
现在这张表是一张空表,我们插入一下数据看看,如下所示:

INSERT INTO articles(title,body,summary,body_html,link_text) VALUES ("Test page title","Test page body.","Test page summary.","

Test page body.

","test-page");
这里需要我们注意,虽然 articles 表有8个字段(id,title,body,summary,body_html,create_time,time_update,link_text),但实际上我们这里只插入5个字段(title,body,summary,body_html,link_text)的数据即可。因为 id 字段是自动递增的(每次插入数据时 MySQL 默认增加1),通常不用处理。另外 create_time 字段的类型是 current_timestamp,默认插入的是时间戳。

当然我们也可以自定义字段内容插入数据:

INSERT INTO articles(id,title,body,summary,body_html,create_time,link_text) VALUES (4,"Test page title","Test page body.","Test page summary.","

Test page body.

","2021-11-20 15:51:45","test-page");
只要你定义的整数在数据表的 id 字段里没有,他就可以插入到数据表。但是,这么做非常不好;除非万不得已(比如程序中漏了一行数据),否则让 MySQL 自己处理 id 和 timestamp 字段。

现在表里面有一些数据了,我们可以通过很多方法查询这些数据。下面是几个 SELECT 语句的示例:

SELECT * FROM articles WHERE id=1;
这条语句告诉 MySQL,“从 articles 表中把所有 id 等于 2 的数据全部挑选出来”。这个星号(*)是通配符,表示所有字段,这行语句会把满足条件(where id=1)的所有字段内容都显示出来。如果 id 这里没有任何一行等于1,就会返回一个空集。例如,下面这个不区分大小写的查询,会返回 title 字段里包含 “test”的所有行(%符号表示 MySQL 字符串通配符)的所有字段:

SELECT * FROM articles WHERE title LIKE "%test%";
但是如果你有很多字段,而你只想返回部分字段怎么办?你可以不要用星号,而是使用下面的方式:

SELECT title, body FROM articles WHERE body LIKE "%test%";
这样就只会返回 body 内容包含 “test” 所有行的 title 和 body 两个字段了。

DELETE 语句语法与 SELECT 语句类似:

DELETE FROM articles WHERE id=1;
由于数据库的数据删除不能恢复,所以在执行 DELETE 语句之前,建议使用 SELECT 确认一下需要删除的数据(上面的删除语句可以使用 SELECT FROM articles WHERE id=1; 查看),然后把 SELECT 换成 DELETE 就可以了,这会是一个好习惯。很多程序员都有过一些 DELETE 误操作的伤心往事,还有一些恐怖的故事就是有人慌乱中忘了在语句中放 WHERE,结果把所有的客户数据都删除了。别让这事情发生在你身上!

还有一个需要介绍的是 UPDATE 语句:

UPDATE articles SET title="A new title", body="Some new body." WHERE id=4;
以上只是使用了最基本的 MySQL 语句,做一些简单的数据查询、创建和更新等工作。

与 Python 整合
Python 没有内置的 MySQL 支持工具。不过,有很多开源的可以用来与 MySQL 做交互,Python 2.x 和 Python 3.x 版本都支持。最有名的一个就是 PyMySQL。

我们可以使用 pip 安装,执行如下命令:

python3 -m pip install PyMySQL

安装完成我们就可以使用 PyMySQL 包了。如果你的 MySQL 服务器处于运行状态,应该就可以成功地执行下面命令:

import pymysql
import os
from dotenv import load_dotenv

class DataSaveToMySQL(object):

def __init__(self):
    # loading env config file
    dotenv_path = os.path.join(os.getcwd(), '.env')
    if os.path.exists(dotenv_path):
        load_dotenv(dotenv_path)

conn = pymysql.connect(host=os.environ.get('MYSQL_HOST'), port=os.environ.get('MYSQL_PORT'),
                       user=os.environ.get('MYSQL_USER'), password=os.environ.get('MYSQL_PASSWORD'),
                       db=os.environ.get('MYSQL_DATABASES'))
cur = conn.cursor()
cur.execute("SELECT * FROM articles WHERE id=4;")
print(cur.fetchone())
cur.close()
conn.close()

这段程序有两个对象:连接对象(conn)和游标对象(cur)。

连接/游标模式是数据库编程中常用的模式,在刚刚接触数据库的时候,有的时候很难区分这两种模式的不同。连接模式除了连接数据库之外,还要发送数据库信息,处理回滚操作(当一个查询或一组查询被中断时,数据库就需要回到初始状态,一般使用事务实现状态回滚),创建新的游标等等。

而一个连接可以有很多个游标,一个游标追踪一种状态(state)信息,比如追踪数据库的使用状态。如果有多个数据库,且需要向所有数据库写内容,就需要多个游标来处理。游标还可以包含最后一次查询执行结果。通过调用游标函数,比如cur.fetchone(),可以获取查询结果。

用完连接和游标之后千万记得关闭它们。如果不关闭就会导致连接泄露(connection leak),造成一种关闭连接现象,即连接已经不再使用,但数据库却不能关闭,因为数据库不确定你还要不要继续使用它。这种现象一直会耗费数据库资源,所以用完数据库之后记得关闭连接!

刚开始的时候,你想做的事情就是把采集的数据保存到数据库。我们继续采集博客文章的例子来演示如何实现数据存储。

import pymysql
import os
from dotenv import load_dotenv

from config import logger_config
from utils import connection_util

class DataSaveToMySQL(object):

def __init__(self):
    # loading env config file
    dotenv_path = os.path.join(os.getcwd(), '.env')
    if os.path.exists(dotenv_path):
        load_dotenv(dotenv_path)
    # MySQL config
    self._host = os.environ.get('MYSQL_HOST')
    self._port = int(os.environ.get('MYSQL_PORT'))
    self._user = os.environ.get('MYSQL_USER')
    self._password = os.environ.get('MYSQL_PASSWORD')
    self._db = os.environ.get('MYSQL_DATABASES')

    self._target_url = 'https://www.scrapingbee.com/blog/'
    self._baseUrl = 'https://www.scrapingbee.com'
    self._init_connection = connection_util.ProcessConnection()
    logging_name = 'store_mysql'
    init_logging = logger_config.LoggingConfig()
    self._logging = init_logging.init_logging(logging_name)

def scrape_data(self):
    get_content = self._init_connection.init_connection(self._target_url)
    if get_content:
        parent = get_content.findAll("section", {"class": "section-sm"})[0]
        get_row = parent.findAll("div", {"class": "col-lg-12 mb-5 mb-lg-0"})[0]
        get_child_item = get_row.findAll("div", {"class": "col-md-4 mb-4"})
        for item in get_child_item:
            # 获取标题文字
            get_title = item.find("a", {"class": "h5 d-block mb-3 post-title"}).get_text()
            # 获取发布时间
            get_release_date = item.find("div", {"class": "mb-3 mt-2"}).findAll("span")[1].get_text()
            # 获取文章描述
            get_description = item.find("p", {"class": "card-text post-description"}).get_text()
            self.article_save_mysql(title=get_title, description=get_description, release_date=get_release_date)
    else:
        self._logging.warning('未获取到文章任何内容,请检查!')


def article_save_mysql(self, title, description, release_date):
    connection = pymysql.connect(host=self._host, port=self._port, user=self._user, password=self._password,
                                 db=self._db, charset='utf-8')
    with connection.cursor() as cursor:
        # Create a new record
        sql = "INSERT INTO articles (title,summary,create_time) VALUES (%s,%s,%s);"
        cursor.execute(sql, (title, description, release_date))

    # connection is not autocommit by default. So you must commit to save
    # your changes.
    connection.commit()

这里有几点需要注意:首先,charset='utf-8' 要增加到连接字符串里。这是让 conn 把所有发送数据库的信息都当成 utf-8 编码格式(当然,前提是数据库默认编码设置成 UTF-8)。

然后需要注意的是 article_save_mysql 函数。它有3个参数:title、description和release_date,并把这两个参数加入到一个 INSERT 语句中并用游标执行,然后使用游标进行确认。这是一个让游标与连接分离的好例子;当游标里存储了一些数据库与数据库上下文(context)的信息时,需要通过连接确认将信息传进数据库,再将信息插入数据库。

上面代码没有使用 try...finally 语句来关闭数据库,而是使用的 with() 来关闭数据库连接,上一期中我们也是使用的 with() 来关闭 CSV 文件。

虽然 PyMySQL 规模并不大,但是里面有一些非常实用的函数,本篇文章中并没有演示,具体可以参考 Python 的 DBAPI 标准文档。

以上是关于将采集的内容保存到 MySQL 的内容,本实例的所有代码托管于 github。

github: https://github.com/sycct/Scrape_1_1.git

如果有任何问题,欢迎在 github issue。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
11天前
|
canal 消息中间件 关系型数据库
Canal作为一款高效、可靠的数据同步工具,凭借其基于MySQL binlog的增量同步机制,在数据同步领域展现了强大的应用价值
【9月更文挑战第1天】Canal作为一款高效、可靠的数据同步工具,凭借其基于MySQL binlog的增量同步机制,在数据同步领域展现了强大的应用价值
89 4
|
14天前
|
SQL 存储 缓存
MySQL是如何保证数据不丢失的?
文章详细阐述了InnoDB存储引擎中Buffer Pool与DML操作的关系。在执行插入、更新或删除操作时,InnoDB为了减少磁盘I/O,会在Buffer Pool中缓存数据页进行操作,随后将更新后的“脏页”刷新至磁盘。为防止服务宕机导致数据丢失,InnoDB采用了日志先行(WAL)机制,通过将DML操作记录为Redo Log并异步刷新到磁盘,结合双写机制和合理的日志刷新策略,确保数据的持久性和一致性。尽管如此,仍需合理配置参数以平衡性能与数据安全性。
MySQL是如何保证数据不丢失的?
|
11天前
|
数据采集 JavaScript 前端开发
构建简易Python爬虫:抓取网页数据入门指南
【8月更文挑战第31天】在数字信息的时代,数据抓取成为获取网络资源的重要手段。本文将引导你通过Python编写一个简单的网页爬虫,从零基础到实现数据抓取的全过程。我们将一起探索如何利用Python的requests库进行网络请求,使用BeautifulSoup库解析HTML文档,并最终提取出有价值的数据。无论你是编程新手还是有一定基础的开发者,这篇文章都将为你打开数据抓取的大门。
|
11天前
|
存储 关系型数据库 MySQL
|
11天前
|
数据采集 存储 数据库
Python中实现简单爬虫与数据解析
【8月更文挑战第31天】在数字化时代的浪潮中,数据成为了新的石油。本文将带领读者通过Python编程语言,从零开始构建一个简单的网络爬虫,并展示如何对爬取的数据进行解析和处理。我们将一起探索请求网站、解析HTML以及存储数据的基础知识,让每个人都能成为自己数据故事的讲述者。
|
11天前
|
数据采集 JavaScript 前端开发
构建你的第一个Python爬虫:抓取网页数据入门指南
【8月更文挑战第31天】在数字时代,数据是新的石油。本文将引导初学者通过简单的步骤,使用Python编程语言创建一个基础的网络爬虫程序。我们将探索如何从网络上提取信息,并理解背后的原理。无论你是编程新手还是想要扩展你的技术工具箱,这篇文章都将为你提供一条清晰的道路,让你学会编写能够自动获取网络数据的脚本。准备好开始你的网络数据抓取之旅了吗?让我们现在就开始吧!
|
11天前
|
数据采集 JavaScript 前端开发
Python 爬虫实战:抓取和解析网页数据
【8月更文挑战第31天】本文将引导你通过Python编写一个简单的网络爬虫,从网页中抓取并解析数据。我们将使用requests库获取网页内容,然后利用BeautifulSoup进行解析。通过本教程,你不仅能够学习到如何自动化地从网站收集信息,还能理解数据处理的基本概念。无论你是编程新手还是希望扩展你的技术工具箱,这篇文章都将为你提供有价值的见解。
|
11天前
|
SQL 关系型数据库 MySQL
SQL Server、MySQL、PostgreSQL:主流数据库SQL语法异同比较——深入探讨数据类型、分页查询、表创建与数据插入、函数和索引等关键语法差异,为跨数据库开发提供实用指导
【8月更文挑战第31天】SQL Server、MySQL和PostgreSQL是当今最流行的关系型数据库管理系统,均使用SQL作为查询语言,但在语法和功能实现上存在差异。本文将比较它们在数据类型、分页查询、创建和插入数据以及函数和索引等方面的异同,帮助开发者更好地理解和使用这些数据库。尽管它们共用SQL语言,但每个系统都有独特的语法规则,了解这些差异有助于提升开发效率和项目成功率。
70 0
|
11天前
|
数据采集 存储 JavaScript
构建你的首个Python网络爬虫:抓取、解析与存储数据
【8月更文挑战第31天】在数字时代的浪潮中,数据成为了新的石油。了解如何从互联网的海洋中提取有价值的信息,是每个技术爱好者的必备技能。本文将引导你通过Python编程语言,利用其强大的库支持,一步步构建出你自己的网络爬虫。我们将探索网页请求、内容解析和数据存储等关键环节,并附上代码示例,让你轻松入门网络数据采集的世界。
|
12天前
|
数据采集 PHP
PHP中实现简单爬虫与数据解析
【8月更文挑战第31天】在互联网的海洋里,爬虫技术如同一艘探索船,帮助我们搜集和理解信息。本文将带你通过PHP语言,轻松实现一个简单的网页内容抓取工具,并展示如何从抓取的数据中解析出有用信息。我们将一起航行在代码的波浪中,体验从无到有构建一个基本爬虫的过程。

推荐镜像

更多