Day 45 爬虫_scrapy框架_CrawlSpider的全站数据

CrawlSpider的全站数据

CrawlSpider:类,Spider的一个子类

全站数据爬去的方式

1、基于Spider:手动请求

2、基于CrawlSpider

CrawlSpider的使用:

1、创建工程:scrapy startproject XXX

2、cd XXX

3、创建爬虫文件(CrawlSpider)

  创建项目:scrapy genspider -t crawl xxx www.xxx.com

  链接提取器:根据指定的规则(allow)进行指定连接的提取

  规则解析器:将谅解提取到的链接进行指定规则(callback)的解析

需求:爬取 sun 网站中的编号,新闻标题,新闻内容,编号

分析:爬取的数据没有在同一张页面中

1、可以使用链接提取器提取所有的页码链接

2、让联机提取器提取所有的新闻详情页的链接

 

代码示例:

baiduPro.py

from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from baidu.items import BaiduItem,BaiduItem_detail

class BaiduproSpider(CrawlSpider):
    name = 'baiduPro'
    # allowed_domains = ['https://www.baidu.com/s?wd=wangyi']
    start_urls = ['http://wz.sun0769.com/political/index/politicsNewest']

    # 连接提取器:根据指定的(allow=“正则”)进行指定连接的提取
    Link = LinkExtractor(allow=r'id=\d+&page=\d+')
    Link_detail = LinkExtractor(allow=r'/political/politics/index\?id=\d+')
    rules = (
        # 规则解析器:将连接提取器提取到的链接进行指定规则(callback)的解析操作
        # follow=True:可以将链接提取器 继续作用到 连接提取器提取到的链接 所对应的页面中
        Rule(Link, callback='parse_item', follow=False),
        Rule(Link_detail, callback='detail_item')
    )

    # 以下两个解析方法无法实现请求传参,因为没有使用手动化请求
    # 也就是说以下两部分内容无法存储到同一个 item 中,可以存储到两个 item 中
    def parse_item(self, response):
        li_title = response.xpath('/html/body/div[2]/div[3]/ul[2]/li')

        for li in li_title:
            title_num = li.xpath('./span[@class="state1"]/text()').extract_first()
            title = li.xpath('.//a[@class="color-hover"]/text()').extract_first()


            item = BaiduItem()
            item['title_num'] = title_num
            item['title'] = title
            yield item

    def detail_item(self, response):
        print(response)
        detail_num = response.xpath('/html/body/div[3]/div[2]/div[2]/div[1]/span[4]/text()').extract_first()
        detail = response.xpath('/html/body/div[3]/div[2]/div[2]/div[2]/pre/text()').extract_first()
        detail_num = detail_num.split('')[1]
        detail = ''.join(detail)

        item = BaiduItem_detail()
        item['detail_num'] = detail_num
        item['detail'] = detail
        yield item

items.py

import scrapy


class BaiduItem(scrapy.Item):
    # define the fields for your item here like:
    title_num = scrapy.Field()
    title= scrapy.Field()


class BaiduItem_detail(scrapy.Item):
    detail_num = scrapy.Field()
    detail = scrapy.Field()

pipelines.py

import pymysql


class BaiduPipeline(object):
    def open_spider(self, spider):
        print('写入数据库。。。')
        self.conn = pymysql.Connect(host='192.168.214.23', port=3306, user='root', password='123456', db='srcapy',
                                    charset='utf8')

    def process_item(self, item, spider):
        # 如何判断 item 类型
        # 将数据写入数据库时,如何保证数据的一致性
        self.cursor = self.conn.cursor()
        if item.__class__.__name__ == 'BaiduItem':
            try:
                self.cursor.execute(
                    'insert into scr_sum(title_num,title) value ("%s","%s")' % (item['title_num'], item['title']))
                self.conn.commit()
            except Exception as e:
                print(e)
                self.conn.rollback()
            pass
        elif item.__class__.__name__ == 'BaiduItem_detail':
            try:
                self.cursor.execute(
                    'update scr_sum set detail_num = "%s",detail = "%s" where title_num = "%s"' % (
                        item['detail_num'], item['detail'], item['detail_num']))
                self.conn.commit()
            except Exception as e:
                print(e)
                self.conn.rollback()
            # print(type(item['detail']),item['detail'])
        return item

    def close_spider(self, spider):
        print('入库完成')
        self.cursor.close()
        self.conn.close()

settings.py

USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36 OPR/67.0.3575.137 (Edition B2)'

ROBOTSTXT_OBEY = False

LOG_LEVEL = 'ERROR'

ITEM_PIPELINES = {
   'baidu.pipelines.BaiduPipeline': 300,
}

 

分布式爬虫

概念:我们需要搭建一个分布式集群,让其对一组资源进行分布联合爬取

作用:提升爬取数据的效率

如何实现分布式

1、安装一个 scrapy-redis 的组件

2、原生的 scrapy 是不可以实现分布式爬虫,必须要让 scrapy 结合着scrapy-redis组件一起实现分布式爬虫

3、为什么原生的 scrapy 不可以实现分布式?

  1、调度器不可以被分布式集群共享

  2、管道你可以被分布式集群共享

scrapy-redis组件作用:可以给原生的 scrapy 框架提供可以被共享的管道和调度器

实现流程

1、创建一个工程:scrapy startproject fbs

2、创建一个基于 CrawlSpider 的爬虫文件:scrapy genspider -t crawl fbsPro www.xxx.com

3、修改当前爬虫文件

  1、导包:from scrapy_redis.spiders import RedisCrawlSpider

  2、将当前爬虫文件的父类修改为:RedisCrawlSpider

  3、将 allowed_domains 与 start_urls 两行注释掉

  4、添加一个新属性:redis_key = 'fbs'  可以被共享的调度器队列的名称

  5、编写数据解析操作

4、修改配置文件 settins

# 指定使用可以被共享的管道
ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 400
}
# 指定调度器
# 增加了一个去重容器类的配置, 作用使用Redis 的 set 集合来存储请求的指纹数据, 从而实现请求去重的持久化
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用 scrapy-redis 组件自己的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 配置调度器是否要持久化, 也就是当爬虫结束了, 要不要清空Redis 中请求队列和去重指纹的 set。如果是True, 就表示要持久化存储, 就不清空数据,否则清空数据
SCHEDULER_PERSIST = True

5、redis相关操作配置:

  1、配置redis的配置文件:
    linux或者mac:redis.conf
    windows:redis.windows.conf
  2、代开配置文件修改:
    将bind 127.0.0.1进行删除
    关闭保护模式:protected-mode yes改为no
  3、结合着配置文件开启redis服务
    redis-server 配置文件
  4、启动客户端:redis-cli

6、执行工程:scrapy runspider xxx.py

7、向调度器的队列中放入一个起始的url:
   调度器的队列在redis的客户端中:lpush xxx www.xxx.com
    
最后,爬取到的数据存储在了 redis 的 proName:items 这个数据结构中

 

增量式爬虫

概念:监测网站数据更新的情况,只会爬取网站最新更新出来的数据。
分析:
  1、指定一个起始url
  2、基于CrawlSpider获取其他页码链接
  3、基于Rule将其他页码链接进行请求
  4、从每一个页码对应的页面源码中解析出每一个电影详情页的URL

  5、核心:检测电影详情页的url之前有没有请求过
    将爬取过的电影详情页的url存储,存储到redis的set数据结构
    

  6、对详情页的url发起请求,然后解析出电影的名称和简介
  7、进行持久化存储

 

posted @ 2020-04-17 20:24  亦双弓  阅读(161)  评论(0编辑  收藏  举报