爬虫分为广度爬虫和深度爬虫。
广度爬虫是使用队列来存放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地址过多的情况下,这个爬虫运行效率会非常的低下。为了解决这个问题,我们要使用分布式爬虫。
我们可以通过修改爬虫工程的settings文件,爬虫传给pipelines管道文件的数据存入Redis数据库,然后通过Redis的频道订阅功能将要爬取的url地址发布给其它机器,加快爬虫的爬取速度,同时,使爬虫具有断电续传的功能。
这样,我们可以让一个爬虫爬取url,同时多个爬虫解析页面,这就是分布式爬虫。
我们安装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)
我们只需对爬虫文件进行修改即可,
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