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

news2024/11/27 6:38:55

目录:

  • 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.html

import scrapy

class 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 datetime

import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule

from ..items import NovelItem,ChapterItem,ContentItem


class 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匹配到的链接的回调函数
        # 匹配每个书籍的url
        Rule(LinkExtractor(allow=r'https://book.zongheng.com/book/\d+.html',
                           restrict_xpaths=("//div[@class='bookname']")), callback='parse_book', follow=True,
             process_links="process_booklink"),

        # 匹配章节目录的url
        Rule(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 link
            else:
                return

    def process_chapterlink(self, links):
        for index,link in enumerate(links):
            #限制21章内容
            if index<=20:
                print("限制20章内容:",link.url)
                yield link
            else:
                return

    def 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.url
        catalog_url = response.css("a").re("https://book.zongheng.com/showchapter/\d+.html")[0]

        item=NovelItem()
        item["category"]=category
        item["book_name"]=book_name
        item["author"]=author
        item["status"]=status
        item["book_nums"]=book_nums
        item["description"]=description
        item["c_time"]=c_time
        item["book_url"]=book_url
        item["catalog_url"]=catalog_url
        yield item

    def 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 + 1
            c_time = datetime.datetime.now()
            catalog_url = response.url
            chapter_list.append([title, ordernum, c_time, chapter_url, catalog_url])

        item=ChapterItem()
        item["chapter_list"]=chapter_list
        yield item


    def get_content(self, response):
        content = "".join(response.xpath('//div[@class="content"]/p/text()').extract())
        chapter_url = response.url
        
        item=ContentItem()
        item["content"]=content
        item["chapter_url"]=chapter_url
        yield 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.html

import 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中如果没有则为None
                try:
                    #如果没有获得一个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 item
        elif 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 item
        elif 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 item

    def 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开始了。
    这该咋办呢?
    在这里插入图片描述在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Pspice-介绍

根据B站&#xff1a;【PSpice简单入门教程】 https://www.bilibili.com/video/BV19Z4y157tA/?share_sourcecopy_web&vd_sourcec20b4c8e2a733bf3f410c58538211a7f&#xff08;一&#xff09;原理图仿真的原因仿真节省经费&#xff0c;比如蒙特卡罗分析&#xff0c;最坏情况…

R语言raster包批量拼接、融合大量栅格图像

本文介绍基于R语言中的raster包&#xff0c;遍历文件夹&#xff0c;读取文件夹下的大量栅格遥感影像&#xff0c;并逐一对每一景栅格图像加以拼接、融合&#xff0c;使得全部栅格遥感影像拼接为完整的一景图像的方法。 其中&#xff0c;本文是用R语言来进行操作的&#xff1b;如…

【MongoDB】多级嵌套数组的操作 含Mongo Shell 和 MongoTemplate的增删改细节

文章目录1.前言2.数据准备3.Mongo Shell操作实践3.1.第一层数组操作3.1.1.新增元素3.1.2.修改元素3.1.2.1.批量修改元素中的坑3.1.3.使用$[<identifier>]做批量修改3.1.4.移除元素3.2.第二层数组操作3.2.1.新增与移除元素3.2.2.修改元素中的字段值3.2.2.1.易错点4.Mongo …

基于yolo的小球位置实时检测

基于yolo的小球位置实时检测 Yolo安装 操作系统&#xff1a;ubuntu 安装cuda和opencv git clone https://github.com/pjreddie/darknet.git cd darknet 修改Makefile文件&#xff0c;使GPU1&#xff0c;OPENCV1 make 2. 数据集处理 2.1 制作数据集 将小球放在摄像头前…

分布式高级篇2 —— 商城业务 (1)

一、商品上架 一、商品上架1、sku 在 ES 中存储模型分析2、nested 嵌套类型3、商品上架业务代码&#xff08;1&#xff09;构建 sku 基本属性&#xff08;2&#xff09;构造 sku 检索属性&#xff08;3&#xff09;远程调用查询库存&#xff08;4&#xff09; 远程调用上架接口…

【干货】Python:time库的用法

【干货】Python&#xff1a;time库的用法1. time库概述2. time库的运用2.1 时间处理2.1.1 time()2.1.2 gmtime()2.1.3 localtime()2.1.4 ctime()2.2 时间格式化2.2.1 mktime()2.2.2 strftime()2.2.3 strptime()2.3 计时3. 习题4. 本文代码编译环境及版本5. 更新日志6. 参考1. t…

【CSS面试题】2023前端最新版css模块,高频15问

&#x1f973;博 主&#xff1a;初映CY的前说(前端领域) &#x1f31e;个人信条&#xff1a;想要变成得到&#xff0c;中间还有做到&#xff01; &#x1f918;本文核心&#xff1a;博主收集的CSS面试题 目录 一、CSS必备面试题 1.CSS3新特性 2.CSS实现元素两个盒子垂…

内网穿透-frp

frp的作用 1.利用处于内网或防火墙后的机器&#xff0c;对外网环境提供 http 或 https 服务。 2.对于 http, https 服务支持基于域名的虚拟主机&#xff0c;支持自定义域名绑定&#xff0c;使多个域名可以共用一个80端口。 3.利用处于内网或防火墙后的机器&#xff0c;对外网环…

【Python】Python学习笔记(二)基本输入输出

Python娘来源&#xff1a;https://next.rikunabi.com/tech/docs/ct_s03600.jsp?p002412 目录print()函数不进行自动换行的print()函数打印输出多个字符串只进行换行input()函数使用format方法格式化字符串字符串与数值转换字符串转换为数值数值转换为字符串总结参考资料print(…

SpringCloud(13)— 分布式缓存(Redis集群)

分布式缓存(Redis集群) 前言 单节点Redis的问题 1.数据丢失 Redis基于内存存储&#xff0c;服务器重启可能会导致数据丢失 2.并发能力 单节点Redis的并发能力虽然已经很不错&#xff0c;但是依然无法满足大型的高并发场景 3.故障恢复 如果Redis宕机&#xff0c;则服务将不…

C# 调用Python

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

电子器件系列31:ULN2003 芯片详解

主体转自&#xff1a; uln2003驱动电路_身在江湖的郭大侠的博客-CSDN博客_uln2003 一、uln2003有什么作用 ULN2003是大电流驱动阵列&#xff0c;多用于单片机、智能仪表、PLC、数字量输出卡等控制电路中。可直接驱动继电器等负载。 输入5VTTL电平&#xff0c;输出可达500mA/…

167. 两数之和 II - 输入有序数组

给你一个下标从 1 开始的整数数组 numbers &#xff0c;该数组已按 非递减顺序排列 &#xff0c;请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] &#xff0c;则 1 < index1 < index2 < numbers…

每天一道大厂SQL题【Day07】教育领域SQL实战

每天一道大厂SQL题【Day07】教育领域SQL实战 大家好&#xff0c;我是Maynor。相信大家和我一样&#xff0c;都有一个大厂梦&#xff0c;作为一名资深大数据选手&#xff0c;深知SQL重要性&#xff0c;接下来我准备用100天时间&#xff0c;基于大数据岗面试中的经典SQL题&#…

C进阶实战通讯录

C语言实战通讯录C语言实战通讯录前言整理逻辑整体框架初始化通讯录添加联系人显示联系人删除联系人查找联系人修改联系人销毁通讯录保存联系人信息加载联系人信息所有源码&#xff1a;test.c&#xff1a;Contact.h:Contact.c:C语言实战通讯录 前言 这次用C语言实现通讯录是一…

Nginx_4

Nginx负载均衡 负载均衡概述 早期的网站流量和业务功能都比较简单&#xff0c;单台服务器足以满足基本的需求&#xff0c;但是随着互联网的发展&#xff0c;业务流量越来越大并且业务逻辑也跟着越来越复杂&#xff0c;单台服务器的性能及单点故障问题就凸显出来了&#xff0c…

VHDL语言基础-组合逻辑电路-基本逻辑门电路

数字电路中的四种基本操作是与、或、非及触发器操作&#xff0c;前三种为组合电路&#xff0c;后一种为时序电路。与非 、或非和异或的操作仍然是与、或、非的基本操作。与、或、非、与非、或非和异或等基本逻辑门电路为常用的门电路。 二输入与非门是一种常用的简单逻辑电路&a…

当下最流行的 ChatGPT :前世今生

GPT 不是凭空而出&#xff0c;它是经过了很多人的努力&#xff0c;以及很长一段时间的演化得来的。因此&#xff0c;梳理一下 GPT 的庞大 “家族” 还是很有必要的&#xff0c;看看他继承了什么&#xff0c;学习了什么&#xff0c;又改进了什么&#xff0c;这样也能更好地理解 …

C++设计模式(11)——桥接模式

亦称&#xff1a; Bridge 意图 桥接模式是一种结构型设计模式&#xff0c; 可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构&#xff0c; 从而能在开发时分别使用。 问题 抽象&#xff1f; 实现&#xff1f; 听上去挺吓人&#xff1f; 让我们慢慢来&…

linux笔记 diff及patch的制作与使用

相关命令展示 为方便查阅博客使用&#xff0c;预先展示相关命令 diff命令 diff -uN old.txt new.txt > patch_test.patch单个文件&#xff0c;不需要使用-r参数 diff 选项参数 旧文件&#xff08;夹&#xff09; 新文件&#xff08;夹&#xff09; > 补丁diff命令的常…