Scrapy框架学习笔记(B站课程随堂记录)

课程资源: 【小琦资源】Python全系列之爬虫scrapy框架及案例

Scrapy框架介绍

  • scrapy框架爬虫流程
    scrapy框架爬虫流程
  • 特点
  1. 通过引擎调度几大模块减少模块间的依赖
  2. 可以通过 Middlewares 中间件进行一些处理
  • 爬取流程
    scheduler 存放初始url --> 引擎 --> downloader 下载页面内容 --> 引擎 --> spiders 提取数据/提取下一个url --> 引擎 --> item pipeline 对数据操作(存取,打印)
1. requests
2. requests
3. response
4. response
5.1 items
6.items
5.2 next_url
Scheduler 存放初始url
引擎
Downloader 下载页面内容
Spiders 提取数据/提取下一个url
Item Pipeline 操作数据

Scrapy 框架安装和使用方法(Windows 操作系统)

  • 安装(anaconda)
    • 使用anaconda直接 conda install scrapy
    • 过后会停留在Proceed <[y]/n>? 这一行
    • 输入 y 确认 继续安装
    • 待安装完成后,输入 scrapy
    • 若能显示出scrapy的一些命令则为安装成功
  • 安装(python 3.7)
    • scrapy框架的安装需要许多的依赖库,其中 Twisted 用 pip 方法安装会不成功
    • 因此,先去下载 Twisted 的离线安装包,使用CTRL + F 查找Twisted,再下载相应的 whl 文件
    • win32 即 32 位系统,amd64 即 64 位系统
    • 在 cmd 终端 通过 cd 命令,打开下载的文件所在的文件夹
    • 输入pip install 文件名.whl安装
    • 安装完成后通过 pip install scrapy 安装 scrapy 框架

  • 使用
    • 导入:
      import scrapy
      
    • 命令:
      • 测试 xpath/css 表达式:
        response.xpath(“表达式”) [ [0], .extract(), .extract_first() ]
        response.css(‘表达式’) [ [0], .extract(), .extract_first() ]
      • 创建:
             ----------常用命令----------         -----作用-----
          scrapy                            | 弹出scrapy命令列表
          scrapy startproject 项目名         | 创建一个项目
          scrapy genspider 爬虫名 url        | 创建一个爬虫
          scrapy genspider -t 模板 name url  | 一般用的有basic(默认)和crawl
          cd 项目名                          | 进入项目文件夹(Windows命令)
          cd ..                             | 退出项目(Windows命令)
          scrapy check                      | 检查代码是否存在错误,并且指出错误
          scrapy list                       | 返回项目里所有的爬虫名称
          scrapy edit 爬虫名                 | 在命令行进行编辑(不方便,一般不用)
      	scrapy settings                   | 查看设置
      	scrapy settings -h 配置名          | 获取配置信息
      	scrapy runspider name.py          | 运行单独一个爬虫文件
      	scrapy shell url                  | 进入交互终端,用于调试爬虫
      	scrapy fetch url                  | 请求这个网址并且返回内容
      	scrapy view url                   | 直接把收集的内容在浏览器中打开
      	scrapy version                    | 查看版本信息
      	scrapy crawl 爬虫名 -o file_name   | 运行爬虫,并输出特定文件(文件可不输出)
      	scrapy runspider 爬虫名.py         | 运行爬虫文件
      	上述命令 --nolog                   | 不显示日志
      	上述命令 --headers                 | 返回响应头信息
      	上述命令 --no-redirect             | 网址重定向
      

Scrapy 项目文件构成

  • 当创建好一个项目后,会生成几个项目文件。
  • 具体介绍如下:
    	|-- spider. py          ----  爬虫文件夹
    	    |---- _init_. py    ----  默认的爬虫代码文件
    	    |---- itcast. py    ----  具体爬虫代码文件
    	|-- _init_. py          ----  爬虫项目的初始化文件,用来对项目做初始化工作。
    	|-- items. py           ----  爬虫项目的数据容器文件,用来定义要获取的数据。
    	|-- pipelines. py       ----  爬虫项目的管道文件,用来对items中的数据进行进一步的加工处理
    	|-- middlewares.py      ----  爬虫中间件文件,处理request和reponse等相关配置
    	|-- setting. py         ----  爬虫项目的设置文件,包含了爬虫项目的设置信息
    	|-- scrapy. cfg         ----  爬虫项目的配置文件,定义了项目的配置文件路径、部署相关信息等内容
    

Scrapy Shell

命令:scrapy shell url

scrapy shell 是一个交互式终端
当我们输入命令后,会刷新出一系列的运行信息,同时也会展示出当前哪些对象时可以使用的
找到 Available Scrapy objects ,会看到下面有一系列的选项

  • 作用:
  1. 不清楚response有哪些属性时,可以输入 response.(有个小点) ,就会显示出 Response 的所有属性
  2. 调试 xpath 表达式
  • 例子:
    response.url :当前相应的url地址
    response.request.url:当前响应对应的请求的url地址
    response.headers:响应头
    response.body:响应体,也就是html代码,默认是 byte 类型
    response.requests.headers:当前响应的请求头(默认User-Agent更改)

Setting模块

  1. setting 文件解析
  • 存在意义:
    1.存放一些公共的变量(数据库的地址,账号密码等)
    2.集中度高,方便修改
  • 一般使用全大写字母命名变量名:SQL_HOST = ‘192.168.0.1’
 Scrapy settings for mySpider project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
#     https://doc.scrapy.org/en/latest/topics/settings.html
#     https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
#     https://doc.scrapy.org/en/latest/topics/spider-middleware.html

BOT_NAME = 'mySpider'   :项目名

SPIDER_MODULES = ['mySpider.spiders']     :爬虫文件位置
NEWSPIDER_MODULE = 'mySpider.spiders'     :新建爬虫文件的位置

LOG_LEVEL = 'WARNING'      :debug显示信息,WARNING及WARNING以上

# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'mySpider (+http://www.yourdomain.com)'    :生成标识

# Obey robots.txt rules
ROBOTSTXT_OBEY = True   :默认为True:遵守Robot协议 | False:不遵守

# Configure maximum concurrent requests performed by Scrapy (default: 16)
CONCURRENT_REQUESTS = 32  :设置最大编码请求 越大越快乐,但是也容易被发现,默认即可

# Configure a delay for requests for the same website (default: 0)
# See https://doc.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
DOWNLOAD_DELAY = 3   :下载延迟,请求前等3秒钟,可以让爬虫爬取速度变慢
# The download delay setting will honor only one of:
CONCURRENT_REQUESTS_PER_DOMAIN = 16  :每个域名的最大并发数
CONCURRENT_REQUESTS_PER_IP = 16      :每个IP地址最大并发数

# Disable cookies (enabled by default)
COOKIES_ENABLED = False    :Cookie是否开启,在请求下一个url地址的时候是否带上cookie

# Disable Telnet Console (enabled by default)
TELNETCONSOLE_ENABLED = False  :一个插件

# Override the default request headers:
DEFAULT_REQUEST_HEADERS = {
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'Accept-Language': 'en',
}   :默认请求头,user Agent放这里是没有用的,上面有专门存user Agent的地方

# Enable or disable spider middlewares
# See https://doc.scrapy.org/en/latest/topics/spider-middleware.html
SPIDER_MIDDLEWARES = {
   'mySpider.middlewares.MyspiderSpiderMiddleware': 543,
}   :爬虫中间件

# Enable or disable downloader middlewares
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
   'mySpider.middlewares.MyspiderDownloaderMiddleware': 543,
}   :下载中间件

# Enable or disable extensions
# See https://doc.scrapy.org/en/latest/topics/extensions.html
EXTENSIONS = {
   'scrapy.extensions.telnet.TelnetConsole': None,
}# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {   #pipeline 优先级 数值表示距离引擎距离
   'mySpider.pipelines.MyspiderPipeline': 300,
}   :位置,权重

# Enable and configure the AutoThrottle extension (disabled by default)
# See https://doc.scrapy.org/en/latest/topics/autothrottle.html
AUTOTHROTTLE_ENABLED = True   :自动限速
# The initial download delay
AUTOTHROTTLE_START_DELAY = 5# The maximum download delay to be set in case of high latencies
AUTOTHROTTLE_MAX_DELAY = 60# The average number of requests Scrapy should be sending in parallel to
# each remote server
AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# Enable showing throttling stats for every response received:
AUTOTHROTTLE_DEBUG = False

# Enable and configure HTTP caching (disabled by default)
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
HTTPCACHE_ENABLED = True       :HTTP缓存
HTTPCACHE_EXPIRATION_SECS = 0
HTTPCACHE_DIR = 'httpcache'
HTTPCACHE_IGNORE_HTTP_CODES = []
HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'

-1. 每个模块的 See 语句后面的 url 都是各个模块对应的文档,可以直接去文档查看
-2. setting 文件中大部分模块都不常使用,对于大部分模块了解其作用即可
-3. 初始 setting 文件中的模块默认为关闭状态(被注释)
-4. 全局变量的创建直接在 setting 文件中赋值即可,使用则可通过 from 项目名.settings import XXX
-5. 全局变量也可以通过 self.settings["变量名"] 获取,通过 self.settings.get("变量名","传入的值") 赋值
-6. pipelines 文件就用 spider.settings.get("变量名") 
  1. logging格式
  • scrapy
    • settings中设置 LOG_LEVEL = “WARNING”
    • settings中设置 LOG_FILE = “./a.log” #设置日志保存的位置,设置会后终端不会显示日志内容
    • import logging , 实例化 logger 的方式在任何文件中使用logger输出内容
  • 普通项目中
    • import logging
    • logging.basicConfig(…) #设置日志输出的样式,格式
    • 实例化一个 ‘logger = logging.getLogger(name)’
    • 在任何 py 文件中调用 logger 即可

Pipeline 模块

  • 使用方法
open_spider(self, spider)   / 开启爬虫时执行,仅执行一次(连接数据库等需要预加载的东西)
close_spider(self, spider)  / 关闭爬虫时执行,仅执行一次
process_item(self, item, spider)   / 若函数内没有 return ,另一个权重较低的pipeline就不会获取到该 item

Middleares 中间件模块

  1. 使用方法:编写一个Downloader Middlewares和我们编写一个pipeline一样,定义一个类,然后再setting中开启

    Downloader Middlewares默认的方法:
    	process——request(self, request, spider):
    		当每个request通过下载中间件时,该方法被调用
    	process_respinse(self, request, response, spider):
    		当下载器完成http请求,传递响应给引擎的时候调用
    
  2. settings 文件中需要开启 DOWNLOADER_MIDDLEWARES 和 SPIDER_MIDDLEWARES(包括位置和权重–距离引擎的距离,表示数据是否会越先经过)

  3. 自定义MiddleWares

    1.把多个UA放入settings文件中,赋值给 USER_AGENTS
    2.模板中的middlewares文件里面的代码时没有什么用处的,可以全部删除再自定义
    3.MiddleWares主要两个用处(自定义: UA池 / 代理IP池)
    ------>添加自定义的UA,给request的headers赋值即可
    class RandomUserAgent (object): 
    	def process_request(self, request, spider): 			
    		useragent = random.choice(USER_AGENTS) 
    		request.headers["User-Agent"] = useragent
    ----->添加代理,需要在request的meta信息中添加proxy字段
    class ProxyMiddleware(object):
    	def process_request(self, request, spider):						 
    		reqeust.meta["proxy"] = "http://124.115.126.76:808"
    		---->代理的形式为:协议 + ip地址 + 端口 
    4.Downloader_Middlewares默认方法:
    	process_request(self, request, spider) ->当每个request通过下载中间件时,该方法被调用
    	process_response(self, request, response, spider) ->当下载器完成http请求时,传递响应给引擎时调用 	
    
  4. 若程序报错must return Response or Request ,got <class ‘NoneType’>则需要加上一下其一:

    • return response:返回的响应通过引擎传递给爬虫
    • return request:返回的请求通过引擎传递给调度器
  5. 对于Middlewares,如果还有问题,可以参考官方文档 – Downloader Middleware / 官方文档 – Spider Middleware

  6. process_request 不需要return;process_response才需要

requests请求翻页

  • 思路
  • 找到下一页url地址
  • 调用 requests.get(url) —— 传递给调度器
  • scrapy 中操作
  • 组装称一个requests对象再传给引擎
  • scrapy.Request 能构建一个requests,同时指定提取数据的 callback 函数
    # 找到下一页的 url 地址
    next_url = response.xpath("xpath_expression").extract_first()
    if next_url != "结束判断条件":
    	next_url = "基础url" + next_url  #获取下一页url 
    	# 构造一个请求,请求->引擎->调度器->引擎->下载器->响应 下一页爬取
    	yield scrapy.Request(
    		next_url,
    		# 若获取下一页内容采用同一函数(parse),则继续调用 自身 获取
    		# 若获取下一页内容采用不同函数(parse1),则直接调用 parse1 获取
    		callback = self.parse()  
    
    	) 
    
  • scrapy.Request() 可选参数
    scrapy.Request( url [, callback, method='GET', headers, body, cookies,meta, dont_filter = False] )
    
  • callback : 指定传入的 url 交给哪个解析函数去处理
  • meta : 实现再不同的解析函数中传递数据,meta默认会携带部分信息,比如下载延迟,请求深度等
  • dont_filter : 让scrapy的去重不会过滤当前url(scrapy 默认会有url去重功能)

Scrapy中的CrawlSpider

  • 简介
    在这里插入图片描述
  • 生成 crawl spider 命令
scrapy genspider -t crawl demo baidu.com
  • 生成文件代码解析
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule


class DemoSpider(CrawlSpider):  # 继承了CrawlSpider父类
    name = 'demo'
    allowed_domains = ['baidu.com']
    start_urls = ['http://baidu.com/']

    # 定义提取url地址规则
    rules = (
        # rules   可定义多个
        # LinkExtractor   连接提取器----提取url地址
        # callback   提取出来的url地址的response会交给callback处理
        # follow   当前url的响应是否重新经过rules来提取url地址
        # callback和follow   可自己选择需不需要写
        Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
    )

    # parse函数有特殊功能,不能定义
    def parse_item(self, response):
        item = {}
        # item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()
        # item['name'] = response.xpath('//div[@id="name"]').get()
        # item['description'] = response.xpath('//div[@id="description"]').get()
        return item
        
  1. 注意点
  • 即使在 allow 中 url 不完整,CrawlSpider 也会自动的补充完整再请求
  • 不能再有以 parse 为名字的数据提取方法,这个方法已被 CrawlSpider 使用
  • 一个 Rule 对象接收很多参数,首先第一个是报假案url规则的 LinkExtractor 对象,常用的还有callback(制定满足规则的 url 的解析函数的字符串)和 follow( response 提取的链接是否需要跟进)
  • 不指定 callback 函数的请求下,如果 follow 为 True ,满足该入了的俩还会继续被请求
  • 如果多个 Rule 都满足某一个 url,会从 rules 中选择第一个满足的进行操作
  • LinkExtractor更多常见参数:
    allow: 满足括号中"正则表达式"的URL会被提取,如果为空,则全部匹配
    deny: 满足括号中“正则表达式"的URL一定不提取( 优先级高于allow )
    allow_domains: 被提取的链接的 domains.
    deny_domains: 一定不会被提取链接的 domains.
    restrict_xpaths: 使用 xpath表达式,和 allow 共同作用过滤链接,级 xpath 满足范围内的 url 地址会被提取
  • spiders. Rule常见参数:
    link_extractor: 是一个 Link Extractor 对象,用于定义需要提取的链接。
    callback: 从 link_extractor 中每获取到链接时,参数所指定的值作为回调函数
    follow: 是一个布尔( boolean )值,指定了根据该规则从 response 提取的链接是否需要跟进
    如果 callback 为 None , follow 默认设置为 True , 否则默认为 False
    process_links: 指定该 spider 中哪个的函数将会被调用,从 link_extractor 中获取到链接列表时将会调用该函数,该方法主要用来过滤 url
    process_request: 指定该 spider 中哪个的函数将会被调用,该规则提取到每个 request 时都会调用该函致,用来过滤 request

Scrapy模拟登录之携带cookie登录

  • 应用场景
  1. cookie过期时间很长,常见于一些不规范的网站
  2. 能在cookie过期之前把搜有的数据拿到
  3. 配合其他程序使用,比如其使用selenium把登陆之后的cookie获取到保存到本地, scrapy发送请求之前先读取本地cookie
  • 如何实现
    我们定义在spider下的start_urls = [] 都是默认交给 start_requests 处理的,所以如果有必要,可以重写 start_requests 方法
    Spider中相关定义
  • 代码编写
  1. cookie 字符串转化为字典:
    cookie = {i.split("=")[0]:i.split("=")[1] for i in cookies.split("; ")}
    
  2. 使用
    def start_requests(self):
    cookies = "..."
    cookies = {i.split("=")[0]:i.split("=")[1] for i in cookies.split("; ")}
    yield scrapy.Request(
        self.start_urls[0],
        callback=self.parse,
        cookies=cookies
    )
    
  3. 若在 settings 文件中关闭了 COOKIES_ENABLED ,后续的页面请求也会带上传递进去的cookies
  4. 也可以在 settings 文件中添加 COKIES_DEBUG = True 以查看 cookies 去向
  5. cookies不能放在 headers 中去请求,否则无效;直接放在 scrapy.Request 中就能获取到cookie

Scrapy模拟登录之发送post请求

  1. scrapy.Request发送的是get请求;scrapy.FormRequest发送post请求(同时使用formdata来携带post数据)
  2. 通过发送cookie中的 form_data 数据进行提交(根据网站的Form Data来填写)
    def parse(self, response):
      authenticity_token = response.xpath("xpath_expression").extract_first()
      utf8 = response.xpath("xpath_expression").extract_first()
      commit = response.xpath("xpath_expression").extract_first()
      post_data = dict(
          login="login_name",
          password="login_password",
          authenticity_token=authenticity_token,
          utf8=utf8,
          commit=commit            
      )
      yield scrapy.FormRequest(
          "html_url",
          formdata=post_data,
          callback=self.after_login
      )
    
      类似于:
      yield scrapy.Request(
      	url,
      	method="POST",
      	body = {   }
      )
    
  3. 利用Scrapy中自动找到action中的url和form表单的功能进行账号和密码的填写进行登录操作
    def parse(self, response):
        yield scrapy.FormRequest.from_response(
            response, #自动从response中寻找form表单
            formdata={"login":"login_name","password":"your_password"},
            callback=self.after_login
        )
    
    TIPS:
    • 如果有多个form表单,可通过加入formname,formid,formnumber,formxpath来选择不同的form表单

scrapy_redis 相关

  1. 分布式爬虫(多台机器协作)的关键是:共享爬取队列,爬取队列在主机维护,各从机调度器统一从共享队列获取Request,即主机维护爬取队列,从机负责数据抓取、数据处理、数据存储;

  2. Redis 是非关系型数据库(Key – Value形式存储),结构灵活;是内存中数据结构存储系统,处理速度快,性能好;提供队列、集合等多重存储结构,方便队列维护

  3. scrapy_redis 在 scrapy 基础上实现了更多、更强大的功能,具体体现在:request去重爬虫持久化轻松实现分布式

  4. 如何实现去重:借助Redis集合,在集合中存储每个Request的指纹。在向Request队列中加入Request之前先验证这个Request的指纹是否已经在集合中:
    (1)已存在则不添加到队列
    (2)不存在则添加入队列并将指纹加入集合

  5. 如何防止中断(实现持久化):在每台从机Scrapy启动时都会首先判断Redis Request队列是否为空:
    (1)不为空则从队列中取得下一个Request执行爬取
    (2)为空则重新开始爬取,第一台从机执行爬取向队列中天下Request

  6. scrapy-redis 下载网址(GitHub)

  7. scrapy_redis的爬虫流程:
    scrapy_redis的爬虫流程

  8. Redis中存在爬过的(用来去重)以及待爬的(可实现断点续爬)指纹集合

动态网站数据抓取

  • 一般抓取动态网站,页面展示出来的并不一定是你能够抓取到的,因此,需要分析 Get 请求和返回 Response 对象,仔细找到后台管理返回的数据,再去抓取。
  • 一般通过浏览器调试功能中的 Network 抓包去找到数据文件再去分析

列表生成式:

list = [i for I in list if len(i)>0 ]   # 对于 list 中的每一项元素 i ,只取长度大于0的元素
list = [re.sub("\xa0|\s","",i) for i in list ]  # 对于 list 中的每一项元素i,都把\xa0或\s替换成空字符串(通过re库完成替换操作)

extract() 和 extract_first() 的区别:

如果一个标签下面还有很多个标签

  • 用 extract() 方法能获取到所有的元素,并组成一个列表
  • 用 extract_first() 方法就多做了一步:将列表中的第一个元素取出,在作用上相当于 extract()[0]

yield 生成器

  • 待补充

selector 选择器

  • 待补充

实战例子01 ---- 爬取苏宁易购图书

  1. 步骤

貌似网站已挂,第一个实战项目,以步骤为主
相关代码以截图为主,以供参考

  • 爬取信息:书名,大分类,小分类,价格,书的图片封面,作者,出版社,简介
  • 分组:大分类 → 小分类 → 列表 → 翻页 → 书本信息 → 价格
  • 寻找初始页面:能翻页的大概率为动态页面,打开控制台,找到Network,判断当前页面是否存在需要的数据,比较简单的方法就是直接查找页面中数据
  • 创建爬虫项目(start_project)创建爬虫文件(genspider)
  • 寻找URL列表:利用 yield 语句传递 url 提取数据
  • 进入信息页面:找到图书信息所在标签,利用 xpath 表达式获得数据
  • 获取价格数据:当数据不在 html 的标签中时用 正则表达式 提取数据
  • 翻页:翻页的数据通过 js 生成(html中没有响应数据),同时 yield 中传出meta与上文 parse_book_list 函数传入的 item 可通过 deepcopy 隔离,即此处的 meta 仅有大分类,小分类和小分类url地址,三项数据;因此上文中的传入item提取则可以通过 deepcopy 隔离提取,让原来的item并没有发生什么变化,这样也能使数据传递更加简单

相当于获得小分类url地址进入列表信息时需要通过翻页操作来获得更多列表项,传至下一页的item只是键值对变得更多了(添加了book_name等),其实影响并不大,因为会被覆盖,但是为了更加的优化,则通过deepcopy隔离开,让函数传入的item并没有增多键值对再传出。



修改后代码为:
在这里插入图片描述

  1. 问题

列表打印后会有元素为空

  • 可能是某一些的数据不在定位的标签内
  • 因此需要进行判断:若为空则使用另一个表达式

列表中有元素重复

  • 因为 scrapy 框架是多线程的,一个爬虫文件内的多个 parse 函数之间可能会同时进行,而 item 在数据还没 保存/打印 之前,若进入了下一个循环,会导致数据被覆盖,就会在最后进行数据 保存/打印 时重复显示,我们看到的数据就会出现重复的数据
  • 解决方法:
    头导入包:from copy import deepcopy
    在 yield 操作时要进行 deepcopy 操作,这样当后一个更改数据时不会影响其他的 item
    yield Scrapy.Request(
    [“s_href”],
    callback=self.parse_book_list,
    {“item”: deepcopy(item) }
    )

实战例子02 ---- 京东爬虫

  • 待更新

实战例子03 ---- 当当爬虫

  • 待更新

实战例子04 ---- 亚马逊爬虫

  • 待更新
发布了27 篇原创文章 · 获赞 12 · 访问量 5197

猜你喜欢

转载自blog.csdn.net/Kobe_k/article/details/98212591