python爬虫之Scrapy框架(三)

Scrapy框架

1.爬虫队列

爬虫分为广度爬虫和深度爬虫。

广度爬虫是使用队列来存放url地址。其会在我们将一个地址传给他时,将地址存入队列,然后取出先放入的url地址,对url地址进行解析,将解析到的url地址再放入队列,这样无限循环下去,直到队列中没有url地址。

我们来看下面的一个爬虫,

from queue import Queue
import requests
import lxml.html


class DownloadItem:
    """
    下载url对象
    """
    def __init__(self, url_str, url_type):
        """
        初始化函数
        :param url_str: 网址
        :param url_type: 网址类型 0 首页 1 详情页
        """
        self.url = url_str
        self.type = url_type


download_queue = Queue()

seed_item = DownloadItem("https://tieba.baidu.com/f?fr=ala0&kw=python&tpl=5", 0)
download_queue.put(seed_item)

while not download_queue.empty():
    # 从队列中取数据
    download_item = download_queue.get()
    if download_item.type == 0:
        # 访问节点
        result = requests.get(download_item.url)
        # 解析
        parser = lxml.html.fromstring(result.text)

        posts = parser.xpath('//ul[@id="thread_list"]/li[@class=" j_thread_list clearfix"]')
        for post in posts:
            url_suffix = post.xpath('.//a[@class="j_th_tit "]/@href')
            url_complete = "https://tieba.baidu.com" + url_suffix[0]
            detail_item = DownloadItem(url_complete, 1)
            download_queue.put(detail_item)
    else:
        result_detail = requests.get(download_item.url)
        print(result_detail.text)

我们可以看到,这个爬虫实现了爬取url并解析的功能。

但是,如果url地址过多的情况下,这个爬虫运行效率会非常的低下。为了解决这个问题,我们要使用分布式爬虫。

2.分布式爬虫

我们可以通过修改爬虫工程的settings文件,爬虫传给pipelines管道文件的数据存入Redis数据库,然后通过Redis的频道订阅功能将要爬取的url地址发布给其它机器,加快爬虫的爬取速度,同时,使爬虫具有断电续传的功能。

这样,我们可以让一个爬虫爬取url,同时多个爬虫解析页面,这就是分布式爬虫。

2.1.单机版分布式爬虫

我们安装scrapy-redis库,

pip install scrapy-redis

我们需要将原始settings文件中的DOWNLOAD_DELAY参数之后的全部代码删除,然后将以下代码添加,

DOWNLOAD_DELAY = 5
# The download delay setting will honor only one of:

# 指定使用scrapy-redis的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"

# 指定使用scrapy-redis的去重
DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'
# 指定排序爬取地址时使用的队列,
# 默认的 按优先级排序(Scrapy默认),由sorted set实现的一种非FIFO、LIFO方式。
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderQueue'
# 可选的 按先进先出排序(FIFO)
# SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderQueue'
# 可选的 按后进先出排序(LIFO)
# SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderStack'
# 在redis中保持scrapy-redis用到的各个队列,从而允许暂停和暂停后恢复,也就是不清理redis queues
SCHEDULER_PERSIST = True
# 只在使用SpiderQueue或者SpiderStack是有效的参数,指定爬虫关闭的最大间隔时间
# SCHEDULER_IDLE_BEFORE_CLOSE = 10

# 通过配置RedisPipeline将item写入key为 spider.name : items 的redis的list中,供后面的分布式处理item
# 这个已经由 scrapy-redis 实现,不需要我们写代码
ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 400
}

# DOWNLOADER_MIDDLEWARES = {
#     'distribute_country.middlewares.SeleniumMiddleware': 543,
#
# }
#

# 指定redis数据库的连接参数
# REDIS_PASS是我自己加上的redis连接密码(默认不做)
REDIS_HOST = '127.0.0.1'
REDIS_PORT = 6379
# REDIS_PASS = 'redisP@ssw0rd'

# LOG等级
LOG_LEVEL = 'DEBUG'

# 默认情况下,RFPDupeFilter只记录第一个重复请求。将DUPEFILTER_DEBUG设置为True会记录所有重复的请求。
DUPEFILTER_DEBUG =True

items文件代码如下,

import scrapy


class TiebaItem(scrapy.Item):
    url = scrapy.Field()

爬虫文件代码如下,

import scrapy
from ..items import TiebaItem
from scrapy import Request


class TiebaComSpider(scrapy.Spider):
    name = 'tieba'
    allowed_domains = ['tieba.baidu.com']
    start_urls = ['https://tieba.baidu.com/f?fr=ala0&kw=python&tpl=5']

    def parse(self, response):
        posts = response.xpath('//ul[@id="thread_list"]/li[@class=" j_thread_list clearfix"]')
        for post in posts:
            item = TiebaItem()
            item['url'] = post.xpath('.//a[@class="j_th_tit "]/@href').extract_first()
            url_complete = "https://tieba.baidu.com" + item['url']
            yield Request(url=url_complete, callback=self.parse_detail)

    def parse_detail(self, response):
        print(response.text)

2.2.联机版爬虫

我们只需对爬虫文件进行修改即可,

import scrapy
from ..items import TiebaItem
from scrapy import Request
# 使用scrapy_redis中的redisspider来实现spider,使其具有redis的交互能力
from scrapy_redis.spiders import RedisSpider


class TiebaComSpider(RedisSpider):
    name = 'tieba'
    allowed_domains = ['tieba.baidu.com']
    """
    redis_key用来接收初始的种子地址,在redis中,使用:
    lpush tieba:starturl 种子地址
    的命令启动爬虫
    """
    redis_key = "tieba:starturl"

    def parse(self, response):
        posts = response.xpath('//ul[@id="thread_list"]/li[@class=" j_thread_list clearfix"]')
        for post in posts:
            item = TiebaItem()
            item['url'] = post.xpath('.//a[@class="j_th_tit "]/@href').extract_first()
            url_complete = "https://tieba.baidu.com" + item['url']
            yield Request(url=url_complete, callback=self.parse_detail)

    def parse_detail(self, response):
        print(response.text)

这样,我们如果运行爬虫时,可以看到,爬虫显示正在等待redis_key。此时,我们打开Redis数据库,使用lpush tieba:starturl 种子地址即可使爬虫开始工作。例如,我们让爬虫开始爬取python吧的内容,

lpush tieba:starturl https://tieba.baidu.com/f?fr=ala0&kw=python&tpl=5

你可能感兴趣的:(python爬虫之Scrapy框架(三))