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地址先附上

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM