Scrapy学习_01

在这里插入图片描述

  • Engine: 引擎,处理整个系统的数据流处理、触发事务,是整个框架的核心。
  • item: 项目,定义爬取结果的数据结构,爬取的数据会被复制成该 item 对象。
  • Scheduler: 调度器,接收引擎发过来的请求,并将其加入队列中,在引擎再次请求的时候将请求提供给引擎。
  • Downloader: 下载器,下载网页内容,并将网页内容返回给蜘蛛。
  • Spiders: 蜘蛛,其内定义了爬取的逻辑和网页的解析规则,它主要负责解析响应并生成提取结果和新的请求。
  • Item Pipline: 项目管道,负责处理有蜘蛛从网页中抽取的项目,它的主要任务是清晰、验证和存储数据。
  • Downloader Middlewares: 下载器中间件,位于引擎和下载器之间的钩子框架,主要处理引擎与下载器之间的请求及响应。
  • Spider Middlewares: 蜘蛛中间件,位于引擎和蜘蛛之间的钩子框架,主要处理蜘蛛输入的响应和输出的结果及新的请求。

抓取流程:

  1. Engine 打开一个网站,找到处理该网站的 Spider , 并向该 Spider请求第一个要爬取的 url 。
  2. Engine 从 Spider 中获取到第一个要爬取的 Url ,并通过 Scheduler 以 request 的形式调度。
  3. Engine 向 Scheduler 请求下一个要爬取的Url。
  4. Scheduler 返回下一个要爬取的Url 给 Engine, Engine 将 Url 通过 Downloader Middlewares 转发给Downloader 下载。
  5. 页面下载完毕后,Downloader 生成该页面的response , 并将其通过 Downloader Middlewares 转发给Engine。
  6. Engine 从下载器中接收到response ,并将其通过Spider Middlewares 发送给Spider 处理。
  7. Engine 处理 response , 返回爬取到 的Item 及新的Request 给 Engine。
  8. Engine 将 Spider 返回的Item 给Item Pipeline, 将新的 Request 给 Scheduler。
  9. 重复 第 2 步 到第 8 步,直到 Schrduler 中没有 更多的 request , Engine 关闭该网站,爬取结束。

我的理解
在这里插入图片描述

scrapy 创建命令

scrapy 查看可用命令
scrapy startproject 项目名称 创建项目
scrapy genspider 蜘蛛名称 需要抓取的网站域名 创建起始spider
scrapy shell 网站链接 在抓取时可用 shell 命令进行测试
备注:创建完成项目后需要使用 cd 命令进入项目目录,然后再创建 spider

scrapy 项目文件结构:

在这里插入图片描述

  • scrapy.cfg: Scrapy 项目的配置文件,其内定义了项目的配置文件路径、部署相关信息等内容。
  • items.py: 定义 Item 数据结构,所有的Item的定义都可以放在这里。
  • pipelines.py: 定义 Item Pipeline 的实现。
  • setting.py: 定义项目的全局配置。
  • middlewares.py: 定义 Spider Middlewares 和 Downloader Middlewares 的实现。
  • spiders: 其内包含一个个的 Spider 的实现。

scrapy 创建一个项目流程:

windows + R # 打开运行框 输入 cmd 回车 打开小黑框

scrapy startproject tutorial # 生成 scrapy 项目

cd tutorial # 进入项目文件夹

scrapy genspider quotes quotes.toscrape.com # 创建 Spider

运行完 第八行的代码后,就会在 spiders 文件夹中生成一个 quotes.py 文件,就是我们刚刚创建的 Spider

Item Pipeline 的实现:

定义一个类(class),并实现 process_item()方法。process_item()方法必须返回包含数据的字典或Item对象,或者抛出 DropItem 异常。

# 修改 pipelines.py 文件
# 删除掉命令行自动生成的文件内容,增加一个 TexePipeline 类(类名自定义),内容如下
# TextPipline 类实现的是数据的处理
from scrapy.exceptions import DropItem

class TextPipline(object):
    def __init__(self):
        self.limit = 50
        
   def process_item(self, item, spider):
       if item["text]:
           if len(item["text"]) > self.limit:
               item["text"] = item['text'][0:self.limit].rstrip() + '...'
           return item
       else:
           return DropItem("Missing Text")

本段代码在构造方法中定义了限制长度为 50 , 实现了 process_item() ,其参数是 item 和 spider
。首先该方法判断 item 的 text 属性是否存在,如果不存在,则抛出 DropItem 异常,
如果存在,再判断长度是否大于50,如果大于50,就执行截断字符串拼接省略号操作,再将item 返回。

# 在上一段的代码后面继续写
# 本段代码实现将数据存到 MongoDB 中

import pymongo

class MongoPipeline(object):
    def __init__(self, mongo_uri, mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db

    @classmethod
    def from_crawler(cls, crawler):
        return cls(mongo_uri = crawler.settings.get("MONGO_URI"),
                   mongo_db = crawler.settings.get("MONGO_DB")
                   )

    def open_spider(self, spider):
        self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client[self.mongo_db]

    def process_item(self, item, spider):
        name = item.__class__.__name__
        self.db[name].insert(dict(item))
        return item

    def close_spider(self, spider):
        self.client.close()

from_crawler:是一个类方法,用 @classmethod 标识,是一种依赖注入的方式。它的参数就是 crawler ,通过
crawler 我们可以拿到全局配置的每个配置信息。在全局配置 setting.py 。中,我们可以定义 MONGO_URI 和
MONGO_DB 来制定 MongoDB 链接需要的地址和数据库名称,拿到配置信息之后返回类对象即可,所以这个方法主要用来获取
setting.py 中的配置信息的。

open_spider: 当 Spider 开启时,这个方法就会被调用,主要用于进行一些初始化操作。

close_spider: 当 Spider 关闭时,这个方法会被调用,主要用于将数据库链接关闭等操作。

process_item:执行数据插入操作。

定义好 TextPipline 和 MongoPipeline 这两个类后,需要在 setting.py 中启用它们。 需要在 setting.py 中加入如下内容:

 ITEM_PIPELINES = {
     'tutorial.pipelines.TextPipline': 300,
     'tutorial.pipelines.MongoPipeline ': 400, }

Spider 类分析

	通过命令创建的 scrpay 项目均是继承自 scrapy.spiders.Spider 。这个类是最简单最基本的 Spider 类,其他Spider 必须继承这个类。

	scrapy.spiders.Spider 类提供了 start_requests() 方法的默认实现,读取并请求 start_urls 属性,并根据返回的结果调用 parse() 方法姐结果。
	
	它还有以下一些基础属性:
			name:爬虫名称,是定义 Spider 名称的字符串, Spider 的名字定义了 Scrapy 如何定位并初始化 Spider , 它必须是唯一的, 我们可以生成多个相同的Spider 实例,数量无限制,但是Name 必须是唯一的。
			allowed_domains:允许爬取的域名,是可选配置,不在此范围内的链接不会被跟进爬取。
			start_urls:起始Url 列表,当我们没有实现start_requests() 方法时,默认会从这个列表开始抓取。
			costom_setting:字典格式,是专属于本Spider 的配置,此设置和覆盖项目全局的设置。此设置必须在初始化钱被更新,必须定义成类变量。
			crawler:它是由 from_crawler() 方法设置的,代表的是本spider 类对应的Crawler 对象。Crawler 对象包含了很多项目组件,利用它我们可以获取项目的一些配置信息,如最常见的获取项目的设置信息,即 Settings。
			settings:它是一个 Settings 对象,利用它我们可以直接获取项目的全局设置变量。

Spider 常用方法:

	start_requests() :此方法用于生成初始请求,它必须返回一个可迭代对象,此方法会默认使用 start_urls 里面的 url 来构造 Request ,而 Request 是GET 请求方式,如果我们像在启动时以POST 方式访问某个站点,可以重写这个方法,发送 POST 请求时使用 FromRequest 即可。
	parse() :当 response 没有指定回调函数时,该方法会默认被调用。它负责处理 response ,处理返回结果,并从中提取想要的数据和下一步的请求,然后返回。该方法需要返回一个包含 Request 或者 Item 的可迭代对象。
	close() : 当Spider 关闭时,该方法会被调用,在这里一般会定义释放资源的一些操作或者其他收尾操作。

Downloader Middlewares 的用法

可实现的功能:

  • 修改 User-Agent
  • 处理重定向
  • 设置代理IP
  • 时报重试
  • 设置Cookies …

Downloader Middlewares 的核心方法

  • process_request(request, spider)
  • process_response(request, response, spider)
  • process_exception(request, exception, spider)

process_request(request, spider)

	Request 被 Scrapy 引擎调度给 Downloader 之前 process_request() 方法就会被调用,也就是在 Request 从队列里调度出来到 Downloader 下载执行之前,我们都可以用 process_request() 方法对 Request 进行处理。方法的返回值必须为 None、Response 对象、 Request 对象之一,或者抛出IgnoreRequest 异常。

process_request()

	process_request() 方法的参数有如下两个:
	request:是Request 对象,即被处理的 Request。
	spider:是 Spider 对象,即此Request 对应的Spider。

返回类型不同,产生的效果也不同:

  • 当返回 None 时, Scrapy 将继续处理该 Request ,接着执行其他 Downloader Middlewares 的 process_request() 方法,一直到 Downloader 把 Request 执行后得到 Response 才结束,这个过程起始就是修改 Request 的过程,不同的 Downloader Middlewares 按照设置的优先级顺序一次对 Request 进行修改,最后送至 Downloader 执行。
  • 当返回 Response 对象时, 更低优先级的 Downloader Middlewares 的 process_request() 和 process_exception() 方法就不会被继续调用,每个 Downloader Middlewares 的 process_response() 方法被一次调用。调用完毕后,直接将 Response 对象发送给 Spider 来处理。
  • 当返回为Request对象时,更低优先级的Downloader Middleware的process_request()方法会 停止执行。这个Request会重新放到调度队列里,其实它就是一个全新的Request,等待被调 度。如果被 Scheduler 调度了,那么所有的 Downloader Middleware 的 process_request()方法 会被重新按照顺序执行。
  • 如果 IgnoreRequest 异常抛出,则所有的 Downloader Middleware 的 process_exception()方法 会依次执行。如果没有一个方法处理这个异常,那么Request的errorback()方法就会回调。 如果该异常还没有被处理,那么它便会被忽略。

process_response()

	process_response()方法的参数有如下三个。
	Request,是 Request 对象,即此 Response对应的 Request。
	response,是 Response 对象,即此被处理的 Response。
	spider,是 Spider 对象,即此 Response对应的 Spider。

返回类型不同,产生的效果也不同:

  • 当返回为Request对象时,更低优先级的Downloader Middleware的process_response()方法 不会继续调用。该Request对象会重新放到调度队列里等待被调度,它相当于一个全新的 Requesto 然后,该 Request 会被 process_request()方法顺次处理。
  • 当返回为Response对象时,更低优先级的Downloader Middleware的process_response()方法 会继续调用,继续对该Response对象进行处理。
  • 如果IgnoreRequest异常抛出,贝0 Request的errorback。方法会回调。如果该异常还没有被处 理,那么它便会被忽略。

process_exception ()

process_exception ()方法的参数有如下三个。

	request,是Request对象,即产生异常的Request。 
	exception,是Exception对象,即抛出的异常。
	spdier,是 Spider 对象,BP Request对应的 Spider。

下面归纳一下不同的返回值。

  • 当返回为None时,更低优先级的Downloader Middleware的process_exception()会被继续顺 次调用,直到所有的方法都被调度完毕。
  • 当返回为 Response 对象时,更低优先级的 Downloader Middleware 的 process_exception()方 法不再被继续调用,每个Downloader Middleware的process_response()方法转而被依次调用。
  • 当返回为Request对象时,更低优先级的Downloader Middleware的process_exception()也不 再被继续调用,该Request对象会重新放到调度队列里面等待被调度,它相当于一个全新的 Requesto然后,该Request又会被process_request()方法顺次处理。

Downloader Middlewares 实现随机 User-Agent

在 middlewares.py中 添加一个新的类,

import random

class RandomUserAgentMiddleware():
    def __init__(self):
        self.user_agent = [
            'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.11) Gecko/2009060310 Linux Mint/6 (Felicia) Firefox/3.0.11',
            'Mozilla/5.0 (Windows NT 6.1; rv:2.0b6pre) Gecko/20100903 Firefox/4.0b6pre Firefox/4.0b6pre',
            'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.15 (KHTML, like Gecko) Chrome/24.0.1295.0 Safari/537.15'
        ]

    def process_request(self, request, spider):
        request.headers["User-Agent"] = random.choice(self.user_agent)

在setting.py 中,将DOWNLOADER_MIDDLEWARES 取消注释并设置为以下内容:

DOWNLOADER_MIDDLEWARES = {
       '项目名称.middlewares.RandomUserAgentMiddleware': 543,
}

Scrapy 对接 Selenium

from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from scrapy.http import HtmlResponse
from logging import getLogger

class SeleniumMiddleware():
    def __init__(self, timeout=None, service_args=[]):
        self.logger = getLogger(__name__)
        self.timeout = timeout
        self.browser = webdriver.PhantomJS(service_args=service_args)
        self.browser.set_window_size(1400, 700)
        self.browser.set_page_load_timeout(self.timeout)
        self.wait = WebDriverWait(self.browser, self.timeout)

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

    def process_request(self, request, spider):
        """
        用 PhantomJS 抓取页面
        :param request:
        :param spider:
        :return:
        """
        self.logger.debug('PhantomJS is Stsrting')
        page = request.meta.get('page', 1)
        try:
            self.browser.get(request.url)
            if page > 1:
                input = self.wait.until(
                    EC.presence_of_all_elements_located((By.CSS_SELECTOR, '#mainsrp-pager dic.form > input')))
                submit = self.wait.until(
                    EC.element_to_be_clickable((By.CSS_SELECTOR, "#mainsrp-pager div.form > span.btn.J_Submit")))
                input.clear()
                input.send_keys(page)
                submit.click()
            self.wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '#mainsrp-pager li.item.active > span'), str(page)))
            self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.m-itemlist .items .item')))
            return HtmlResponse(url=request.url, body=self.browser.page_source, request=request, encoding='utf-8', status=200)
        except TimeoutException:
            return HtmlResponse(url=request.url, status=500, request=request)

    @classmethod
    def from_crawler(cls, crawler):
        return cls(timeout=crawler.settings.get("SELENIUM_TIMEOUT"),
                   service_args=crawler.settings.get("PHANTOMJS_SERVICE_ARGS"))
	首先我们在 __init__() 里对一些对象进行初始化,包括PhantomJS. WebDriverWait等对象,
同时 设置页面大小和页面加载超时时间。在process_request()方法中,我们通过Request的meta属性获取当前
需要爬取的页码,调用PhantomJS对象的get()方法访问Request的对应的URL。这就相当于 从Request对象里获取
请求链接,然后再用PhantomJS加载,而不再使用Scrapy里的Downloader。

	随后的处理等待和翻页的方法在此不再赘述,和前文的原理完全相同。最后,页面加载完成之后, 
我们调用PhantomJS的page_source属性即可获取当前页面的源代码,然后用它来直接构造并返回一个
HtmlResponse对象。构造这个对象的时候需要传入多个参数,如url. body等,这些参数实际上就是它的基础属性。
可以在官方文档査看HtmlResponse对象的结构:
	https://doc.scrapy.org/en/latest/topics/ request-response.htmlo
	这样我们就成功利用PhantomJS来代替Scrapy完成了页面的加载,最后将 Response返回即可。
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: `CONCURRENT_REQUESTS_PER_DOMAIN` 是 Scrapy 中一个设置并发请求数量的参数,它指定了同一域名下的并发请求数量。默认情况下,它的值为 8,意味着在同一时间内,Scrapy 最多会发送 8 个请求到同一域名下。如果需要调整该参数,可以在 Scrapy 的配置文件中设置: ```python CONCURRENT_REQUESTS_PER_DOMAIN = 16 # 将并发请求数量设置为 16 ``` 需要注意的是,同时也可以设置 `CONCURRENT_REQUESTS_PER_IP` 参数,它指定了同一 IP 地址下的并发请求数量。默认情况下,它的值为 0,表示不限制并发请求数量。但是在某些情况下,服务器可能会对同一 IP 地址下的访问频率进行限制,因此需要适当调整该参数。 ### 回答2: scrapy中的CONCURRENT_REQUESTS_PER_DOMAIN是指在同一个域名下同时发送的请求的数量限制。通过设置CONCURRENT_REQUESTS_PER_DOMAIN,可以控制爬虫同时访问同一域名下页面的并发请求数量,从而限制爬虫对目标网站的访问频率。 这个设置参数可以在爬虫的settings.py文件中进行配置。默认情况下,CONCURRENT_REQUESTS_PER_DOMAIN的值为8,即一个域名下最多同时发送8个请求。 通过调整CONCURRENT_REQUESTS_PER_DOMAIN的值,可以控制对目标网站的访问速度和并发数。如果设置的值较大,爬虫可以更快地抓取数据,但也会带来更高的对目标网站的访问压力和风险。而如果设置的值较小,访问速度会较慢,但可以更好地保护爬虫不被目标网站封禁。 在实际应用中,可以根据目标网站的反爬策略、自身机器的性能以及所需的数据抓取速度来配置CONCURRENT_REQUESTS_PER_DOMAIN的值。例如,对于反爬策略比较严格的网站,可以将该值设置为较小的数值,以减少被封禁的风险;而对于性能强大的服务器和较宽松的反爬策略的网站,可以适当增加该值,提高爬虫的抓取效率。 总之,通过设置CONCURRENT_REQUESTS_PER_DOMAIN参数,可以有效控制爬虫对目标网站的并发请求数量,以达到更好的抓取效果和保护爬虫的目的。 ### 回答3: Scrapy的CONCURRENT_REQUESTS_PER_DOMAIN是一个设置并发请求的参数,它控制着每个域名下的并发请求的数量。换句话说,它决定了Scrapy能够同时发送给同一域名的请求数量。 通过设置CONCURRENT_REQUESTS_PER_DOMAIN参数,可以控制对同一域名的同时请求数量,从而避免对目标网站造成过大的压力或被封IP的风险。这个参数的默认值是8,这意味着Scrapy可以同时发送8个请求给同一域名。 但是,需要注意的是,该参数并不限制对不同域名的并发请求数量。如果需要限制整个应用程序的并发请求数量,可以使用CONCURRENT_REQUESTS参数来控制。 合理设置CONCURRENT_REQUESTS_PER_DOMAIN参数有助于平衡Scrapy爬虫的速度和稳定性。如果将该值设得过高,可能会对目标网站造成过大的压力导致请求失败;而将该值设得过低,则可能会降低爬取效率。 在实际应用中,我们可以根据目标网站的反应速度和服务器负载情况来合理设置CONCURRENT_REQUESTS_PER_DOMAIN参数。可以先尝试将其设为较低的值,观察爬虫的稳定性和目标网站的反应情况,再进行调整。 总之,通过合理设置CONCURRENT_REQUESTS_PER_DOMAIN参数,可以在一定程度上控制Scrapy爬虫对同一域名的并发请求数量,从而提高爬取效率、避免对目标网站造成过大压力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宿夏星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值