scrapy的概念

Scrapy是一个Python编写的开源网络爬虫框架。它是一个被设计用于爬取网络数据、提取结构性数据的框架。同时能够以少量的代码帮助我们快速的抓取数据

scrapy的流程

在这里插入图片描述

  1. 爬虫中起始的url构造成request对象–>爬虫中间件–>引擎–>调度器
  2. 调度器把request–>引擎–>下载中间件—>下载器
  3. 下载器发送请求,获取response响应---->下载中间件---->引擎—>爬虫中间件—>爬虫
  4. 爬虫提取url地址,组装成request对象---->爬虫中间件—>引擎—>调度器,重复步骤2
  5. 爬虫提取数据—>引擎—>管道处理和保存数据

scrapy的三个内置对象

  • request请求对象:由url method post_data headers等构成
  • response响应对象:由url body status headers等构成
  • item数据对象:本质是个字典

scrapy中每个模块的具体作用

在这里插入图片描述

  • 引擎(engine): 负责数据和信号在不同模块间的传递
  • 调度器(scheduler): 实现一个队列,存放引擎发过来的request请求对象
  • 下载器(downloader): 发送引擎发过来的request请求,获取响应,并将响应交给引擎
  • 爬虫(spider): 处理引擎发过来的response,提取数据,提取url,并交给引擎
  • 管道(pipeline): 处理引擎传递过来的数据,比如存储
  • 下载中间件(downloader middleware): 可以自定义的下载扩展,比如设置代理ip。

默认方法的作用:
process_request(self, request, spider):

  1. 当每个request通过下载中间件时,该方法被调用。
  2. 返回None值:没有return也是返回None,该request对象传递给下载器,或通过引擎传递给其他权重低的proces当下载器完成http请求,传递响应给引擎的时候调用
  3. 返回Resposne:通过引擎交给爬虫处理或交给权重更低的其他下载中间件的process_response方法
  4. 返回Request对象:通过引擎交给调取器继续请求,此时将不通过其他权重低的process_request方法s_request方法
  5. 返回Response对象:不再请求,把response返回给引擎
  6. 返回Request对象:把request对象通过引擎交给调度器,此时将不通过其他权重低的process_request方法

使用IP代理:

class ProxyMiddleware(object):
	def process_request(self,request,spider):
		# proxies可以在settings.py中,也可以来源于代理ip的webapi 
		proxy = random.choice(proxies)
		# 免费的会失效,报 111 connection refused 信息!重找一个代理ip再试
		proxy = 'https://1.71.188.37:3128'
		request.meta['proxy'] = proxy
		return None # 可以不写return

process_response(self, request, response, spider):
在配置文件中启用中间件,权重值越小越优先启用。

  • 爬虫中间件(spider middleware): 可以自定义request请求和进行response过滤,与下载中间件作用重复

安装scrapy

yum install scrapy -y
或者pip/pip3 install scrapy

scrapy项目开发流程

创建一个项目

scrapy startproject Yourprojectname

生成一个爬虫

scrapy genspider yourspidername yourdomain

yourspidername: 作为爬虫运行时的参数
yourdomain: 为对于爬虫设置的爬取范围,设置之后用于过滤要爬取的url,如果爬取的url与允许的域不通则被过滤掉。

完善爬虫

在/Yourprojectname/yourspidername /spiders/yourspidername.py中修改内容如下:

import scrapy

class ItcastSpider(scrapy.Spider):  # 继承scrapy.spider
	# 爬虫名字 
    name = 'itcast' 
    # 允许爬取的范围
    allowed_domains = ['itcast.cn'] 
    # 开始爬取的url地址
    start_urls = ['http://www.itcast.cn/channel/teacher.shtml']
    
    # 数据提取的方法,接收下载中间件传过来的response
    def parse(self, response): 
    	# scrapy的response对象可以直接进行xpath
    	names = response.xpath('//div[@class="tea_con"]//li/div/h3/text()') 
    	print(names)

    	# 获取具体数据文本的方式如下
        # 分组
    	li_list = response.xpath('//div[@class="tea_con"]//li') 
        for li in li_list:
        	# 创建一个数据字典
            item = {}
            # 利用scrapy封装好的xpath选择器定位元素,并通过extract()或extract_first()来获取结果
            item['name'] = li.xpath('.//h3/text()').extract_first() # 老师的名字
            item['level'] = li.xpath('.//h4/text()').extract_first() # 老师的级别
            item['text'] = li.xpath('.//p/text()').extract_first() # 老师的介绍
            print(item)

注意:

  • scrapy.Spider爬虫类中必须有名为parse的解析
  • 如果网站结构层次比较复杂,也可以自定义其他解析函数
  • 在解析函数中提取的url地址如果要发送请求,则必须属于allowed_domains范围内,但是start_urls中的url地址不受这个限制,我们会在后续的课程中学习如何在解析函数中构造发送请求
  • 启动爬虫的时候注意启动的位置,是在项目路径下启动
  • parse()函数中使用yield返回数据,注意:解析函数中的yield能够传递的对象只能是:BaseItem, Request, dict, None

一些API的作用

  1. response.xpath方法的返回结果是一个类似list的类型,其中包含的是selector对象,操作和列表一样,但是有一些额外的方法
  2. 额外方法extract():返回一个包含有字符串的列表
  3. 额外方法extract_first():返回列表中的第一个字符串,列表为空没有返回None

response响应对象的常用属性

  • response.url:当前响应的url地址
  • response.request.url:当前响应对应的请求的url地址
  • response.headers:响应头
  • response.requests.headers:当前响应的请求头
  • response.body:响应体,也就是html代码,byte类型
  • response.status:响应状态码

pipeline保存数据

管道能够实现数据的清洗和保存,能够定义多个管道实现不同的功能,使用管道pipeline来处理(保存数据),在pipelines.py文件中定义一个管道类,重写管道类的process_item方法,process_item方法处理完item之后必须返回给引擎。

import json

class JDPipeline():
    
    # 在爬虫开启的时候仅执行一次(想起了flask中的请求钩子)
    def open_spider(self, spider):
    	pass
    # 在爬虫关闭的时候仅执行一次
    def close_spider(self, spider):
    	pass
    # 爬虫文件中提取数据的方法每yield一次item,就会运行一次
    # 该方法为固定名称函数,必须要用的函数
    def process_item(self, item, spider):
        # process_item的方法必须return item,否则后一个pipeline取到的数据为None值
        return item

在settings.py配置启用管道:(可以开启多个管道)

ITEM_PIPELINES = {
    'myspider.pipelines.ItcastPipeline': 400 #配置项中值为管道的使用顺序,设置的数值约小越优先执行,该值一般设置为1000以内。
}

运行scrapy

在项目目录下执行scrapy crawl yourspidername

数据建模与请求

建模的好处有如下几点:

  1. 定义item即提前规划好哪些字段需要抓,防止手误,因为定义好之后,在运行过程中,系统会自动检查
  2. 配合注释一起可以清晰的知道要抓取哪些字段,没有定义的字段不能抓取,在目标字段少的时候可以使用字典代替
  3. 使用scrapy的一些特定组件需要Item做支持,如scrapy的ImagesPipeline管道类,百度搜索了解更多

在items.py文件中定义要提取的字段:

class MyspiderItem(scrapy.Item): 
    name = scrapy.Field()
    title = scrapy.Field()
    desc = scrapy.Field()

使用模板类:

from myspider.items import MyspiderItem   # 导入Item,注意路径
    def parse(self, response)

        item = MyspiderItem() # 实例化后可直接使用
        item['name'] = node.xpath('./h3/text()').extract_first()
        item['title'] = node.xpath('./h4/text()').extract_first()
        item['desc'] = node.xpath('./p/text()').extract_first()
        print(item)

实现翻页

requests模块实现翻页请求:

  1. 找到下一页的URL地址
  2. 调用requests.get(url)

scrapy实现翻页的思路:

  1. 找到下一页的url地址
  2. 构造url地址的请求对象,传递给引擎

实现方法:
3. 确定url地址
4. 构造请求,scrapy.Request(url,callback)

  • callback:指定解析函数名称,表示该请求返回的响应使用哪一个函数进行解析
  1. 把请求交给引擎:yield scrapy.Request(url,callback)
    在这里插入图片描述
    scrapy.Request的更多参数解释:
scrapy.Request(url[,callback,method="GET",headers,body,cookies,meta,dont_filter=False])
  1. 中括号里的参数为可选参数
  2. callback:表示当前的url的响应交给哪个函数去处理
  3. meta:实现数据在不同的解析函数中传递,meta默认带有部分数据,比如下载延迟,请求深度等
  4. dont_filter:默认为False,会过滤请求的url地址,即请求过的url地址不会继续被请求,对需要重复请求的url地址可以把它设置为Ture,比如贴吧的翻页请求,页面的数据总是在变化;start_urls中的地址会被反复请求,否则程序不会启动
  5. method:指定POST或GET请求
  6. headers:接收一个字典,其中不包括cookies
  7. cookies:接收一个字典,专门放置cookies
  8. body:接收json字符串,为POST的数据,发送payload_post请求时使用(在下一章节中会介绍post请求)

meta的作用:meta可以实现数据在不同的解析函数中的传递(把当前页面抓取到的数据传递给下一个parse函数进行进一步处理)

注意:

  1. meta参数是一个字典
  2. meta字典中有一个固定的键proxy,表示代理ip

分布式爬虫scrapy-redis

三个类:

RFPDupeFilter:实现了对request对象的加密
RedisPipeline:进行数据的保存,存入了redis中
Scheduler:实现了决定什么时候把request对象加入带抓取的队列,同时把请求过的request对象过滤掉

ettings.py中关键的配置“

DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
SCHEDULER_PERSIST = True

ITEM_PIPELINES = {
    'example.pipelines.ExamplePipeline': 300,
    'scrapy_redis.pipelines.RedisPipeline': 400,
}
REDIS_URL = "redis://127.0.0.1:6379"

scrapy_redis的流程

  • 在scrapy_redis中,所有的待抓取的request对象和去重的request对象指纹都存在所有的服务器公用的redis中

  • 所有的服务器中的scrapy进程公用同一个redis中的request对象的队列

  • 所有的request对象存入redis前,都会通过该redis中的request指纹集合进行判断,之前是否已经存入过

  • 在默认情况下所有的数据会保存在redis中

使用:

爬虫文件:

import scrapy
from scrapy_redis.spiders import RedisSpider


class RdsDoubSpider(RedisSpider):
    """分布式爬虫"""
    name = 'rds_doub'
    redis_key = 'rds_douban_url'

    # allowed_domains = ['douban.com']
    # start_urls = ['http://douban.com/']
    def __init__(self, *args, **kwargs):
        # Dynamically define the allowed domains list.
        domain = kwargs.pop('domain', '')
        self.allowed_domains = list(filter(None, domain.split(',')))
        super(RdsDoubSpider, self).__init__(*args, **kwargs)

    def parse(self, response):
        node_list = response.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]')

        for data in node_list:
            item = {}
            item['name'] = data.xpath('./div/a/span[1]/text()').extract_first()
            item['actor'] = data.xpath('./div/p[1]/text()').extract_first().strip()
            item['score'] = data.xpath('./div/div/span[@class="rating_num"]/text()').extract_first()
            item['desc'] = data.xpath('./div/p/span/text()').extract_first()
            yield item

        # # 获取翻页url
        part_url = response.xpath('//*[@id="content"]/div/div[1]/div[2]/span[3]/a/@href').extract_first()

        if part_url is not None:
            url = response.urljoin(part_url)
            # 构建请求返回
            # callbock:当翻页的页面结构不发生改变依旧可以解析可以写当前的parse方法
            yield scrapy.Request(url=url,
                                 callback=self.parse)

启动方式

scrapy crawl spider 

往redis中添加起始URL

127.0.0.1:6379> lpush rds_douban_url https://xxx.com250

scrapy_splash

scrapy_splash是scrapy的一个组件,是一个Javascript渲染服务。它实现了HTTP API的轻量级浏览器,Splash是用Python和Lua语言实现的,基于Twisted和QT等模块构建。使用scrapy-splash最终拿到的response相当于是在浏览器全部渲染完成以后的网页源代码。

环境安装:

docker pull scrapinghub/splash
# 运行镜像成为一个容器
docker run -d -p 8050:8050 scrapinghub/splash

安装splash

pip install scrapy-splash

使用splash

import scrapy
from scrapy_splash import SplashRequest # 使用scrapy_splash包提供的request对象

class WithSplashSpider(scrapy.Spider):
    name = 'with_splash'
    allowed_domains = ['baidu.com']
    start_urls = ['https://www.baidu.com/s?wd=13161933309']

    def start_requests(self):
        yield SplashRequest(self.start_urls[0],
                            callback=self.parse_splash,
                            args={'wait': 10}, # 最大超时时间,单位:秒
                            endpoint='render.html') # 使用splash服务的固定参数

    def parse_splash(self, response):
        with open('with_splash.html', 'w') as f:
            f.write(response.body.decode())

settings.py

SPLASH_URL = 'http://127.0.0.1:8050'
DOWNLOADER_MIDDLEWARES = {
    'scrapy_splash.SplashCookiesMiddleware': 723,
    'scrapy_splash.SplashMiddleware': 725,
    'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}
DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'

scrapy_redis和scrapy_splash配合使用的配置

# 渲染服务的url
SPLASH_URL = 'http://127.0.0.1:8050'
# 下载器中间件
DOWNLOADER_MIDDLEWARES = {
    'scrapy_splash.SplashCookiesMiddleware': 723,
    'scrapy_splash.SplashMiddleware': 725,
    'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}
# 使用Splash的Http缓存
HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'

# 去重过滤器
# DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
# DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 指纹生成以及去重类
DUPEFILTER_CLASS = 'test_splash.spiders.splash_and_redis.SplashAwareDupeFilter' # 混合去重类的位置

SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 调度器类
SCHEDULER_PERSIST = True # 持久化请求队列和指纹集合, scrapy_redis和scrapy_splash混用使用splash的DupeFilter!
ITEM_PIPELINES = {'scrapy_redis.pipelines.RedisPipeline': 400} # 数据存入redis的管道
REDIS_URL = "redis://127.0.0.1:6379" # redis的url

部署scrapy项目

scrapyd介绍

scrapyd是一个用于部署和运行scrapy爬虫的程序,它允许你通过JSON API来部署爬虫项目和控制爬虫运行,scrapyd是一个守护进程,监听爬虫的运行和请求.

scrapyd的安装

scrapyd服务:(在服务器安装)
pip install scrapyd

scrapyd客户端:(在客户端安装)
pip install scrapyd-client

scrapyd的配置文件

# [root@scrapy gerapy]# vim /etc/scrapyd/scrapyd.conf
[scrapyd]
eggs_dir    = eggs
logs_dir    = logs
items_dir   =
jobs_to_keep = 5
dbs_dir     = dbs
max_proc    = 0
max_proc_per_cpu = 4
finished_to_keep = 100
poll_interval = 5.0
bind_address = 127.0.0.1
http_port   = 6800
debug       = off
runner      = scrapyd.runner
application = scrapyd.app.application
launcher    = scrapyd.launcher.Launcher
webroot     = scrapyd.website.Root

[services]
schedule.json     = scrapyd.webservice.Schedule
cancel.json       = scrapyd.webservice.Cancel
addversion.json   = scrapyd.webservice.AddVersion
listprojects.json = scrapyd.webservice.ListProjects
listversions.json = scrapyd.webservice.ListVersions
listspiders.json  = scrapyd.webservice.ListSpiders
delproject.json   = scrapyd.webservice.DeleteProject
delversion.json   = scrapyd.webservice.DeleteVersion
listjobs.json     = scrapyd.webservice.ListJobs
daemonstatus.json = scrapyd.webservice.DaemonStatus

启动scrapyd服务

  1. 在scrapy项目路径下 启动scrapyd的命令:sudo scrapydscrapyd

  2. 启动之后就可以打开本地运行的scrapyd,浏览器中访问本地6800端口可以查看scrapyd的监控界面

部署爬虫项目到scrapyd

编辑scrapy.cfg文件:

在这里插入图片描述

在这里插入图片描述
注意 -p选项的名称可随意,如果部署到服务器(gerapy)报错,修改代码重新部署

管理scrapy项目
启动项目:
curl http://localhost:6800/schedule.json -d project=project_name -d spider=spider_name
在这里插入图片描述

关闭项目:
curl http://localhost:6800/cancel.json -d project=project_name -d job=jobid

使用requests模块控制scrapy项目

import requests

# 启动爬虫
url = 'http://localhost:6800/schedule.json'
data = {
	'project': 项目名,
	'spider': 爬虫名,
}
resp = requests.post(url, data=data)

# 停止爬虫
url = 'http://localhost:6800/cancel.json'
data = {
	'project': 项目名,
	'job': 启动爬虫时返回的jobid,
}
resp = requests.post(url, data=data)

小结

  1. 在scrapy项目路径下执行sudo scrapydscrapyd,启动scrapyd服务;或以后台进程方式启动nohup scrapyd > scrapyd.log 2>&1 &
  2. 部署scrapy爬虫项目scrapyd-deploy -p myspider
  3. 启动爬虫项目中的一个爬虫`curl http://localhost:6800/schedule.json -d project=myspider -d spider=tencent

使用gerapy管理爬虫项目

Gerapy 是一款 分布式爬虫管理框架,支持 Python 3,基于 Scrapy、Scrapyd、Scrapyd-Client、Scrapy-Redis、Scrapyd-API、Scrapy-Splash、Jinjia2、Django、Vue.js 开发,Gerapy 可以帮助我们:

  1. 更方便地控制爬虫运行
  2. 更直观地查看爬虫状态
  3. 更实时地查看爬取结果
  4. 更简单地实现项目部署
  5. 更统一地实现主机管理

Gerapy的安装:

pip3 install gerapy

验证是否安装成功

在这里插入图片描述

初始化一个项目:

gerapy init
# 进入gerapy项目对数据库进行迁移
[root@scrapy gerapy]# gerapy migrate

在这里插入图片描述
创建一个超级用户

在这里插入图片描述
启动服务:

在这里插入图片描述
创建主机
在这里插入图片描述
项目部署:

只需把项目文件夹上传至gerapy下的project目录下即可。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐