scrapy+baiduapi搭建一个私人小说阅读器(智能爬取加智能朗读)(一)

写在前面的话

喜欢看小说,平时都是通过电脑或者手机看小说,手机听小说(智能语音),或者喜马拉雅搜索小说听(好多喜欢的都收费o(╥﹏╥)o,然后网上好多免费资源却不能听),想在电脑上听小说,目前Microsoft Edge可以阅读网页文本很赞,不能自动翻译很烦(# ̄~ ̄#),而且智能语音库体验很差,所以想一个能搜索网上资源智能朗读的东东,木有发现好的,就自己写一个吧!

(Microsoft Edge阅读视图朗读示例)

 

 

小说资源获取(scrapy爬虫获取biquge资源)

1.scrapy环境搭建,这里就不说了,网上资源一堆,我也收录了一个,不知道可以看看,附带一个scrapy经典的架构图(转载)

 

    • 引擎(Scrapy)
      用来处理整个系统的数据流处理, 触发事务(框架核心)
    • 调度器(Scheduler)
      用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
    • 下载器(Downloader)
      用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
    • 爬虫(Spiders)
      爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
    • 项目管道(Pipeline)
      负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
    • 下载器中间件(Downloader Middlewares)
      位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。
    • 爬虫中间件(Spider Middlewares)
      介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。
    • 调度中间件(Scheduler Middewares)
      介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。

2.核心代码编写,按照爬虫架构,来编写爬虫及各个中间件内容(见代码)

  爬虫核心代码(BiqugeSpider),最高爬取深度4层,同时定义了关键字和全文爬取2种策略

  第1层 根据关键字搜索小说列表(默认函数parse),这里主要根据获取分页所有页url,传递给下一层,小说的基本信息列表爬取层

  

 

   代码如下(这里xpath就不做介绍了,有兴趣自己学习下):

    def parse(self, response):  
        index=0
        #检索小说(这里通过判断关键字来进行全文或者关键字爬取)
        if self.p:
            for each in response.xpath("//*[@class='search-result-page-main']/a"):
               src=each.attrib['href']
               yield scrapy.Request(self.domainUrl+ src, callback = self.ParsePage,dont_filter=False)            
        else:
            for each in response.xpath("//*[@id='main']/div[1]/ul/li"):
                if index>0:
                    src=each.xpath(".//span[2]/a")[0].attrib['href']
                    yield scrapy.Request(self.domainUrl+ src, callback = self.BookBasic,dont_filter=False) 
                index=index+1

  第2层 根据小说基本信息列表获取每本小说的详情url,传递给下一层,小说基本信息爬取层

  图和第一层类似,这里省略

  代码如下:

 

    #获取分页内容,进行关键字深度爬取
    def ParsePage(self,response):
        for each in response.xpath("//*[@class='result-list']/div"):
            src=each.xpath(".//div/a")[0].attrib['href']
            yield scrapy.Request(self.domainUrl+ src, callback = self.BookBasic,dont_filter=False) 

 

  第3层 根据小说分页列表获取每本检索到小说的基本信息,同时获取章节列表url,传递给下一层,小说内容爬取层

  

 

  代码如下:(存储了小说的名称、作者、简介信息)

#获取小说基本信息,同时让调度器爬取小说内容
    def BookBasic(self,response):
        Id=uuid.uuid1()
        BookName=response.xpath("string(//*[@id='info']/h1)")[0].root.strip()  
        Author=response.xpath("string(//*[@id='info']/p)")[0].root.strip().split('')[1]
        imgSrc=response.xpath("//*[@id='fmimg']/img")[0].attrib['src']
        img= requests.get(imgSrc)
        Image= img.content
        LatestChapter=response.xpath("string(//*[@id='info']/p[4]/a)")[0].root.strip()
        Desc1=response.xpath("string(//*[@id='intro'])")[0].root.strip()
        #查询小说是否存在
        resp= self.mysql.Query("select Id from BookBasic where BookName='{0}'".format(BookName))
        if not resp:          
           # 获取小说信息          
           Id=uuid.uuid1()
           #插入数据
           self.mysql.ExecuteSql("insert into BookBasic (BookName,Author,Image,LatestChapter,Desc1,Id) values(%s,%s,%s,%s,%s,%s)",(str(BookName),str(Author),Image,str(LatestChapter),str(Desc1),str(Id)))
        else:
            Id=resp[0][0]
            self.mysql.ExecuteSql("update BookBasic set LatestChapter=%s,Desc1=%s where Id=%s",(str(LatestChapter),str(Desc1),str(Id)))
        #查询所有章节
        list=self.mysql.Query("select Title from BookContent where Id='{0}'".format(str(Id)))
        index=0
        listName=[]
        for each in response.xpath("//*[@id='list']/dl/dd"):
             try:                                             
                 src= each.xpath(".//a")[0].attrib['href']
                 title=each.xpath("string(.//a)")[0].root.strip()
                 if self.InTable(list,title):
                     print(title,"已经入库...")
                 elif title in listName:
                     print(title,"重复章节...")                
                 else:
                     request= scrapy.Request(self.domainUrl+ src, callback = self.BookContent,dont_filter=False)   
                     request.meta['id']=Id
                     request.meta['Chapter']=index
                     index=index+1
                     listName.append(title)
                     yield request
             except Exception as e:
                 print("analysis item error:"+item["title"]+ e);

  第4层 根据小说章节url获取小说内容信息,存储小说内容,到此,小说内容全部爬取完成

  

 

   代码如下:

  #爬取每个章节小说内容
    def BookContent(self,response):
        try:
            item=BiqugespiderItem()
            item['Title']=response.xpath("string(//*[@class='bookname']/h1)")[0].root.strip()
            item['Content']=response.xpath("string(//*[@id='content'])")[0].root.strip()
            item['Id']= response.meta['id']
            item['Chapter']= response.meta['Chapter']
            yield item
        except Exception as e:
            print("login item error:"+item["title"]+ e);

 

 

 3.持久化处理

上面说到了爬虫的内容怎么获取,下面说到存储,当然这层可以自己处理,这里为了方便使用腾讯云mysql进行存储小说信息(mysql帮助类)

数据库和表结构初始化:

 #初始化数据库
    def IniMysql(self):
         #创建数据库
      conn=pymysql.connect(host=self.host,port=self.port,user=self.user,password=self.password,database='mysql')
      sql='create database if not exists Biquge'
      cursor=conn.cursor()
      cursor.execute(sql)
      cursor.close()
      conn.close()           
      #创建表
      conn=pymysql.connect(host=self.host,port=self.port,user=self.user,password=self.password,database=self.defaultDb)
      cursor=conn.cursor()
      sql= """CREATE TABLE IF NOT EXISTS BookBasic(
                    BookName varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '书名',
                    Author varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '作者',
                    Image longblob NULL COMMENT '书封面',
                    LatestChapter varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '最新章节',
                    Desc1 varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '内容描述',
                    Id char(36) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '主键',
                    PRIMARY KEY (`Id`) USING BTREE
                  ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
              """   
      cursor.execute(sql)
      sql="""CREATE TABLE IF NOT EXISTS BookContent(
                    DId char(36) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
                    Title varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '详情标题',
                    Content longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '内容信息',
                    Id char(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '书主键',
                    Chapter varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '章节',
                    SyncTime datetime(0) NULL DEFAULT NULL COMMENT '入库时间',
                    PRIMARY KEY (`DId`) USING BTREE
                  ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
          """
      cursor.execute(sql)
      cursor.close()
      conn.close()      

 

小说基本信息存储代码:

 if not resp:          
           # 获取小说信息          
           Id=uuid.uuid1()
           #插入数据
           self.mysql.ExecuteSql("insert into BookBasic (BookName,Author,Image,LatestChapter,Desc1,Id) values(%s,%s,%s,%s,%s,%s)",(str(BookName),str(Author),Image,str(LatestChapter),str(Desc1),str(Id)))
        else:
            Id=resp[0][0]
            self.mysql.ExecuteSql("update BookBasic set LatestChapter=%s,Desc1=%s where Id=%s",(str(LatestChapter),str(Desc1),str(Id)))

小说内容信息存储代码:

class BiqugespiderPipeline(object):
    def process_item(self, item, spider):
        DId=uuid.uuid1()
        SyncTime=datetime.datetime.now()
        mysql=MySqlComment()
        mysql.ExecuteSql("insert into BookContent values(%s,%s,%s,%s,%s,%s)",(str(DId),str(item['Title']),str(item['Content']),str(item['Id']),str(item['Chapter']),SyncTime))
        return item

 

数据库存储结果:

(小说基本信息)

 

(小说内容信息)

 

 

 4.代码地址

只能朗读客户端下一章继续写,代码github地址先附上

 

posted @ 2020-01-13 15:08  comicwang  阅读(941)  评论(0编辑  收藏  举报