当前位置: 首页 > news >正文

Python爬虫之Scrapy框架系列(13)——实战ZH小说爬取数据入MySql数据库

目录:

  • 1 数据持久化存储,写入Mysql数据库
    • ①定义结构化字段:
    • ②重新编写爬虫文件:
    • ③编写管道文件:
    • ④辅助配置(修改settings.py文件):
    • ⑤navicat创库建表:
    • ⑥ 效果如下:

1 数据持久化存储,写入Mysql数据库

①定义结构化字段:

  • (items.py文件的编写):
# -*- coding: utf-8 -*-# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.htmlimport scrapyclass NovelItem(scrapy.Item):'''匹配每个书籍URL并解析获取一些信息创建的字段'''# define the fields for your item here like:# name = scrapy.Field()category = scrapy.Field()book_name = scrapy.Field()author = scrapy.Field()status = scrapy.Field()book_nums = scrapy.Field()description = scrapy.Field()c_time = scrapy.Field()book_url = scrapy.Field()catalog_url = scrapy.Field()class ChapterItem(scrapy.Item):'''从每个小说章节列表页解析当前小说章节列表一些信息所创建的字段'''# define the fields for your item here like:# name = scrapy.Field()chapter_list = scrapy.Field()class ContentItem(scrapy.Item):'''从小说具体章节里解析当前小说的当前章节的具体内容所创建的字段'''# define the fields for your item here like:# name = scrapy.Field()content = scrapy.Field()chapter_url = scrapy.Field()

②重新编写爬虫文件:

  • (将解析的数据对应到字段里,并将其yield返回给管道文件pipelines.py)
# -*- coding: utf-8 -*-
import datetimeimport scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rulefrom ..items import NovelItem,ChapterItem,ContentItemclass Bh3Spider(CrawlSpider):name = 'zh'allowed_domains = ['book.zongheng.com']start_urls = ['https://book.zongheng.com/store/c0/c0/b0/u1/p1/v0/s1/t0/u0/i1/ALL.html']rules = (# Rule定义爬取规则: 1.提取url(LinkExtractor对象)   2.形成请求    3.响应的处理规则# 源码:Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True)# 1.LinkExractor是scrapy框架定义的一个类,它定义如何从每个已爬网页面中提取url链接,并将这些url作为新的请求发送给引擎# 引擎经过一系列操作后将response给到callback所指的回调函数。# allow=r'Items/'的意思是提取链接的正则表达式   【相当于findall(r'Items/',response.text)】# 2.callback='parse_item'是指定回调函数。# 3.follow=True的作用:LinkExtractor提取到的url所生成的response在给callback的同时,还要交给rules匹配所有的Rule规则(有几条遵循几条)# 拿到了书籍的url                                  回调函数                            process_links用于处理LinkExtractor匹配到的链接的回调函数# 匹配每个书籍的urlRule(LinkExtractor(allow=r'https://book.zongheng.com/book/\d+.html',restrict_xpaths=("//div[@class='bookname']")), callback='parse_book', follow=True,process_links="process_booklink"),# 匹配章节目录的urlRule(LinkExtractor(allow=r'https://book.zongheng.com/showchapter/\d+.html',restrict_xpaths=('//div[@class="fr link-group"]')), callback='parse_catalog', follow=True),# 章节目录的url生成的response,再来进行具体章节内容的url的匹配     之后此url会形成response,交给callback函数Rule(LinkExtractor(allow=r'https://book.zongheng.com/chapter/\d+/\d+.html',restrict_xpaths=('//ul[@class="chapter-list clearfix"]')), callback='get_content',follow=False, process_links="process_chapterlink"),# restrict_xpaths是LinkExtractor里的一个参数。作用:过滤(对前面allow匹配到的url进行区域限制),只允许此参数匹配的allow允许的url通过此规则!!!)def process_booklink(self, links):for index, link in enumerate(links):# 限制一本书if index == 0:print("限制一本书:", link.url)yield linkelse:returndef process_chapterlink(self, links):for index,link in enumerate(links):#限制21章内容if index<=20:print("限制20章内容:",link.url)yield linkelse:returndef parse_book(self, response):print("解析book_url")# 字数:book_nums = response.xpath('//div[@class="nums"]/span/i/text()').extract()[0]# 书名:book_name = response.xpath('//div[@class="book-name"]/text()').extract()[0].strip()category = response.xpath('//div[@class="book-label"]/a/text()').extract()[1]author = response.xpath('//div[@class="au-name"]/a/text()').extract()[0]status = response.xpath('//div[@class="book-label"]/a/text()').extract()[0]description = "".join(response.xpath('//div[@class="book-dec Jbook-dec hide"]/p/text()').extract())c_time = datetime.datetime.now()book_url = response.urlcatalog_url = response.css("a").re("https://book.zongheng.com/showchapter/\d+.html")[0]item=NovelItem()item["category"]=categoryitem["book_name"]=book_nameitem["author"]=authoritem["status"]=statusitem["book_nums"]=book_numsitem["description"]=descriptionitem["c_time"]=c_timeitem["book_url"]=book_urlitem["catalog_url"]=catalog_urlyield itemdef parse_catalog(self, response):print("解析章节目录", response.url)  # response.url就是数据的来源的url# 注意:章节和章节的url要一一对应a_tags = response.xpath('//ul[@class="chapter-list clearfix"]/li/a')chapter_list = []for index, a in enumerate(a_tags):title = a.xpath("./text()").extract()[0]chapter_url = a.xpath("./@href").extract()[0]ordernum = index + 1c_time = datetime.datetime.now()catalog_url = response.urlchapter_list.append([title, ordernum, c_time, chapter_url, catalog_url])item=ChapterItem()item["chapter_list"]=chapter_listyield itemdef get_content(self, response):content = "".join(response.xpath('//div[@class="content"]/p/text()').extract())chapter_url = response.urlitem=ContentItem()item["content"]=contentitem["chapter_url"]=chapter_urlyield item

③编写管道文件:

  • (pipelines.py文件)
  • 数据存储到MySql数据库分三步走:
    ①存储小说信息;
    ②存储除了章节具体内容以外的章节信息(因为:首先章节信息是有序的;其次章节具体内容是在一个新的页面里,需要发起一次新的请求);
    ③更新章节具体内容信息到第二步的表中。
# -*- coding: utf-8 -*-# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.htmlimport pymysql
import logging
from .items import NovelItem,ChapterItem,ContentItem
logger=logging.getLogger(__name__)      #生成以当前文件名命名的logger对象。 用日志记录报错。class ZonghengPipeline(object):def open_spider(self,spider):# 连接数据库data_config = spider.settings["DATABASE_CONFIG"]if data_config["type"] == "mysql":self.conn = pymysql.connect(**data_config["config"])self.cursor = self.conn.cursor()def process_item(self, item, spider):# 写入数据库if isinstance(item,NovelItem):#写入书籍信息sql="select id from novel where book_name=%s and author=%s"self.cursor.execute(sql,(item["book_name"],item["author"]))if not self.cursor.fetchone():          #.fetchone()获取上一个查询结果集。在python中如果没有则为Nonetry:#如果没有获得一个id,小说不存在才进行写入操作sql="insert into novel(category,book_name,author,status,book_nums,description,c_time,book_url,catalog_url)"\"values(%s,%s,%s,%s,%s,%s,%s,%s,%s)"self.cursor.execute(sql,(item["category"],item["book_name"],item["author"],item["status"],item["book_nums"],item["description"],item["c_time"],item["book_url"],item["catalog_url"],))self.conn.commit()except Exception as e:          #捕获异常并日志显示self.conn.rollback()logger.warning("小说信息错误!url=%s %s")%(item["book_url"],e)return itemelif isinstance(item,ChapterItem):#写入章节信息try:sql="insert into chapter (title,ordernum,c_time,chapter_url,catalog_url)"\"values(%s,%s,%s,%s,%s)"#注意:此处item的形式是!  item["chapter_list"]====[(title,ordernum,c_time,chapter_url,catalog_url)]chapter_list=item["chapter_list"]self.cursor.executemany(sql,chapter_list)     #.executemany()的作用:一次操作,写入多个元组的数据。形如:.executemany(sql,[(),()])self.conn.commit()except Exception as e:self.conn.rollback()logger.warning("章节信息错误!%s"%e)return itemelif isinstance(item,ContentItem):try:sql="update chapter set content=%s where chapter_url=%s"content=item["content"]chapter_url=item["chapter_url"]self.cursor.execute(sql,(content,chapter_url))self.conn.commit()except Exception as e:self.conn.rollback()logger.warning("章节内容错误!url=%s %s") % (item["chapter_url"], e)return itemdef close_spider(self,spider):# 关闭数据库self.cursor.close()self.conn.close()

④辅助配置(修改settings.py文件):

第一个:关闭robots协议;
第二个:开启延迟;
第三个:加入头文件;
第四个:开启管道:

在这里插入图片描述

第五个:配置连接Mysql数据库的参数:

DATABASE_CONFIG={"type":"mysql","config":{"host":"localhost","port":3306,"user":"root","password":"123456","db":"zongheng","charset":"utf8"}
}

⑤navicat创库建表:

(1)创库:

在这里插入图片描述

(2)建表:(注意:总共需要建两张表!)

  1. 存储小说基本信息的表,表名为novel
    在这里插入图片描述在这里插入图片描述

  2. 存储小说具体章节内容的表,表名为chapter:

  • 注意id不要忘记设自增长了!
    在这里插入图片描述

⑥ 效果如下:

在这里插入图片描述

在这里插入图片描述

  • 拓展操作:
    如果来回调试有问题的话,需要删除表中所有数据重新爬取,直接使用navicate删除表中所有数据(即delete操作),那么id自增长就不会从1开始了。
    这该咋办呢?
    在这里插入图片描述在这里插入图片描述

相关文章:

Python爬虫之Scrapy框架系列(13)——实战ZH小说爬取数据入MySql数据库

目录&#xff1a;1 数据持久化存储&#xff0c;写入Mysql数据库①定义结构化字段&#xff1a;②重新编写爬虫文件&#xff1a;③编写管道文件&#xff1a;④辅助配置&#xff08;修改settings.py文件&#xff09;&#xff1a;⑤navicat创库建表&#xff1a;⑥ 效果如下&#xf…...

MySQL篇02-三大范式,多表查询

数据入库时,由于数据设计不合理&#xff0c;会存在数据重复、更新插入异常等情况, 故数据库中表的设计遵循的设计规范&#xff1a;三大范式1.第一范式(1NF)要求数据库的每一列都是不可分割的原子数据项&#xff0c;即原子性。强调的是列的原子性&#xff0c;即数据库中每一列的…...

vue-cli3创建Vue项目

文章目录前言一、使用vue-cli3创建项目1.检查当前vue的版本2.下载并安装Vue-cli33.使用命令行创建项目二、关于配置前言 本文讲解了如何使用vue-cli3创建属于自己的Vue项目&#xff0c;如果本文对你有所帮助请三连支持博主&#xff0c;你的支持是我更新的动力。 下面案例可供…...

Linux perf probe 的使用(三)

文章目录前言一、Dynamic Tracing二、kprobes2.1 perf kprobe 的使用2.2 kprobe Arguments3.3 tcp_sendmsg()3.3.1 Kernel: tcp_sendmsg()3.3.2 Kernel: tcp_sendmsg() with size3.3.2 Kernel: tcp_sendmsg() line number and local variable三、uprobes的使用3.1 perf uprobe …...

python GUI编程 多窗口跳转

# 多窗口跳转例子from tkinter import *def main(): # 主窗体def goto(num):root.destroy() # 关闭主窗体if num 1:one() # 进入第1个窗体elif num 2:two() # 进入第2个窗体root Tk()root.geometry(300x150600200)root.title(登录窗口)but1 Button(root, text"进入…...

nuxt 学习笔记

这里写目录标题路由跳转NuxtLinkquery参数params参数嵌套路由tab切换效果layouts 文件夹强制约定放置所有布局文件&#xff0c;并以插槽的形式作用在页面中1.在app.vue里面2.component 组件使用Vue < component :is"">Vuex生命周期数据请求useFetchuseAsyncDat…...

Python编程自动化办公案例(1)

作者简介&#xff1a;一名在校计算机学生、每天分享Python的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.使用库讲解 1.xlrd 2.xlwt 二.主要案例 1.批量合并 模板如下&#xf…...

一站式 Elasticsearch 集群指标监控与运维管控平台

上篇文章写了一下消息运维管理平台&#xff0c;今天带来的是ES的监控和运维平台。目前初创企业&#xff0c;不像大型互联网公司&#xff0c;可以重复的造轮子。前期还是快速迭代试错阶段&#xff0c;方便拿到市场反馈&#xff0c;及时调整自己的战略和产品方向。让自己活下去&a…...

C# 调用Python

一、简介 IronPython 是一种在 NET 和 Mono 上实现的 Python 语言&#xff0c;由 Jim Hugunin&#xff08;同时也是 Jython 创造者&#xff09;所创造。 Python是一种跨平台的计算机程序设计语言。 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python是…...

51单片机最强模块化封装(3)

文章目录 前言一、创建smg文件,添加smg文件路径二、smg文件编写三、模块化测试总结前言 本篇文章将带大家继续封装我们的代码。 这里我们会封装数码管的操作函数。 一、创建smg文件,添加smg文件路径 这里的操作就不过多解释了,大家自行看前面的文章即可。 51单片机模块化…...

【CSS 布局】水平垂直居中

CSS 布局-水平垂直居中 一、水平居中 创建一个父盒子&#xff0c;和子盒子 <div class"parent"><div class"child"></div> </div>基本样式如下 .parent {background-color: #fff; }.child {background-color: #999;width: 100p…...

【C++】类和对象--类的6个默认成员函数

目录1.类的6个默认成员函数2.构造函数2.1概念2.2特性3.析构函数3.1概念3.2特性4.拷贝构造函数4.1概念4.2特征5.赋值运算符重载5.1运算符重载5.2赋值运算符重载5.3前置和后置重载5.4流插入和流提取运算符重载6.const成员7.取地址重载和const取地址操作符重载1.类的6个默认成员函…...

常见面试题---------如何处理MQ消息丢失的问题?

如何处理MQ消息丢失的问题? RabbitMQ丢失消息分为如下几种情况&#xff1a; 生产者丢消息&#xff1a; 生产者将数据发送到RabbitMQ的时候&#xff0c;可能在传输过程中因为网络等问题而将数据弄丢了。 RabbitMQ自己丢消息&#xff1a; 如果没有开启RabbitMQ的持久化&#x…...

十四、Linux网络:高级IO

目录 五种IO模型 同步IO 阻塞IO 非阻塞IO 信号驱动IO IO多路转接 异步IO...

带你走进API安全的知识海洋

Part1什么是API API&#xff08;Application Programming Interface&#xff0c;应用程序接口&#xff09;是一些预先定义的接口&#xff08;如函数、HTTP接口&#xff09;&#xff0c;或指软件系统不同组成部分衔接的约定。用来提供应用程序与开发人员基于某软件或硬件得以访…...

【Java】TCP的三次握手和四次挥手

三次握手 TCP三次握手是一个经典的面试题&#xff0c;它指的是TCP在传递数据之前需要进行三次交互才能正式建立连接&#xff0c;并进行数据传递。&#xff08;客户端主动发起的&#xff09;TCP之所以需要三次握手是因为TCP双方都是全双工的。 什么是全双工&#xff1f; TCP任何…...

JUC并发编程

1.什么是JUC java.util工具包、包、分类 业务&#xff1a;普通业务线程代码 Thread Runable: 没有返回值、效率相比Callable相对较低。 2.线程和进程 进程&#xff1a;一个程序&#xff0c;QQ.exe Music.exe 程序的集合 一个进程往往可以包含多个线程&#xff0c;至少包含一个…...

概率统计·假设检验【正态总体均值的假设检验、正态总体方差的假设检验】

均值假设检验定义 2类错误 第1类错误&#xff08;弃真&#xff09;&#xff1a;当原假设H0为真&#xff0c;观察值却落入拒绝域&#xff0c;因而拒 绝H0这类错误是“以真为假” 犯第一类错误的概率显著性水平α第2类错误&#xff08;取伪&#xff09;&#xff1a;当原假设H0不…...

如何预测机组设备健康状态?你可能需要这套解决方案

1. 应用场景随机振动[注1]会发生在工业物联网的各个场景中&#xff0c;包括产线机组设备的运行、运输设备的移动、试验仪器的运行等等。通过分析采集到的振动信号可以预估设备的疲劳年限、及时知晓设备已发生的异常以及预测未来仪器可能发生的异常等等。本篇教程会提供给有该方…...

C++类和对象:面向对象编程的核心。| 面向对象还编什么程啊,活该你是单身狗。

&#x1f451;专栏内容&#xff1a;C学习笔记⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;日拱一卒&#xff0c;功不唐捐 文章目录一、前言二、面向对象编程三、类和对象1、类的引入2、类的定义Ⅰ、声明和定义在一起Ⅱ、声明和定义分开Ⅲ、成员变量命…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

Python训练营-Day26-函数专题1:函数定义与参数

题目1&#xff1a;计算圆的面积 任务&#xff1a; 编写一个名为 calculate_circle_area 的函数&#xff0c;该函数接收圆的半径 radius 作为参数&#xff0c;并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求&#xff1a;函数接收一个位置参数 radi…...

图解JavaScript原型:原型链及其分析 | JavaScript图解

​​ 忽略该图的细节&#xff08;如内存地址值没有用二进制&#xff09; 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么&#xff1a;保存在堆中一块区域&#xff0c;同时在栈中有一块区域保存其在堆中的地址&#xff08;也就是我们通常说的该变量指向谁&…...

医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor

1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...

当下AI智能硬件方案浅谈

背景&#xff1a; 现在大模型出来以后&#xff0c;打破了常规的机械式的对话&#xff0c;人机对话变得更聪明一点。 对话用到的技术主要是实时音视频&#xff0c;简称为RTC。下游硬件厂商一般都不会去自己开发音视频技术&#xff0c;开发自己的大模型。商用方案多见为字节、百…...

Selenium 查找页面元素的方式

Selenium 查找页面元素的方式 Selenium 提供了多种方法来查找网页中的元素&#xff0c;以下是主要的定位方式&#xff1a; 基本定位方式 通过ID定位 driver.find_element(By.ID, "element_id")通过Name定位 driver.find_element(By.NAME, "element_name"…...

spring boot使用HttpServletResponse实现sse后端流式输出消息

1.以前只是看过SSE的相关文章&#xff0c;没有具体实践&#xff0c;这次接入AI大模型使用到了流式输出&#xff0c;涉及到给前端流式返回&#xff0c;所以记录一下。 2.resp要设置为text/event-stream resp.setContentType("text/event-stream"); resp.setCharacter…...

【Qt】控件 QWidget

控件 QWidget 一. 控件概述二. QWidget 的核心属性可用状态&#xff1a;enabled几何&#xff1a;geometrywindows frame 窗口框架的影响 窗口标题&#xff1a;windowTitle窗口图标&#xff1a;windowIconqrc 机制 窗口不透明度&#xff1a;windowOpacity光标&#xff1a;cursor…...