• 关于使用scrapy框架爬取小说的进一步优化


    一、背景:前面使用scrapy框架爬取小说时,需要修改两个文件内容才能开展新的爬取,考虑只修改一个文件,把需要修改的参数都放入爬虫文件中。

    二、实现过程:

    1、items.py中添加三个全局变量

    (python) [root@DL xbiquge]# vi xbiquge/items.py

    import scrapy
    class XbiqugeItem(scrapy.Item):
        # define the fields for your item here like:
        # name = scrapy.Field()
        name = scrapy.Field()
        url_firstchapter = scrapy.Field()
        name_txt = scrapy.Field()
        url = scrapy.Field()
        preview_page = scrapy.Field()
        next_page = scrapy.Field()
        content = scrapy.Field()
    

      

    2、pipelines.py修改如下:

    (python) [root@DL xbiquge]# vi xbiquge/pipelines.py

    import os
    import time
    import pymysql
    from twisted.enterprise import adbapi
    from pymysql import cursors
    
    class XbiqugePipeline(object):
        #定义类初始化动作,包括连接数据库novels及建表
        def __init__(self):
            dbparams = {
                'host':'127.0.0.1',
                'port':3306,
                'user':'root',
                'password':'password',
                'database':'novels',
                'charset':'utf8mb4'   #使用utf8mb4字符集可避免emoji表情符号无法存入数据表的错误,这是因为mysql的
    utf8只支持3个字节的存储,而一般字符是3个字节,但是emoji表情符号是4字节。
    
            }
            self.conn = pymysql.connect(**dbparams)
            self.cursor = self.conn.cursor()
            self._sql = None
            self.name_novel = ''
    
        #爬虫开始
        def open_spider(self, spider):
            #self.createtable()  #爬虫开始时先初始化小说存储表。(已放到爬虫程序中)
            return
    
        #建表
        def createtable(self,name_novel):
            self.cursor.execute("drop table if exists "+ name_novel)
            self.cursor.execute("create table " + name_novel + " (id int unsigned auto_increment not null primary key, url varchar(80) not null, preview_page varchar(80), next_page varchar(80), content TEXT not null) charset=utf8mb4")
            return
    
        def process_item(self, item, spider):
            if self.name_novel == '':                             #这里判断self.name_novel是否已由item传过来,若没有则需要更新变量。
                self.name_novel = item['name']
                self.url_firstchapter = item['url_firstchapter']
                self.name_txt = item['name_txt']
            self.cursor.execute(self.sql, (item['url'], item['preview_page'], item['next_page'], item['content']))
            self.conn.commit()
            return item
    
        @property
        def sql(self):
            if not self._sql:
                self._sql = """
                    insert into """ + self.name_novel + """(id, url, preview_page, next_page, content) values(null, %s, %s, %s, %s)
                    """
                return self._sql
            return self._sql
    
        #从数据库取小说章节内容写入txt文件
        def content2txt(self):
            self.cursor.execute("select count(*) from " + self.name_novel)
            record_num = self.cursor.fetchall()[0][0]
            print(record_num)
            counts=record_num
            url_c = """+self.url_firstchapter+"""
            start_time=time.time()  #获取提取小说内容程序运行的起始时间
            f = open(self.name_txt+".txt", mode='w', encoding='utf-8')   #写方式打开小说名称加txt组成的文件
            for i in range(counts):
                #print(i)
                sql_c = "select content from " + self.name_novel + " where url=" + url_c  #组合获取小说章节内容>的sql命令。此处需要修改数据库文件名称
                #print(sql_c)
                self.cursor.execute(sql_c)
                record_content_c2a0=self.cursor.fetchall()[0][0]  #获取小说章节内容
                record_content=record_content_c2a0.replace(u'xa0', u'')  #消除特殊字符xc2xa0
                f.write('
    ')
                f.write(record_content + '
    ')
                f.write('
    
    ')
                sql_n = "select next_page from " + self.name_novel + " where url=" + url_c   #组合获取下一章链接
    的sql命令。此处需要修改数据库文件名称
                self.cursor.execute(sql_n)
                url_c = """ + self.cursor.fetchall()[0][0] + """  #下一章链接地址赋值给url_c,准备下一次循环。
            f.close()
            print(time.time()-start_time)
            print(self.name_txt + ".txt" + " 文件已生成!")
            return
    
        #爬虫结束,调用content2txt方法,生成txt文件
        def close_spider(self,spider):
            self.content2txt()
            return
    

    3、爬虫文件修改:

    (python) [root@DL xbiquge]# vi xbiquge/spiders/sancun.py

    # -*- coding: utf-8 -*-
    import scrapy
    from xbiquge.items import XbiqugeItem
    from xbiquge.pipelines import XbiqugePipeline
    
    class SancunSpider(scrapy.Spider):
        name = 'sancun'
        allowed_domains = ['www.xbiquge.la']
        #start_urls = ['http://www.xbiquge.la/10/10489/']
        url_ori= "http://www.xbiquge.la"    
        url_firstchapter = "http://www.xbiquge.la/10/10489/4534454.html"   #小说的第一章url
        name_txt = "./novels/三寸人间"                                      #输出小说的目录及中文名
    
        pipeline=XbiqugePipeline()
        pipeline.createtable(name)    #创建数据表,以爬虫名称命名
        item = XbiqugeItem()
        item['name'] = name
        item['url_firstchapter'] = url_firstchapter
        item['name_txt'] = name_txt
    
        def start_requests(self):
            start_urls = ['http://www.xbiquge.la/10/10489/']
            for url in start_urls:
                yield scrapy.Request(url=url, callback=self.parse)
    
        def parse(self, response):
            dl = response.css('#list dl dd')     #提取章节链接相关信息
            for dd in dl:
                self.url_c = self.url_ori + dd.css('a::attr(href)').extract()[0]   #组合形成小说的各章节链接
                #print(self.url_c)
                #yield scrapy.Request(self.url_c, callback=self.parse_c,dont_filter=True)
                yield scrapy.Request(self.url_c, callback=self.parse_c)    #以生成器模式(yield)调用parse_c方法
    获得各章节链接、上一页链接、下一页链接和章节内容信息。
                #print(self.url_c)
        def parse_c(self, response):
            #item = XbiqugeItem()
            self.item['url'] = response.url
            self.item['preview_page'] = self.url_ori + response.css('div .bottem1 a::attr(href)').extract()[1]
            self.item['next_page'] = self.url_ori + response.css('div .bottem1 a::attr(href)').extract()[3]
            title = response.css('.con_top::text').extract()[4]
            contents = response.css('#content::text').extract()
            text=''
            for content in contents:
                text = text + content
            #print(text)
            self.item['content'] = title + "
    " + text.replace('15', '
    ')     #各章节标题和内容组合成content数
    据,15是^M的八进制表示,需要替换为换行符。
            yield self.item     #以生成器模式(yield)输出Item对象的内容给pipelines模块。
    

    上述代码修改后,新爬取小说时,只需要修改爬虫文件(如:sancun.py)中的相应参数即可。

      

  • 相关阅读:
    TransactSQL语言 学习sql server2005 step by step(四)
    一步一步学习C#(一)
    SQL实例进阶学习sql server2005 step by step(八)
    SQL Server中常用全局变量和函数 学习sql server2005 step by step(五)
    SQL实例进阶学习sql server2005 step by step(七)
    C#操作excel(开篇)
    SQL进阶提升(平时小积累)学习sql server2005 step by step(十)
    mysql 备份各种方法
    ubuntu 这可怕的弹出窗口啊“Enter password to unlock your login keyring”
    很简单的内核模块A+B
  • 原文地址:https://www.cnblogs.com/sfccl/p/13371549.html
Copyright © 2020-2023  润新知