scrapy爬虫框架

scrapy爬虫框架

  • 一、scrapy的概念作用和工作流程
    • 1、scrapy的概念
    • 2、scrapy框架的作用
    • 3、scrapy的工作流程(重点)
      • 3.1 回顾之前的爬虫流程
      • 3.2 改写上述流程
      • 3.3 scrapy的流程
      • 3.4 scrapy的三个内置对象
      • 3.5 scrapy中每个模块的具体作用
  • 二、scrapy的入门使用
    • 1、安装scrapy
    • 2、scrapy项目开发流程
    • 3、创建项目
    • 4、创建爬虫
    • 5、完善爬虫
      • 5.1 在/spiders/itcast.py中修改内容
      • 5.2 定位元素以及提取数据、属性值的方法
      • 5.3 response响应对象的常用属性
    • 6、保存数据
      • 6.1 在pipelines.py文件中定义对数据的操作
      • 6.2 在settings.py配置启用管道
    • 7、运行scrapy
  • 三、scrapy数据建模与请求
    • 1、数据建模
      • 1.1 为什么要建模
      • 1.2 如何建模
      • 1.3 如何使用模板类
    • 2、翻页请求的思路
    • 3、构造Request对象,并发送请求
      • 3.1 实现方法
      • 3.2 网易招聘爬虫案例
      • 3.3 代码实现
      • 3.4 scrapy.Request的更多参数
    • 4、meta参数的使用
  • 四、scrapy模拟登录
    • 1、模拟登录的方法
      • 1.1 requests的模拟登录
      • 1.2 selenium的模拟登录
      • 1.3 scrapy的模拟登录
    • 2、scrapy携带cookies直接获取需要登录后的页面
      • 2.1 实现:重构scrapy的start_requests方法
      • 2.2 携带cookies登录GitHub
    • 3、scrapy.Request发送post请求
      • 3.1 发送post请求
        • 3.1.1 思路分析
        • 3.1.1 代码实现
  • 五、scrapy管道的使用
    • 1、pipeline 中常用的方法
    • 2、管道文件的修改
    • 3、开启管道
    • 4、pipeline使用注意点
  • 六、scrapy中间件的使用
    • 1、scrapy中间件的分类和作用
      • 1.1 scrapy中间件的分类
      • 1.2 scrapy中间件的作用
    • 2、下载中间件的使用方法
    • 3、定义实现随机User-Agent的下载中间件
      • 3.1 在settings.py中添加UA列表
      • 3.2 在middleware.py中完善代码
      • 3.3 在settings中设置开启自定义的下载中间件,设置方法同管道
    • 4、代理IP的使用
      • 4.1 思路分析
      • 4.2 具体实现
      • 4.3 检测代理IP是否可用
    • 5、在中间件中使用selenium
  • 七、scrapy_redis的概念、作用及流程
    • 1、分布式是什么
    • 2、scrapy_redis的概念
    • 3、scrapy_redis的作用
    • 4、scrapy_redis的工作流程
      • 4.1 回顾scrapy流程
      • 4.2 scrapy_redis的流程

一、scrapy的概念作用和工作流程

1、scrapy的概念

scrapy是一个python编写的开源网络爬虫框架。它是一个被设计用于爬虫网络数据、提取结构性数据的框架。

scrapy使用了twisted异步网络框架,可以加快我们的下载速度。
scrapy文档地址:https://scrapy-chs.readthedocs.io/zh-cn/0.24/intro/overview.html

2、scrapy框架的作用

少量的代码,就能够快速的抓取

3、scrapy的工作流程(重点)

3.1 回顾之前的爬虫流程

3.2 改写上述流程

3.3 scrapy的流程

在这里插入图片描述
其流程可以描述为:

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

注意:

  • 图中中文是为了方便理解后加上去
  • 图中绿色线条表示数据的传递
  • 注意图中中间件的位置,决定了其作用
  • 注意其中引擎的位置,所有模块之间相互独立,只和引擎进行交互

3.4 scrapy的三个内置对象

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

3.5 scrapy中每个模块的具体作用

模块作用其他
Scrapy Engine(引擎)总指挥:负责数据和信号在不同模块之间的传递scrapy已经实现
Scheduler(调度器)一个队列,存放引擎发过来的request请求scrapy已经实现
Downloader(下载器)下载引擎发过来的request请求,并返回给引擎scrapy已经实现
Spider(爬虫)处理引擎发来的response,提取数据,提取url,并交给引擎需要手写
Item Pipeline(管道)处理引擎传过来的数据,比如存储需要手写
Downloader Middlewares(下载中间件)可以自定义的下载扩展,比如设置代理一般不用手写
Spider Middlewares(爬虫中间件)可以自定义request请求和进行response过滤一般不用手写

注意:

  • 爬虫中间件和下载中间件只是运行逻辑的位置不同,作用是重复的:如替换UA等。

二、scrapy的入门使用

1、安装scrapy

命令:sudo apt-get install scrapy 或者 pip/pip3 install scrapy

2、scrapy项目开发流程

  1. 创建项目:scrapy startproject mySpider
  2. 生成爬虫:scrapy genspider itcast itcast.cn
  3. 提取数据:根据网站结构在spider中实现数据采集相关内容
  4. 保存数据:使用pipeline进行数据后续处理和保存

3、创建项目

创建scrapy项目的命令:scrapy startproject <项目名称>
示例:scrapy startproject myspider
生成的目录和文件结果如下:
在这里插入图片描述

4、创建爬虫

在项目路径下执行如下命令:scrapy genspider <爬虫名字> <允许爬取的域名>
爬虫名字:作为爬虫运行时的参数
允许爬取的域名:设置爬虫的爬取范围,设置之后用于过滤要爬取的url,如果爬取的url与允许的域不同则会被过滤掉。
示例:
cd myspider
scrapy genspider itcast itcast.cn
生成的目录和文件结果如下:
在这里插入图片描述

5、完善爬虫

在上一步生成出来的爬虫文件中编写指定网站的数据采集操作,实现数据提取

5.1 在/spiders/itcast.py中修改内容

import scrapy


class ItcastSpider(scrapy.Spider):  # 继承scrapy.Spider
    name = 'itcast'                     # 爬虫名字
    allowed_domains = ['itcast.cn']     # 允许爬取的范围
    start_urls = ['https://www.itheima.com/teacher.html#ajavaee']  # 开始爬取的url地址

    # 数据提取的方法,接收下载中间件传过来的response
    def parse(self, response):
        # 获取所有老师节点
        node_list = response.xpath('//div[@class="li_txt"]')

        # 遍历老师节点列表
        for node in node_list:
            temp = {}

            # xpath方法返回的是选择器对象列表,extract()用于从选择器对象中提取数据
            temp['name'] = node.xpath('./h3/text()')[0].extract()
            temp['title'] = node.xpath('./h4/text()')[0].extract()
            temp['desc'] = node.xpath('./p/text()')[0].extract()

            yield temp

注意:

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

5.2 定位元素以及提取数据、属性值的方法

解析并获取scrapy爬虫中的数据:利用xpath规则字符串进行定位和提取

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

5.3 response响应对象的常用属性

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

6、保存数据

利用管道pipeline来处理(保存)数据

6.1 在pipelines.py文件中定义对数据的操作

  1. 定义一个管道类
  2. 重写管道类的process_item方法
  3. process_item方法处理完item之后必须返回给引擎
import json


class MyspiderPipeline:
    def __init__(self):
        self.file = open('itcast.json', 'w')

    def process_item(self, item, spider):
        # 将字典数据序列化
        json_data = json.dumps(item, ensure_ascii=False) + ',\n'
        # 将数据写入文件
        self.file.write(json_data)

        # 默认使用完管道之后需要将数据返回给引擎
        return item

    def __del__(self):
        self.file.close()

6.2 在settings.py配置启用管道

ITEM_PIPELINES = {
   'myspider.pipelines.MyspiderPipeline': 300,
}

配置项中键为使用的管道类,管道类使用.进行分割,第一个为项目目录,第二个为文件,第三个为定义的管道类。
配置项中值为管道的使用顺序,设置的数值越小越优先执行,该值一般设置为1000以内。

7、运行scrapy

命令:在项目目录下执行scrapy crawl <爬虫名字>
示例:scrapy crawl itcast

三、scrapy数据建模与请求

1、数据建模

通常在做项目的过程中,在items.py中进行数据建模

1.1 为什么要建模

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

1.2 如何建模

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

class MyspiderItem(scrapy.Item):
    # define the fields for your item here like:
    name = scrapy.Field()   # 讲师的名字
    title = scrapy.Field()  # 讲师的职称
    desc = scrapy.Field()   # 讲师的介绍

1.3 如何使用模板类

模板类定义以后需要在爬虫中导入并且实例化,之后的使用方法和使用字典相同
itcast.py:

import scrapy
from myspider.items import MyspiderItem


class ItcastSpider(scrapy.Spider):  # 继承scrapy.Spider
    name = 'itcast'                     # 爬虫名字
    allowed_domains = ['itcast.cn']     # 允许爬取的范围
    start_urls = ['https://www.itheima.com/teacher.html#ajavaee']  # 开始爬取的url地址

    # 数据提取的方法,接收下载中间件传过来的response
    def parse(self, response):
        # 获取所有老师节点
        node_list = response.xpath('//div[@class="li_txt"]')

        # 遍历老师节点列表
        for node in node_list:
            item = MyspiderItem()  # 实例化后直接使用

            # xpath方法返回的是选择器对象列表,extract()用于从选择器对象中提取数据
            item['name'] = node.xpath('./h3/text()')[0].extract()
            item['title'] = node.xpath('./h4/text()')[0].extract()
            item['desc'] = node.xpath('./p/text()')[0].extract()

            yield item

注意:

  • from myspider.items import MyspiderItem这一行代码中注意item的正确导入路径,忽略pycharm标记的错误
  • python中的导入路径要诀:从哪里开始运行,就从哪里开始导入

2、翻页请求的思路

对于要提取如下图中所有页面上的数据该怎么办?

在这里插入图片描述
requests模块是如何实现翻页请求的:

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

scrapy实现翻页的思路:

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

3、构造Request对象,并发送请求

3.1 实现方法

  1. 确定url地址
  2. 构造请求,scrapy.Request(url, callback)
    • callback:指定解析函数名称,表示该请求返回的响应使用哪一个函数进行解析
  3. 把请求交给引擎:yield scrapy.Request(url, callback)

3.2 网易招聘爬虫案例

  • 通过爬取网易社会招聘的页面的招聘信息,学习如何实现翻页请求
  • 地址:https://hr.163.com/job-list.html
    在这里插入图片描述

思路分析:

  1. 获取首页的数据
  2. 寻找下一页的地址,进行翻页,获取数据

注意:

  1. 可以在settings中设置ROBOTS协议

    # False表示忽略网站的robots.txt协议,默认为True
    ROBOTSTXT_OBEY = False
    
  2. 可以在settings中设置User-Agent:

    # scrapy发送的每一个请求的默认UA都是设置的这个User-Agent
    USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
    

3.3 代码实现

  1. 网易社会招聘网站是一个JSON数据渲染的动态网页,网页源码中并不包含数据,所以不能使用xpath进行解析;对网站进行分析,找到请求页面动态加载JSON数据的请求,如下图所示:
    在这里插入图片描述
    在这里插入图片描述
    图中包含了每个职位的相关信息,此时的start_url地址发生了变化,需要进行更改,请求的方式为POST请求,相关的参数如下:
    在这里插入图片描述

  2. 该网站存在翻页,需要进行模拟翻页请求

  3. 将爬取到的数据存储到JSON文件中

  4. 代码如下:
    items.py:

    # Define here the models for your scraped items
    import scrapy
    
    
    class WangyiItem(scrapy.Item):
        name = scrapy.Field()               # 职位名称
        workType = scrapy.Field()           # 工作性质
        workPlaceNameList = scrapy.Field()  # 工作地点
        productName = scrapy.Field()        # 所属产品
        firstPostTypeName = scrapy.Field()  # 职位类别
        recruitNum = scrapy.Field()         # 招聘人数
        reqEducationName = scrapy.Field()   # 学历
        reqWorkYearsName = scrapy.Field()   # 工作经验
        description = scrapy.Field()        # 职位描述
        requirement = scrapy.Field()        # 职位要求
        updateTime = scrapy.Field()         # 更新时间
    
    

    job.py:

    import json
    
    import scrapy
    
    from wangyi.items import WangyiItem
    from datetime import datetime
    
    next_page = 1  # 用来控制翻页的变量
    
    
    class JobSpider(scrapy.Spider):
        name = 'job'
        allowed_domains = ['hr.163.com']
        # start_urls = ['https://hr.163.com/job-list.html']
        start_urls = ['https://hr.163.com/api/hr163/position/queryPage']
    
        def start_requests(self):
            url = self.start_urls[0]
            # 起始url参数
            payload = {
                "currentPage": "1",
                "pageSize": "10"
            }
    
            # 构建post请求
            yield scrapy.Request(
                url=url,
                body=json.dumps(payload),
                method='POST',
                callback=self.parse,
                headers={'Content-Type': 'application/json;charset=utf-8'}
            )
    
        def parse(self, response):
            dic = response.json()
            job_list = dic['data']['list']
            item = WangyiItem()
            for job in job_list:
                item['name'] = job['name'].replace(u'\xa0', u' ')
                item['workType'] = job['workType']
                item['workPlaceNameList'] = str(job['workPlaceNameList']).replace('[', '').replace(']', '').replace("'", '')
                item['productName'] = job['productName']
                item['firstPostTypeName'] = job['firstPostTypeName']
                item['recruitNum'] = job['recruitNum']
                item['reqEducationName'] = job['reqEducationName']
                item['reqWorkYearsName'] = job['reqWorkYearsName']
                item['description'] = job['description'].replace('\t', '').replace('\n', '').replace('\\', '/')\
                    .replace(u'\xa0', u' ')
                item['requirement'] = job['requirement'].replace('\t', '').replace('\n', '').replace(u'\xa0', u' ')
                item['updateTime'] = datetime.utcfromtimestamp(job['updateTime'] / 1000).strftime('%Y-%m-%d')
    
                yield item
    
            # 模拟翻页
            page_stutas = dic['data']['lastPage']  # 根据返回的数据来判断是否是最后一页,true为最后一页
            global next_page
            next_page = next_page + 1
            if not page_stutas:
                print("正在爬取第{}页···········".format(next_page))
                payload = {
                    "currentPage": "{}".format(next_page),
                    "pageSize": "10"
                }
                url = self.start_urls[0]
                yield scrapy.Request(
                    url=url,
                    body=json.dumps(payload),
                    method='POST',
                    callback=self.parse,
                    headers={'Content-Type': 'application/json;charset=utf-8'}
                )
    
    

    pipelines.py:

    # Define your item pipelines here
    import json
    
    
    class WangyiPipeline:
        def __init__(self):
            self.file = open('wangyi.json', 'w', encoding='utf-8')
    
        def process_item(self, item, spider):
            # 将item对象强转为字典,该操作只能在scrapy中使用
            item = dict(item)
            # 将字典数据序列化
            json_data = json.dumps(item, ensure_ascii=False) + ',\n'
            # 将数据写入文件
            self.file.write(json_data)
    
            # 默认使用完管道之后需要将数据返回给引擎
            return item
    
        def __del__(self):
            self.file.close()
        
    
  5. 最终结果:
    在这里插入图片描述

3.4 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地址可以把它设置成True,比如贴吧的翻页请求,页面的数据总是在变化;start_urls中的地址会被反复请求,否则程序不会启动
  5. method:指定POST或GET请求
  6. header:接收一个字典,其中不包括cookie
  7. cookies:接收一个字典,专门放置cookie
  8. body:接收json字符串,为POST的数据,发送payload_post请求时使用

4、meta参数的使用

meta的作用:meta可以实现数据在不同解析函数中的传递

在爬虫文件的parse方法中,提取详情页增加之前callback指定的parse_detail函数:

def parse(self, response):
	...
	yield scrapy.Request(
		url = detail_url,
		callback = self.parse_detail,
		meta = {"item": item}
	)

def parse_detail(self, response):
	# 获取之前传入的item
	item = response.meta["item"]

注意:

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

四、scrapy模拟登录

1、模拟登录的方法

1.1 requests的模拟登录

  1. 直接携带cookies请求页面
  2. 找url地址,发送post请求存储cookies

1.2 selenium的模拟登录

  1. 找到对应的input标签,输入文本点击登录

1.3 scrapy的模拟登录

  1. 直接携带cookies
  2. 找url地址,发送post请求存储cookie

2、scrapy携带cookies直接获取需要登录后的页面

应用场景

  1. cookie过期时间很长,常用于一些不规范的网站
  2. 能在cookie过期前把所有的数据拿到
  3. 配合其他程序使用,比如其使用selenium把登录之后的cookie获取保存到本地,scrapy发送请求之前先读取本地cookie

2.1 实现:重构scrapy的start_requests方法

scrapy中start_url是通过start_request来进行处理的,其实现代码如下:

def start_requests(self):
	cls = self.__class__
	if not self.start_urls and hasattr(self, 'start_url'):
	    raise AttributeError(
	        "Crawling could not start: 'start_urls' not found "
	        "or empty (but found 'start_url' attribute instead, "
	        "did you miss an 's'?)")
	if method_is_overridden(cls, Spider, 'make_requests_from_url'):
	    warnings.warn(
	        "Spider.make_requests_from_url method is deprecated; it "
	        "won't be called in future Scrapy releases. Please "
	        "override Spider.start_requests method instead "
	        f"(see {cls.__module__}.{cls.__name__}).",
	    )
	    for url in self.start_urls:
	        yield self.make_requests_from_url(url)
	else:
	    for url in self.start_urls:
	        yield Request(url, dont_filter=True)

所以对应的,如果start_url地址中的url是需要登录后才能访问的url地址,则需要重写start_request方法并在其中手动添加上cookie

2.2 携带cookies登录GitHub

import scrapy


class Git1Spider(scrapy.Spider):
    name = 'git1'
    allowed_domains = ['github.com']
    start_urls = ['https://github.com/yixinchd']

    def start_requests(self):
        url = self.start_urls[0]
        temp = '_octo=GH1.1.1704635908.1695371613; preferred_color_mode=dark; tz=Asia%2FShanghai; _device_id=e0e107d487355d28e612363acb5a1896; has_recent_activity=1; saved_user_sessions=102498110%3AVR4xKz7HlrvOYXtBGIYS5ymlBh60jLTs5ZluskQ1quEy_SHy; user_session=VR4xKz7HlrvOYXtBGIYS5ymlBh60jLTs5ZluskQ1quEy_SHy; __Host-user_session_same_site=VR4xKz7HlrvOYXtBGIYS5ymlBh60jLTs5ZluskQ1quEy_SHy; tz=Asia%2FShanghai; color_mode=%7B%22color_mode%22%3A%22light%22%2C%22light_theme%22%3A%7B%22name%22%3A%22light_high_contrast%22%2C%22color_mode%22%3A%22light%22%7D%2C%22dark_theme%22%3A%7B%22name%22%3A%22dark%22%2C%22color_mode%22%3A%22dark%22%7D%7D; logged_in=yes; dotcom_user=yixinchd; _gh_sess=qO24Aua93K95viiLT3FeWsEqiAZRqjuIA%2BBJMmnRsaEKwiXYC8MJZF1nXni%2Fvt0bLRxwXadmjsHsbIuFTfLx6EwdGuQVjAdC0b88nq7w%2Bo7eOZYspcKxyqn5QoHYOiKFkPG8up6y7jwLwWgdZawMWndiK3OvlHO4rYccMttUcRUgmWY%2Fqlc1y8PgGDbuphe0aUY2agmZjxsm4DUmOCOZrnQybBXefybehb8Y5yql5A%2BWKNYES1dIDd9wToPmjYhf%2Fg0Y3rn0Vd%2Fi%2FIW%2Fj%2BfDCpv3BB4NY2zBBR3B3LSrRzYMNvOQFBM1SsL2vaoQSRg5kt6OpQfpkZZ7LZLZn0dmBoGNOrV66efLO4cSq8VXoUqPafbz%2B%2BFGx6NbgR0bFBkSDuHuTf%2FqFKb0io6d2LqcASJgIU6aOqSR2TDPsdob25leBzErM1mVwTN7sTq36CD5Fypr%2Fyuj8Ofu6HA%2FJ7oDss1UUAl%2Bh5G8C8I%2F1zcZKEkr5mlyL4LbZdteBqLfBE2PQp%2B1l%2BdZBLKkTbBrUTc%2BB1IFupsDIocOh5gx0%2BszdegRieG3RNn%2BfuqaxOsbRbWGNI%2F7pVgdswp%2F67PI6NT6SbnXwDRccZO63f8k3hAc26Az%2B5JmXf%2B5XWkgcf8hGWUL3eWJAOn7HXjUp7bCERsD%2BxEZH%2FRQDv8EozZKpGi3AWyL4uJYfondySe6kOARef0Vr%2Fe0AoFBakLmrEkvv7nI9BpbYDCj5zuV1ItPkDxPy%2BpOPzFbbZGEOx8dkKQenNFj0LzG1VSUGa5ZyYxwXdTu%2FwxouF4mgi%2Bp1c0Jl60BYuidKo0izfVH3giU4VZG6wzDi1QU66UapA5UzsixdTJJL8gQ0uPst1NUdvocY%2FToIyGwntGoPR3NYlCm9Jx2uE2WaKCJ1S8ahcyX5sV95t4ioEm%2FSrsuwrjBSbohk2mB%2B81xAPR4qBDURwpVwXSXt0LYvl5CIJUL%2BUnZd17%2BvvOTwpfY0e5uS3u2vOUVwgdD9dI2ZU3MFqURgjHjz15gSfSqZdb64Q%3D%3D--Fdopx2y%2BDKwpM9rj--Yn7THwpkvt8r67w5NaoDFQ%3D%3D'
        cookies = {data.split('=')[0]: data.split('=')[-1] for data in temp.split('; ')}
        yield scrapy.Request(
            url=url,
            callback=self.parse,
            cookies=cookies
        )

    def parse(self, response):
        print(response.xpath('/html/head/title/text()').extract_first())

注意:

  1. scrapy中cookie不能放在headers中,在构造请求的时候有专门的cookies参数,能够接收字典形式的cookie
  2. 在setting中设置Robots协议、User_Agent

3、scrapy.Request发送post请求

可以通过scrapy.Request()指定method、body参数来发送post请求,但通常使用scrapy.FormRequest()来发送post请求

3.1 发送post请求

注意:scrapy.FormRequest()能够发送表单和ajax请求

3.1.1 思路分析
  1. 找到post的url地址:点击登录按钮进行抓包,然后定位url地址为
  2. 找到请求体的规律:分析post请求的请求体,其中包含的参数均在前一次的响应中
  3. 是否登录成功:通过请求个人主页,观察是否包含用户名
3.1.1 代码实现
import scrapy


class LoginSpider(scrapy.Spider):
    name = 'login'
    allowed_domains = ['github.com']
    start_urls = ['https://github.com/login']

    def parse(self, response):
        # 从登录页面响应中解析出post数据
        token = response.xpath('//input[@name="authenticity_token"]/@value').extract_first()
        post_data = {
            'commit': 'Sign in',
            'authenticity_token': token,
            'login': '**************',
            'password': '**************',
            'webauthn-conditional': 'undefined'
        }
        print(post_data)
        
        # 针对登录url发送post请求
        yield scrapy.FormRequest(
            url='https://github.com/session',
            callback=self.after_login,
            formdata=post_data
        )

    def after_login(self, response):
        yield scrapy.Request(
            url='https://github.com/yixinchd',
            callback=self.check_login
        )

    def check_login(self, response):
        print(response.xpath('/html/head/title/text()').extract_first())

注意:

  • 在settings.py中通过设置COOKIES_DEBUG = True能够在终端看到cookie的传递过程
    在这里插入图片描述

五、scrapy管道的使用

1、pipeline 中常用的方法

  1. process_item(self, item, spider)
    • 管道类中必须有的函数
    • 实现对item数据的处理
    • 必须return item
  2. open_spider(self, spider):在爬虫开启的时候仅执行一次
  3. close_spider(self, spider):在爬虫关闭的时候仅执行一次

2、管道文件的修改

3、开启管道

在settings.py设置开启pipeline


注意:在settings中能够开启多个管道,为什么需要开启多个?

  1. 不同的pipeline可以处理不同爬虫的数据,通过spider.name属性来区分
  2. 不同的pipeline能够对一个或多个爬虫进行不同的数据处理的操作,比如一个进行数据清洗,一个进行数据的保存
  3. 同一个管道类也可以处理不同爬虫的数据,通过spider.name属性来区分

4、pipeline使用注意点

  1. 使用之前需要在settings中开启
  2. pipeline在settings中,键表示位置(即pipeline在项目中的位置可以自定义),值表示距离引擎的远近,越近数据会越先经过:权重值小的优先执行
  3. 有多个pipeline的时候,process_item方法必须return item,否则后一个pipeline取到的数据为None值
  4. pipeline中process_item的方法必须有,否则item没有办法接收和处理
  5. process_item方法接收item和spider,其中spider表示当前传递item过来的spider
  6. open_spider(spider):能够在爬虫开启的时候执行一次
  7. close_spider(spider):能够在爬虫关闭的时候执行一次
  8. 上述两个方法经常用于爬虫和数据库的交互,在爬虫开启的时候建立数据库连接,在爬虫关闭的时候断开数据库的连接

六、scrapy中间件的使用

1、scrapy中间件的分类和作用

1.1 scrapy中间件的分类

根据scrapy运行流程中所在位置不同分为:

  1. 下载中间件
  2. 爬虫中间件

1.2 scrapy中间件的作用

预处理request和response对象

  1. 对header以及cookie进行更换和处理
  2. 使用代理ip等
  3. 对请求进行定制化操作

在scrapy默认的情况下,两种中间件都在middlewares.py文件中,爬虫中间件使用方法和下载中间件相同,且功能重复,通常使用下载中间件

2、下载中间件的使用方法

Downloader Middlewares默认的方法:

  • process_request(self, request, spider):
  1. 当每个request通过下载中间件时,该方法被调用
  2. 返回None值:没有return也是返回None,该request对象传递给下载器,或通过引擎传递给其他权重低的process_request方法
  3. 返回Response对象:不再请求,把response返回给引擎
  4. 返回Request对象:把request对象通过引擎交给调度器,此时不通过其他权重低的process_request方法
  • process_response(self, request, response, spider):
  1. 当下载器完成http请求,传递响应给引擎的时候调用
  2. 返回Response对象:通过引擎交给爬虫处理或交给权重更低的其他下载中间件的process_response方法
  3. 返回Request对象:通过引擎交给调度器继续请求,此时将不通过其他权重低的process_request方法
  • 在settings.py中配置开启中间件,权重值越小越优先执行

3、定义实现随机User-Agent的下载中间件

3.1 在settings.py中添加UA列表

USER_AGENT_LIST = [
    "Mozilla/5.0 (Windows; U; Windows 98) AppleWebKit/532.16.1 (KHTML, like Gecko) Version/4.0.3 Safari/532.16.1",
    "Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.0 (KHTML, like Gecko) Chrome/59.0.863.0 Safari/535.0",
    "Mozilla/5.0 (iPod; U; CPU iPhone OS 4_3 like Mac OS X; kl-GL) AppleWebKit/534.50.7 (KHTML, like Gecko) Version/4.0.5 Mobile/8B113 Safari/6534.50.7",
    "Mozilla/5.0 (X11; Linux x86_64; rv:1.9.5.20) Gecko/7048-11-15 20:21:34 Firefox/11.0",
    "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_8_0) AppleWebKit/531.1 (KHTML, like Gecko) Chrome/54.0.878.0 Safari/531.1",
    "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_11_4; rv:1.9.6.20) Gecko/5511-07-04 09:40:02 Firefox/3.6.20",
    "Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 6.1; Trident/3.1)",
    "Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 5.0; Trident/3.0)",
    "Mozilla/5.0 (Linux; Android 9) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/23.0.863.0 Safari/533.2",
    "Mozilla/5.0 (Windows; U; Windows NT 10.0) AppleWebKit/531.7.2 (KHTML, like Gecko) Version/4.1 Safari/531.7.2",
    "Mozilla/5.0 (compatible; MSIE 5.0; Windows 95; Trident/5.0)",
    "Mozilla/5.0 (iPad; CPU iPad OS 4_2_1 like Mac OS X) AppleWebKit/536.0 (KHTML, like Gecko) CriOS/36.0.810.0 Mobile/82Q614 Safari/536.0",
    "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/3.1)",
    "Mozilla/5.0 (iPad; CPU iPad OS 3_1_3 like Mac OS X) AppleWebKit/533.0 (KHTML, like Gecko) FxiOS/14.9o6822.0 Mobile/83W885 Safari/533.0",
    "Mozilla/5.0 (Macintosh; PPC Mac OS X 10_10_0; rv:1.9.6.20) Gecko/5731-02-10 20:23:15 Firefox/6.0",
    "Mozilla/5.0 (compatible; MSIE 8.0; Windows CE; Trident/3.0)",
    "Opera/8.62.(Windows NT 6.2; zh-HK) Presto/2.9.168 Version/12.00",
    "Mozilla/5.0 (Windows 98) AppleWebKit/536.1 (KHTML, like Gecko) Chrome/13.0.827.0 Safari/536.1",
    "Opera/9.98.(Windows NT 6.1; pa-PK) Presto/2.9.188 Version/12.00"
]

3.2 在middleware.py中完善代码

import random
from douban.settings import USER_AGENT_LIST


# 定义一个中间件类
class RandomUserAgent(object):
    def process_request(self, request, spider):
        ua = random.choice(USER_AGENT_LIST)
        request.headers['User-Agent'] = ua


class CheckUA:
    def process_response(self, request, response, spider):
        print(request.headers['User-Agent'])
        return response

3.3 在settings中设置开启自定义的下载中间件,设置方法同管道

DOWNLOADER_MIDDLEWARES = {
   'douban.middlewares.RandomUserAgent': 543,
   'douban.middlewares.CheckUA': 600,
}

4、代理IP的使用

4.1 思路分析

  1. 代理添加的位置:request.meta中增加proxy字段
  2. 获取一个代理IP,赋值给request.meta['proxy']
    • 代理池中随机选择代理IP
    • 代理IP的webapi发送请求获取一个代理IP

4.2 具体实现

class RandomProxy(object):
    def process_request(self, request, spider):
        proxy = random.choice(PROXY_LIST)
        print(proxy)
        if 'user_passwd' in proxy:
            # 对账号密码进行编码
            b64_up = base64.b64encode(proxy['user_passwd'].encode())
            # 设置认证
            request.headers['Proxy-Authorization'] = 'Basic' + b64_up.decode()
            # 设置代理
            request.meta['proxy'] = proxy['ip_port']
        else:
            # 设置代理
            request.meta['proxy'] = proxy['ip_port']

4.3 检测代理IP是否可用

在使用了代理IP的情况下,可以在下载中间件的process_response()方法中处理代理IP的使用情况,如果该代理IP不能使用可以替换其他代理IP

class ProxyMiddleware(object):
	......
	def process_response(self, request, response, spider):
		if response.status != '200':
	    	request.dont_filter = True  # 重新发送的请求对象能够再次进入队列
	       	return request

在settings.py中开启该中间件

5、在中间件中使用selenium

七、scrapy_redis的概念、作用及流程

1、分布式是什么

分布式就是不同的节点(服务器,IP不同)共同完成一个任务

2、scrapy_redis的概念

scrapy_redis是scrapy框架的基于redis的分布式组件

3、scrapy_redis的作用

scrapy_redis在scrapy的基础上实现了更多,更强大的功能,具体体现在:

通过持久化请求队列和请求的指纹集合来实现:

  • 断点续爬
  • 分布式快速抓取

4、scrapy_redis的工作流程

4.1 回顾scrapy流程

在这里插入图片描述

4.2 scrapy_redis的流程

  • 在scrapy_redis中,所有待抓取的request对象和去重的request对象指纹都存在所有的服务器共用的redis中
  • 所有服务器中scrapy进程共用同一个redis中的request对象的队列
  • 所有的request对象存入redis前,都会通过该redis中的request指纹集合进行判断,之前是否已经存入过
  • 在默认情况下所有的数据会保存在redis中
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/483717.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习】无监督学习算法之:主成分分析

主成分分析 1、引言2、主成分分析2.1 定义2.2 原理2.3 实现方式2.4 算法公式2.5 代码示例 3、总结 1、引言 小屌丝&#xff1a;鱼哥&#xff0c; 快&#xff0c;快。 小鱼&#xff1a;… 啥情况&#xff0c; 你可别乱喊。 小屌丝&#xff1a;额… 我的意思&#xff0c;是你该继…

【附订阅OnlyFans攻略】2024年AI:一个交织着创新与挑战的故事

2024年AI&#xff1a;一个交织着创新与挑战的故事 在2024年的一个清晨&#xff0c;阳光透过智能窗户的调节&#xff0c;柔和地洒在书房里。李华&#xff0c;一位年轻的科技创业者&#xff0c;坐在书桌前&#xff0c;凝视着电脑屏幕上不断跳动的数据和图像。他正在进行一项重要…

Flutter 项目架构技术指南

Flutter 项目架构技术指南 视频 https://www.bilibili.com/video/BV1rx4y127kN/ 前言 原文 https://ducafecat.com/blog/flutter-clean-architecture-guide 探讨Flutter项目代码组织架构的关键方面和建议。了解设计原则SOLID、Clean Architecture&#xff0c;以及架构模式MVC…

qt 实现 轮播图效果,且还有 手动 上一页和下一页 已解决

QT中有 轮播图的需求&#xff0c;按照正常html版本 。只需要配置数组就能搞定&#xff0c;但是c qt版本 应该用什么了。 第一想到的是采用定时器。 // 定时器初始化{m_pTime new QTimer(this);m_pTime->start(4 * 1000);//启动定时器并设置播放时间间隔m_pAutoFlag true;/…

网络层(IP层)

IP协议的本质&#xff1a;有将数据跨网络传输的能力 而用户需要的是将数据从主机A到主机B可靠地跨网络传输 IP的组成&#xff1a;目标网络目标主机 IP由目标网络和目标主机两部分组成&#xff0c;IP报文要进行传输&#xff0c;要先到达目标网络&#xff0c;然后经过路由器转到…

使用阿里云服务器搭建网站教程,超简单10分钟网站上线

使用阿里云服务器快速搭建网站教程&#xff0c;先为云服务器安装宝塔面板&#xff0c;然后在宝塔面板上新建站点&#xff0c;阿里云服务器网aliyunfuwuqi.com以搭建WordPress网站博客为例&#xff0c;来详细说下从阿里云服务器CPU内存配置选择、Web环境、域名解析到网站上线全流…

【算法设计与分析】实现Trie前缀树

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;算法分析与设计 ⛺️稳中求进&#xff0c;晒太阳 题目 Trie&#xff08;发音类似 "try"&#xff09;或者说 前缀树 是一种树形数据结构&#xff0c;用于高效地存储和检索字符串…

【评分标准】【网络系统管理】2019年全国职业技能大赛高职组计算机网络应用赛项H卷 无线网络勘测设计

第一部分&#xff1a;无线网络勘测设计评分标准 序号评分项评分细项评分点说明评分方式分值1点位设计图AP编号AP编号符合“AP型号位置编号”完全匹配5AP型号独立办公室、小型会议室选用WALL AP110完全匹配5员工寝室选用智分&#xff0c;其他用放装完全匹配5其它区域选用放装AP…

睿考网:二建可以跨省考试吗?

二级建造师资格考试允许考生进行跨省报名&#xff0c;但是考试通过后的注册环节&#xff0c;必须在考试所在的省份完成。建议在初始注册过程中选择与报名信息相符的单位&#xff0c;以确保符合当地的职业要求规定。 关于二建的考试地点&#xff0c;考生可以选择在工作单位所在…

鸿蒙Harmony应用开发—ArkTS-@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化

上文所述的装饰器仅能观察到第一层的变化&#xff0c;但是在实际应用开发中&#xff0c;应用会根据开发需要&#xff0c;封装自己的数据模型。对于多层嵌套的情况&#xff0c;比如二维数组&#xff0c;或者数组项class&#xff0c;或者class的属性是class&#xff0c;他们的第二…

sr501人体红外传感器

sr501人体红外传感器 文章目录 sr501人体红外传感器1.介绍2.使用方法3.示例代码 持续更新中1.介绍 模块信息介绍来自百问网&#xff0c;仅供学习和参考​ 人体都有恒定的体温&#xff0c;一般在 37 度&#xff0c;所以会发出特定波长 10uM 左右的红外线&#xff0c;被动式红外…

推荐一款制造执行系统(MES)国内比较好的实施厂家

什么是MES 制造执行系统&#xff08;MES&#xff09;是一种用于监控、控制和优化制造过程的软件系统。它通过与企业资源计划&#xff08;ERP&#xff09;系统和自动化系统的集成&#xff0c;实现对生产过程的管理和监测&#xff0c;包括生产计划、生产过程和生产数据。 MES可…

基于python+vue分类信息服务平台移动端的设计与实现flask-django-php-nodejs

分类信息服务平台是在Android操作系统下的应用平台。为防止出现兼容性及稳定性问题&#xff0c;框架选择的是django&#xff0c;Android与后台服务端之间的数据存储主要通过MySQL。用户在使用应用时产生的数据通过 python等语言传递给数据库。通过此方式促进分类信息服务平台信…

高等数学基础篇(数二)之微分方程

微分方程&#xff1a; 一、常微分方程的基本概念 二、一阶微分方程 三、可降阶的高阶方程 四、高阶线性微分方程 目录 一、常微分方程的基本概念 二、一阶微分方程 三、可降阶的高阶方程 四、高阶线性微分方程 一、常微分方程的基本概念 二、一阶微分方程 帮助理解&…

C++学习之旅(二)运行四个小项目 (Ubuntu使用Vscode)

如果是c语言学的比较好的同学 可以直接跟着代码敲一遍&#xff0c;代码附有详细语法介绍&#xff0c;不可错过 一&#xff0c;猜数字游戏 #include <iostream> #include <cstdlib> #include <ctime>int main() {srand(static_cast<unsigned int>(tim…

SpringBoot整合Swagger-UI实现在线API文档

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉🍎个人主页:Leo的博客 💞当前专栏: 循序渐进学SpringBoot ✨特色专栏: MySQL学习 🥭本文内容:SpringBoot整合Swagger-UI实现在线API文档 📚个人知识库: Leo知识库,欢迎大…

在 vite 开发环境,使用https自签证书 --- mkcert

在 vite 开发环境&#xff0c;使用https自签证书 — mkcert 使用basicSsl&#xff08;vitejs/plugin-basic-ssl&#xff09; 在vite开发环境中&#xff0c;使用 basicSsl 插件能暂时提供https服务&#xff0c;同时&#xff0c;也会面临总是提示一下的问题,如下图 提示https证…

108、3D Gaussian Splatting for Real-Time Radiance Field Rendering

简介 官网 更少训练时间的同时实现最先进的视觉质量&#xff0c;能在1080p分辨率下实现高质量的实时(≥30 fps)新视图合成 NeRF使用隐式场景表示&#xff0c;体素&#xff0c;点云等属于显示建模方法&#xff0c;3DGS就是显示辐射场。它用3D高斯作为灵活高效的表示方法&…

R-CNN笔记

目标检测之R-CNN论文精讲&#xff0c;RCNN_哔哩哔哩_bilibili 论文背景 在该论文提出之前&#xff0c;主流的目标检测思路是&#xff1a; 将一幅图片划分成很多个区域&#xff0c;单独提取出来 对于每个区域使用传统的特征提取方法提取 提取结束后可以使用以为特征向量表示 可以…

Python+django+vue开发的家教信息管理系统

一直想做一款管理系统&#xff0c;看了很多优秀的开源项目但是发现没有合适的。 于是利用空闲休息时间开始自己写了一套管理系统。 功能介绍 平台采用B/S结构&#xff0c;后端采用主流的Pythondjango进行开发&#xff0c;前端采用主流的Vue.js进行开发。 整个平台包括前台和…