Python分布式爬虫打造搜索引擎
一、scrapy进阶开发
1、selenium相关学习:https://www.cnblogs.com/Eric15/p/9884309.html
2、selenium集成到scrapy中
下载中间件的process_request和process_response方法
①、process_request:每个交给下载器的request对象都会经过该方法,并期望返回response(每爬取一个url前,都会执行此方法)
②、 process_response:当下载器完成http请求,返回响应给引擎的时候调用
对一些动态网站,获取数据时采用selenium/phantomjs实现。集成selenium到scrapy框架中,只需要在middleware.py中新建中间件类,在其中实现process_request方法就可以了:
# middleware.py from selenium import webdriver from scrapy.http import HtmlResponse class JSPageMiddleware(object): #通过selenium - chrome请求动态网页 def process_request(self, request, spider): # 每次进行url请求时,都会调用此方法(前提setting中需配置) if spider.name == "jobbole": # 爬取伯乐时执行 # browser = webdriver.Chrome(executable_path="C:/chromedriver.exe") spider.browser.get(request.url) import time time.sleep(3) print ("访问:{0}".format(request.url)) # 返回HtmlResponse,可以结束当条爬虫对下载中间件中其他类的执行, return HtmlResponse(url=spider.browser.current_url, body=spider.browser.page_source, encoding="utf-8", request=request) #HtmlResponse中这几个参数都是必须的,固定写法 process_response(self): # 每条数据爬取完成后调用 pass
关于控制浏览器打开关闭,可在爬虫主文件处理。首先需引人dispatcher/sigals
dispatcher.connect:信号的映射
导入:from scrapy.xlib.pydispatch import dispatcher
signals:信号量,非常重要
导入:from scrapy import signals
使用:
from scrapy.xlib.pydispatch import dispatcher from scrapy import signals #使用selenium def __init__(self): # 会将driver封装进spider中 self.browser = webdriver.Chrome(executable_path="D:/Temp/chromedriver.exe") super(JobboleSpider, self).__init__() # 信号的映射,spider_closed:当爬虫结束时执行self.spider_closed方法 dispatcher.connect(self.spider_closed, signals.spider_closed) def spider_closed(self, spider): #当爬虫退出的时候关闭chrome print ("spider closed") self.browser.quit()
setting.py中配置:
DOWNLOADER_MIDDLEWARES = { 'ArticleSpider.middlewares.JSPageMiddleware': 1, # 'ArticleSpider.middlewares.RandomUserAgentMiddlware': 543, # 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None, }
需要注意的是:以上述方式运行爬虫相关,当执行到selenium相关操作部分时,便不再是异步操作而是同步的了。当然,也可以通过twisted集成实现异步操功能。
3、scrapy的暂停与重启
前提:进入爬虫目录
启动/重启爬虫:
scrapy crawl jobbole -s JOBDIR=jobs/001
JOBDIR=jobs/001:启动爬虫时,新建jobs/001目录,用于保存中间态信息;重启爬虫时,会读取001目录中相关信息,进行后续的数据爬取
暂停爬虫:ctrl + c ,按一次表示暂停,按两次表示强制结束。pycharm中没有此功能,一般在终端运行。
jobs/001目录:开始爬取数据后,001目录下会生成如下文件:
在requests.queue目录下也生成两个文件:
文件名 | 类型 | 用处 |
request.seen | 文件 | 已经访问过的url |
spider.state | 文件 | spider的状态信息 |
requests.queue | 文件夹 | request队列 |
requests.queue/p1 | 文件 | 需要继续做完的request |
注意:
- JOBDIR目录下主要是保存中间态信息
- 不同的spider不能共用同一个目录(另生成目录001、002、003等)
- 不同的run不能共用同一个目录
- 不能用
kill -9 main.py
命令强制结束爬虫 - 正常退出使用
ctrl+c
(只能按一次,按完需要一段时间处理中间状态)
4、scrapy的数据收集
document:https://scrapy-chs.readthedocs.io/zh_CN/latest/topics/stats.html
[以下是文档概述-scrapy数据收集]
数据收集(Stats Collection)
Scrapy提供了方便的收集数据的机制。数据以key/value方式存储,值大多是计数值。 该机制叫做数据收集器(Stats Collector),可以通过 Crawler API 的属性 stats
来使用。在下面的章节 常见数据收集器使用方法 将给出例子来说明。
无论数据收集(stats collection)开启或者关闭,数据收集器永远都是可用的。 因此您可以import进自己的模块并使用其API(增加值或者设置新的状态键(stat keys))。 该做法是为了简化数据收集的方法: 您不应该使用超过一行代码来收集您的spider,Scrpay扩展或任何您使用数据收集器代码里头的状态。
数据收集器的另一个特性是(在启用状态下)很高效,(在关闭情况下)非常高效(几乎察觉不到)。
数据收集器对每个spider保持一个状态表。当spider启动时,该表自动打开,当spider关闭时,自动关闭。
可用的数据收集器
除了基本的 StatsCollector
,Scrapy也提供了基于 StatsCollector
的数据收集器。 您可以通过 STATS_CLASS
设置来选择。默认使用的是 MemoryStatsCollector
常见数据收集器的使用方法(通过 stats
属性来使用数据收集器),具体请参考上述文档链接
demo:
爬取伯乐在线文章,截获404,将抓取到的404页面的次数添加到数据收集器中(stats)
在jobbole.py中编写代码:
#收集伯乐在线所有404的url以及404页面数 handle_httpstatus_list = [404]
scrapy中默认是会过滤掉有问题的HTTP response,除了状态码为200-300的请求及response会被处理外,其他状态码均会被默认过滤掉,不做处理。我们要截获404错误页面,通过response.status == 403判断是没有的,需要使用
handle_httpstatus_list=[404],此时能截获到404页面进行处理(200-300 、404页面给予处理),后面才能根据状态码来判断是否爬取的是404页面,然后才能将数据收集到数据收集器中。
实现每捕获一个404页面,就让数据收集器里的failed_url +1:
class JobboleSpider(scrapy.Spider): name = "jobbole" allowed_domains = ["blog.jobbole.com"] start_urls = ['http://blog.jobbole.com/all-posts/'] #收集伯乐在线所有404的url以及404页面数 handle_httpstatus_list = [404] def parse(self, response): # 捕获到404页面,stats中failed_url数据+1 if response.status == 404: self.fail_urls.append(response.url) self.crawler.stats.inc_value("failed_url") #stats是存在于crawler中的 ......
5、scrapy的信号
document:https://scrapy-chs.readthedocs.io/zh_CN/latest/topics/signals.html
二、scrapy-redis 分布式爬虫
1、scrapy-redis概述
1)scrapy本身是个通用的爬虫框架,但并不支持分布式爬虫模式。要使用分布式爬虫,则需要使用到scrapy-redis爬虫组件,scrapy-redis提供了一些以redis为基础的组件(仅有组件)。
2)分布式爬虫的优点:
- 可以充分利用多台机器的带宽
- 可以充分利用多台机器的ip地址
- 多台机器做,爬取效率更高
3)分布式爬虫必须要解决的问题:
- 分布式爬虫是好几台机器在同时运行,如何保证不同的机器爬取页面的时候不会出现重复爬取的问题?
- 同样,分布式爬虫在不同的机器上运行,在把数据爬取完成后如何保证保存在同一个地方?
2、scrapy-redis 安装
pip install scrapy-redis
3、redis相关操作参考:https://www.cnblogs.com/Eric15/articles/9459329.html
4、将scrapy项目变成scrapy-redis项目:
1)将爬虫继承的类:scrapy.Spider,变成继承:scrapy_redis.spiders.RedisSpider ; 如果继承的是scrapy.CrawlSpider类,则变成继承:scrapy_redis.spiders.RedisCrawlSpider。
2)将爬虫中的start_urls删掉/注释,增加redis_key="xxx"。作用:用于控制爬虫启动,爬虫的第一个url就是在redis中通过这个redis_key发送出去的
#demo: redis_key = "sina_guide:strat_urls"
3)在配置文件中增加以下配置:
# 使用scrapy-redis里的去重组件,不使用scrapy默认的去重方式 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用scrapy-redis里的调度器组件,不使用默认的调度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 允许暂停,redis请求记录不丢失 SCHEDULER_PERSIST = True # 默认的scrapy-redis请求队列形式(按优先级) SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue" # 队列形式,请求先进先出 #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue" # 栈形式,请求先进后出 #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack" # 只是将数据放到redis数据库,不需要写pipelines文件 ITEM_PIPELINES = { # 'Sina.pipelines.SinaPipeline': 300, 'scrapy_redis.pipelines.RedisPipeline': 400, } # LOG_LEVEL = 'DEBUG' # 日志打印等级 # Introduce an artifical delay to make use of parallelism. to speed up the # crawl. # DOWNLOAD_DELAY = 1 # 爬虫延迟时间 # 指定数据库的主机IP REDIS_HOST = "192.168.13.26" # 指定数据库的端口号 REDIS_PORT = 6379
运行爬虫时:
1)在爬虫服务器上,进入爬虫文件所在的路径,输入运行命令:scrapy runspider [爬虫文件名]↓
#demo: scrapy runspider sina.py
2)在Redis服务器上,推入一个开始的url链接:redis-cli(客户端进入);推入链接:lpush [redis_key] start_url ,开始爬取↓
#demo: redis-cli> lpush sina_guide:start_urls http://news.sina.com.cn/guide/ # sina_guide:start_urls 需与上面的start_urls 保持一致