Scrapy爬取全站数据并存储到数据库和文件中

  • scrapy五大核心组件简介

    • 引擎(Scrapy)

      • 用来处理整个系统的数据流处理, 触发事务(框架核心)

    • 调度器(Scheduler)

      • 用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
    • 下载器(Downloader)

      • 用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
    • 爬虫(Spiders)

      • 爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
    • 项目管道(Pipeline)

      • 负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据

爬取全站数据

大部分的网站展示的数据都进行了分页操作,那么将所有页码对应的页面数据进行爬取就是爬虫中的全站数据爬取。

基于scrapy如何进行全站数据爬取呢?

使用Request方法手动发起请求。

需求:将糗事百科所有页码的作者和段子内容数据进行爬取切持久化存储

spider文件:

class QiubaiSpider(scrapy.Spider):
    name = 'qiubai'  # 应用名称(唯一标识)
    # 允许爬取的域名(如果遇到非该域名的url则爬取不到数据)
    allowed_domains = ['https://www.qiushibaike.com/']
    # 起始爬取的url
    start_urls = ['https://www.qiushibaike.com/text']

    # 爬取多页
    pageNum = 1  # 起始页码
    # 统用的url模板(不可变)
    url = 'https://www.qiushibaike.com/text/page/%s/'  # 每页的url

    # 访问起始URL并获取结果后的回调函数,该函数的response参数就是向起始的url发送请求后,获取的响应对象.该函数返回值必须为可迭代对象或者NUll
    def parse(self, response):
        # print(response.text)  # 获取字符串类型的响应内容
        # 获取作者名称和内容
        # print(response.body)  # 获取字节类型的相应内容
        # xpath为response中的方法,可以将xpath表达式直接作用于该函数中
        odiv = response.xpath('//div[@class="col1 old-style-col1"]/div')
        # print(len(odiv))
        content_list = []  # 用于存储解析到的数据
        for div_item in odiv:
            # xpath函数返回的为列表,列表中存放的数据为Selector类型的数据。
            # 我们解析到的内容被封装在了Selector对象中,需要调用extract()函数将解析的内容从Selecor中取出。
            author = div_item.xpath('.//div[1]/a[2]/h2/text() | .//div[1]/span/h2/text()')[0].extract()
            content = div_item.xpath('.//div[@class="content"]/span/text()').extract()
            content = ''.join(content)  # 列表转换为字符串
            # 打印展示爬取到的数据
            # print(author, content)
            # print(content)
            author = author.strip('\n')  # 过滤空行
            content = content.strip('\n')
            # 将解析到的数据封装至items对象中
            item = FirstbloodItem()
            item['author'] = author
            item['content'] = content
            print(author)

            yield item  # 提交item到管道文件(pipelines.py)

        # 爬取所有页码数据
        print('pageNum={}'.format(self.pageNum))
        if self.pageNum < 5:  # 一共爬取13页(共13页)
            self.pageNum += 1
            new_url = format(self.url % self.pageNum)
            print(new_url)
            # 递归爬取数据:callback参数的值为回调函数(将url请求后,得到的相应数据继续进行parse解析),递归调用parse函数
            # 在 scrapy.Request() 函数中将参数 dont_filter=True 设置为 True,使 requests 不被过滤:
            yield scrapy.http.Request(url=new_url, callback=self.parse, dont_filter=True)

items.py文件

import scrapy


class FirstbloodItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    author = scrapy.Field()
    content = scrapy.Field()
    pass

pipeline文件

import pymysql
class FirstbloodPipeline(object):
    # 构造方法
    def __init__(self):
        self.fp = None  # 定义一个文件描述符属性

    # 下列都是在重写父类的方法:
    # 开始爬虫时,执行一次
    def open_spider(self, spider):
        print('爬虫开始')
        self.fp = open('./data.txt', 'w',encoding='utf-8')
    # 专门处理item对象
    # 因为该方法会被执行调用多次,所以文件的开启和关闭操作写在了另外两个只会各自执行一次的方法中。
    def process_item(self, item, spider):
        author = item['author']
        content = item['content']
        contents = '{}:{}\n\n'.format(author,content)
        self.fp.write(contents)
        return item  # 传递到下一个被执行的管道类

    def close_spider(self, spider):
        print('爬虫结束')
        self.fp.close()

#管道文件中的一个管道类对应的是讲数据存储到一种平台
#爬虫文件提交的item只会给管道文件中第一个被执行的管道类接受
#process_item的return item表示item传递到下一个被执行的管道类
#将数据存储到数据库
class mysqlPipeline(object):
    # 构造方法
    def __init__(self):
        self.conn = None  # 定义一个文件描述符属性
        self.cursor = None
        self.num = 0

    # 下列都是在重写父类的方法:
    # 开始爬虫时,执行一次
    def open_spider(self, spider):
        self.conn = pymysql.Connect(host='192.168.31.xx',port=3306,user='root',password='111',db='xxx_db',charset='utf8')
        print('爬虫数据库开始')

    # 专门处理item对象
    # 因为该方法会被执行调用多次,所以文件的开启和关闭操作写在了另外两个只会各自执行一次的方法中。
    def process_item(self, item, spider):
        author = item['author']
        content = item['content']
        self.cursor = self.conn.cursor()
        try:

            self.cursor.execute('insert into qiubai values(%s,%s)' ,(author,content))
            self.conn.commit()
        except Exception as e:
            print(e)
            self.conn.rollback()
        return item

    def close_spider(self, spider):
        print('爬虫数据库结束')
        self.cursor.close()
        self.conn.close()

setting

USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'  # 伪装请求载体身份
# Obey robots.txt rules
# ROBOTSTXT_OBEY = True
ROBOTSTXT_OBEY = False  #可以忽略或者不遵守robots协议
#只显示指定类型的日志信息
LOG_LEVEL='ERROR'
# Configure maximum concurrent requests performed by Scrapy (default: 16)
#CONCURRENT_REQUESTS = 32



# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
   'firstBlood.pipelines.FirstbloodPipeline': 300,
    'firstBlood.pipelines.mysqlPipeline': 320,

    #300表示优先级,越小越高
}
posted @ 2020-04-02 00:23  一只小小的寄居蟹  阅读(3501)  评论(0编辑  收藏  举报