![Python3网络爬虫宝典](https://wfqqreader-1252317822.image.myqcloud.com/cover/331/33831331/b_33831331.jpg)
1.5 数据存储
顺利地发出网络请求并从响应正文中提取出想要的内容后,便要考虑如何将内容保存起来。数据的存储方式通常由需求方决定,如果需求方没有指定存储方式,则由爬虫工程师自己选择。
文字类数据通常存储在数据库中,例如MySQL数据库、MongoDB数据库或者Redis数据库。文件存储在服务器硬盘中,再将存储路径存储到数据库里。商品类数据或者后续用于分析的数据通常会存储在CSV或者xls文件中。将数据存储在文本文件中的情况比较少,但也不能忽略。
本节我们将学习如何将文字类数据存储到数据库中,如何存储文件类数据,如何将数据存储到xls文件中。
1.5.1 将数据存入MySQL数据库
MySQL是一款免费的关系型数据库,它免费且简单易用的特点使得它深受开发者的喜爱。Python连接或操作MySQL数据库时需要使用专门的连接库,例如PyMySQL。我们可以通过PyMySQL来操作MySQL数据库,例如创建数据库、创建数据表、写入数据、读取数据和删除数据等。
在开始学习之前,请按照MySQL官方文档的指引安装MySQL数据库。接着使用Python的包管理工具安装PyMySQL库,对应的安装命令如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_75.jpg?sign=1739223010-fC7PHSBmS4mlLhKSAUJJELRCbeGjvPJC-0-6977eb86586d6697d011ee91f1233c5d)
假设MySQL中有一个名为books的数据库,数据库里有一张名为ranking的数据表,其内容如表1-3所示。
表1-3 数据表ranking内容示意
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_76.jpg?sign=1739223010-7SthL7oDHEykxCH60CMCMu1GNEcBBSTv-0-7036eb2d436d757a8bb905a410371016)
接下来我们将围绕这张数据表执行一系列操作,以了解PyMySQL的基本使用方法。首先编写连接数据库的代码:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_77.jpg?sign=1739223010-fBR9tmdnTvfFVy1rrEpp8p8bIs8BtbXJ-0-a2179daf87c2626cf0387937ba08f9f9)
PyMySQL提供了execute()方法,我们可以将数据库操作语句以字符串的方式传入,包括查询语句、更新语句和删除语句等。查询数据表ranking中所有数据的代码为:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_78.jpg?sign=1739223010-gWutpJpu6w3XhGXnwdOL2729cym5vSur-0-783ea601353a086ceb7c70e7ff26dd02)
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_79.jpg?sign=1739223010-Lyv4qr5aCjq8WPiR6omI8VVWunp6vYbm-0-bb3ee55aa2c9d52b69defb8f2657d6ff)
代码执行结果如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_80.jpg?sign=1739223010-HITM6tEGxWpnMXH0wHZPawV78gYO61fQ-0-deab4aed0b42b27c86c6da8875d23846)
执行fetchall()方法后返回了一个列表对象,列表中以字典形式存储着数据库中的行数据。假设我们需要逐条打印书籍信息,那么我们可以用 for 循环,对应的代码为:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_81.jpg?sign=1739223010-fJxOFx9XTAE6SPWDqSL80ApFyx4zFjVq-0-f7d42fc6eb0baae805d796aedfbe9f9a)
代码运行结果如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_82.jpg?sign=1739223010-S9Sr96JJhcbcEqMtT1dOQpYtn3HmoUSh-0-0b9699c02ac9af666f3e47aa4a149078)
假设我们需要更新《Python3反爬虫原理与绕过实战》的价格,对应语句为:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_83.jpg?sign=1739223010-VcaHUoDiwlAClfBLQ6FhxvTKJRCIM1l7-0-c07c25b89857bbce4a63351fdf623a4b)
这段代码执行后并不会输出或打印结果,要想查看结果我们可以再次执行上一次的查询代码和循环代码,代码运行结果如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_84.jpg?sign=1739223010-9L0pXjnXrrCJjTJb29DiUh5sEYKAJfEE-0-b90cfd1e4aaff4299fb00700a74011f0)
图书《Python3反爬虫原理与绕过实战》的价格由89.0变成了66.7,说明更新语句执行成功。假设我们从网页提取的数据存放在对象data中,那么将data逐一添加到数据库的代码为:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_85.jpg?sign=1739223010-hHiQzSw7kK26WcOoKtmYi7496xArsOPM-0-7910794a075cd0a2273d4612de09e0fd)
代码运行后,数据库中的数据如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_86.jpg?sign=1739223010-56U5cN91HOZOs4UVXwyoMTmz53DNAMYX-0-27cc66de71b83001d8a96634d82c4189)
这说明数据已经存入了数据库中。以上就是使用PyMySQL操作MySQL数据库的基本方法,更多知识请翻阅PyMySQL官方文档。
1.5.2 将数据存入MongoDB数据库
MongoDB是一款基于分布式文件存储的非关系型数据库。MongoDB数据库不需要提前设置字段名称和对应的类型,使用时直接写入即可。同一个集合可以存储不同结构的文档,就算缺少字段也不会影响数据的写入。这两个特点正是它成为最受爬虫工程师欢迎的数据库的原因。
Python连接或操作MongoDB数据库时需要使用专门的连接库,例如PyMongo。我们可以通过PyMongo来操作MongoDB数据库,例如创建数据库、创建集合、写入文档、读取文档和删除文档等。
在开始学习之前,请按照MongoDB官方文档的指引安装MongoDB数据库。接着使用Python的包管理工具安装PyMongo库,对应的安装命令如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_87.jpg?sign=1739223010-sYMDTuzEOs9CY2kRiBiSprrTIFUQxLui-0-94b5d1c72480d833f48728742b4851c9)
由于MongoDB不需要提前建立数据库和集合,所以我们可以在代码中指定任意名称的数据库和集合。首先编写连接数据库的代码,并指定数据库名为books,指定集合名称为ranking:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_88.jpg?sign=1739223010-VPiHeiixmYgWq86vwZ91pFbBbyZm80tL-0-68b42c5eabd3147bfad3be0bfab09ac4)
假设我们从网页提取的数据存放在对象data中,那么将data添加到数据库的代码为:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_89.jpg?sign=1739223010-yh4Xecs92EQFkaHNAPcRxG9YwZp0sihd-0-c42278ca86b2bb2873a2cf77cf54215f)
从MongoDB中查询数据也很简单,代码如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_90.jpg?sign=1739223010-bsX4UqBd4cfNmieYAFQkheamDeqmxHpX-0-355825ecb7e13431bde4a8315790f231)
代码运行结果如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_91.jpg?sign=1739223010-ph9KIuneTAGXGBOJ0MPZTtIyHrDgu9N1-0-3af0ad7fc04180eb3201f2f2fcbb584f)
这里的_id是MongoDB自动为文档生成的唯一ID。当我们想要从数据库中查询指定的数据时,可以指定_id或者指定查询条件,例如查询价格大于50.0的书籍信息:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_92.jpg?sign=1739223010-4e0RguETmAdO45R0r03AgLtuW0YqjoWh-0-668cf63dd8c1a23988b2d50fd4e70f7f)
代码运行结果如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_93.jpg?sign=1739223010-9IPa1bjAZKrSmFblWIT80nqQ3nHExf3S-0-45465e3d22cf41f01940299ca9ed420a)
假设我们需要将图书《Go语言核心编程》的状态改为ON,则对应的代码如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_94.jpg?sign=1739223010-Nc3ITSmSOdJaDObcwJxqXFQgGBTBemWk-0-e00dfc1d6defda4093fc84ce72550a8b)
代码运行结果如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_95.jpg?sign=1739223010-z74jsWZ0pJhIW4GgPTyrQLkqGsDbw4a8-0-8df2395221dcc35add2ca4a5193c53c5)
运行结果说明我们已经成功地修改了图书《Go语言核心编程》的状态。熟悉MongoDB的读者会发现,PyMongo的语法与MongoDB原生语法十分接近,包括条件筛选、联合查询和删除操作。这样的设计减少了使用者的学习成本,这也是PyMongo库深受爬虫工程师欢迎的原因之一。
以上就是PyMongo操作MongoDB数据库的基本方法,更多知识请翻阅PyMongo官方文档。
1.5.3 将数据存入Redis数据库
Redis是目前流行的且性能极高的Key-Value数据库,爬虫工程师在设计分布式爬虫架构时通常会将Redis考虑到其中。与MySQL和MongoDB数据库相同的是,Python连接或操作Redis数据库时也需要使用专门的连接库,例如redis库。我们可以通过redis库来操作Redis数据库,例如创建集合、写入数据、读取数据和删除数据等。
在开始学习之前,请按照Redis官方文档的指引安装Redis数据库。接着使用Python的包管理工具安装redis库,对应的安装命令如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_96.jpg?sign=1739223010-5tDkoOymRM1kVLSAgXJdv4nuSVhvXG0m-0-ff56553e164399540020c67deef06d2d)
首先编写连接数据库的代码:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_97.jpg?sign=1739223010-VKLAVggKRavwJ5qw43QcYnJebdFJb8qQ-0-9753bc23afd0ac1f9d65ef8d2a23fb3f)
假设我们需要在Redis数据库中新建一个名为ranking的集合,并将爬虫爬取到的书籍信息写入到ranking中,对应的代码如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_98.jpg?sign=1739223010-Zg1yTh4WPpWCZhlB5vQeNZ1Rncg55277-0-7cc09b80e9a0ebeac7138e103cca4713)
查询Redis数据库中ranking集合数据的代码为:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_99.jpg?sign=1739223010-5dGDlqo4eng9nsyMtlpWOa0LB74i9rGg-0-ffddf4d53c17222208c5f00d6e5ec991)
代码运行结果如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_100.jpg?sign=1739223010-2t0SABWjRUmRqlmmjpTJUztO0vSBcOqF-0-324c3d57caafb27caf5fab1cc6e88cd9)
假设我们需要查询集合中存储了多少条数据,可以用SCARD命令,对应的代码如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_101.jpg?sign=1739223010-P6fTdLKIIntDPQhcCeQGvaMVzukGoord-0-81b11a249c1abdf3b91f6d68cdbe099b)
输出结果为3。
在实际的爬虫程序编写过程中,我们很有可能遇到这样的一种需求:数据条数满100条时存储1次,以达到降低数据库I/O消耗的目的。这种需求我们可以借助redis库提供的pipeline()方法实现,对应的代码为:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_102.jpg?sign=1739223010-ijQuqbs92AupvUkJAO8GCvh2n4LRATDE-0-ffbe8844069df5dd78d26368901c4be6)
待写入数据总数为1008条,每100条写入1次。考虑到剩下的8不满足100条的条件,遂在for循环外再调用一次execute()方法。代码运行后,Redis数据库中ranking集合的数据条数如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_103.jpg?sign=1739223010-XHUgJ0bEeKx9d2KrqbWFumBf6tJc5NSZ-0-8c1155db61d113df4d827d19cb5aeda1)
之前有3条数据,加上这次写入的1008条,共1011条,说明数据已经成功写入Redis数据库中。
跟PyMongo语法和MongoDB语法的关系一样,redis库的语法与Redis数据库原生语法也十分接近,只要了解Redis数据库的语法,一定能够快速掌握redis库的基本用法。
以上就是使用redis库操作Redis数据库的基本方法,更多知识请翻阅redis库的官方文档。
1.5.4 Excel文件的读写
Python对文件的读写有着原生的支持,我们只需要调用内置的open()函数或者使用上下文管理器with open()的方式创建一个文件并将内容写入即可。我们可以将str格式的文字写入到文本文件中,也可以将图片的bytes数据以wb模式写入到文件中,并用.png或.jpg等作为文件后缀。图片爬取和存储的代码片段如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_104.jpg?sign=1739223010-ZfQ6J9JTRI6Y40VZ0OLncg0gt0j8KKtD-0-d963f169c106ff08c7c78e02ed74ca22)
代码逻辑很简单,导入Requests库后调用get()方法向电子工业出版社官网LOGO图片的网址发出请求,然后提取bytes格式的响应信息,接着创建一个名为logo、后缀为.jpg的文件,并将bytes格式的响应信息写入文件。代码运行后,logo.jpg文件就会生成,图片如图1-32所示。
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_105.jpg?sign=1739223010-2wKDKgyKhiw3RMI9aCeHYYFMhd4YAKrx-0-f7a34d51589e1579f87944275fc53dab)
图1-32 logo.jpg
如果想要将数据按照一定格式存入Excel文档对应的xls文件中,可就没那么简单了。Python官方并不支持特定的文件格式,我们需要借助其他开发者编写的库向Excel文档写入数据或者从Excel文档中读取数据。Python开发者用得最多的Excel库分别是xlwt和xlrd,其中xlwt用于向Excel文档写入数据,xlrd用于从Excel文档读取数据。我们可以使用Python的包管理工具安装这两个库,对应的安装命令如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_106.jpg?sign=1739223010-eJRcLY34TeYFqVEUO8mJNAB41ynsGKls-0-a5bfc7fb428e5c6e591945bd12382861)
根据xlwt库的文档示例,我们可以很快地写出创建Excel文档、向文档指定的行和指定的列写入数据的代码。例如,创建一个名为abs.xls的文件,并在第1个Sheet的第1行第1列写入“你好”,对应的代码为:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_107.jpg?sign=1739223010-e9Bi5Sb45Ftsw53VFYYGPGjeReIvcick-0-877eda94fd310c0a01761eb7c1086f0e)
代码运行后,我们就会在目录中找到程序创建的abs.xls文件,打开后可以看到我们成功地将“你好”写入到指定的位置。接下来我们将在这个基础上增加一些难度,例如将一个列表中的数据存入Excel文档:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_108.jpg?sign=1739223010-CUA1mUEWNHBVIytdogKWSc5BaIkoGp3K-0-0e1da2e589232471ec4b5e2a3711cc2b)
这里用了for循环将列表中的数据一条一条地写入Excel文档的指定位置。代码运行后我们将在名为simple的Excel文件中看到如表1-4所示的信息。
表1-4 Excel文件内容
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_109.jpg?sign=1739223010-sh0pZ4kLRSAkB0cWV4TJq04bfnmDd8D9-0-85c3d844fef2db20e062f599da4bd124)
在第三方库的帮助下,Excel 文档的写入工作变得很轻松,相信读取工作也一样轻松。假设我们需要从simple.xls文件中读取刚才写入的数据,对应的代码为:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_110.jpg?sign=1739223010-D9iDH7N06J2t3fd8VHzFPEfif8ENMdi6-0-3f00f0dd71e31de84ea55dc7e7a09ece)
代码运行结果如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_111.jpg?sign=1739223010-4BNHWUlx6ofLWOWDtwAtDGcDmBjhr770-0-a293332df8a30892a01174776c268c37)
虽然成功地输出了Excel文件中的信息,但是似乎没有结构和顺序,我们试试看能不能将它们恢复成原来的结构。首先我们需要准备一个名为info的空列表,用于存放所有数据,然后再准备一个空的字典用于存放每一行不同列的数据,接着通过for循环行和for循环列将单元格中的数据取出来,并按照循环时的顺序将数据添加到字典中。我们只需要改动for循环部分的代码即可,对应改动如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_112.jpg?sign=1739223010-aDrtKyWzM05uWb2CwaTdyaZn3LexlApU-0-0226338ae2381b172c5060dcd68903c8)
代码运行结果如下:
![img](https://epubservercos.yuewen.com/141F12/18096061008240606/epubprivate/OEBPS/Images/txt001_113.jpg?sign=1739223010-rrXwniB6UXD5H0eTIv9hzcdqSavaKvH3-0-03602e2aee77b49696f75da5abde4b44)
运行结果说明我们成功地将Excel文档中的数据还原为存储前的格式。
以上就是使用xlwt和xlrd库读写Excel文档的基本方法,更多知识请翻阅对应的文档。