当前位置: 首页 > news >正文

scrapy_yield详解


一、yield 的核心作用

1. 生成器(Generator)特性

yield 是 Python 生成器的核心关键字,允许函数暂停执行并返回中间结果,后续可从暂停处继续执行。
• 在 Scrapy 中,这种特性被用来按需生成请求或数据,而不是一次性生成所有内容,节省内存。

2. 异步调度机制

• Scrapy 的引擎会循环处理 Spider 通过 yield 提交的请求或数据。
• 每当 yield 一个 Request 对象时,引擎会将其加入调度队列,等待下载器处理;当 yield 一个 Item 对象时,引擎会将其传递给 Pipeline 处理。


二、yield 在 Spider 中的典型用途

1. 生成请求(Request)
import scrapy

class MySpider(scrapy.Spider):
    name = 'example'
    start_urls = ['http://example.com']

    def parse(self, response):
        # 生成新的请求
        next_page = response.css('a.next-page::attr(href)').get()
        if next_page:
            yield scrapy.Request(url=next_page, callback=self.parse)

关键点yield scrapy.Request() 会将新请求加入调度队列,Scrapy 引擎会异步处理这些请求。
callback 参数指定处理该请求响应的回调函数(默认为 parse)。

2. 生成数据项(Item)
import scrapy

class MyItem(scrapy.Item):
    title = scrapy.Field()
    content = scrapy.Field()

class MySpider(scrapy.Spider):
    name = 'example'
    start_urls = ['http://example.com']

    def parse(self, response):
        # 提取数据并生成 Item
        item = MyItem()
        item['title'] = response.css('h1::text').get()
        item['content'] = response.css('div.content::text').get()
        yield item  # 将数据传递给 Pipeline

关键点yield item 会将数据项传递给 Item Pipeline,进行后续处理(如清洗、存储)。

3. 组合使用请求和数据
def parse(self, response):
    # 提取当前页数据
    yield MyItem(...)

    # 生成下一页请求
    yield scrapy.Request(...)

• 可以在同一个方法中混合 yield 请求和数据,Scrapy 会分别处理。


三、yield 的优势

  1. 非阻塞异步处理
    • 不需要等待一个请求完成后再处理下一个,适合大规模爬取。
  2. 内存高效
    • 按需生成请求和数据,避免一次性加载所有内容到内存。
  3. 代码简洁性
    • 避免手动管理复杂的回调链。

四、常见问题与注意事项

  1. 忘记 yield
    • 如果直接 return 一个 RequestItem,Scrapy 将无法处理它,必须使用 yield

  2. 生成器单次迭代
    • 生成器只能遍历一次,但在 Scrapy 中通常不会直接操作生成器,框架已处理了调度。

  3. 调试技巧
    • 如果发现请求未执行或数据未生成,检查是否漏掉了 yield


五、完整示例

import scrapy
from myproject.items import ArticleItem

class NewsSpider(scrapy.Spider):
    name = 'news'
    start_urls = ['http://news.example.com']

    def parse(self, response):
        # 提取文章链接
        for article_url in response.css('a.article-link::attr(href)').getall():
            yield scrapy.Request(
                url=response.urljoin(article_url),
                callback=self.parse_article
            )

        # 分页处理
        next_page = response.css('a.next-page::attr(href)').get()
        if next_page:
            yield scrapy.Request(url=response.urljoin(next_page), callback=self.parse)

    def parse_article(self, response):
        # 生成数据项
        item = ArticleItem()
        item['title'] = response.css('h1.headline::text').get()
        item['author'] = response.css('div.author::text').get()
        item['content'] = response.xpath('//div[@class="article-body"]//text()').getall()
        yield item

通过合理使用 yield,你可以构建出高效、可维护的 Scrapy Spider,轻松应对复杂的网页抓取场景。

六、回调函数

在进一步的请求yield scrapy.Request(url=response.urljoin(next_page), callback=self.parse)

中,Request里有两个参数,通过 yield 来发起一个请求,并通过 callback 参数为这个请求添加回调函数,在请求完成之后会将响应作为参数传递给回调函数,scrapy框架会根据 yield 返回的实例类型来执行不同的操作,a. 如果是 scrapy.Request 对象,scrapy框架会去获得该对象指向的链接并在请求完成后调用该对象的回调函数。b. 如果是 scrapy.Item 对象,scrapy框架会将这个对象传递给 pipelines.py做进一步处理。

生成器

yield 的作用就是把一个函数变成一个生成器(generator),带有yield的函数不再是一个普通函数,Python解释器会将其视为一个generator,单独调用(如fab(5))不会执行fab函数,而是返回一个 iterable 对象!
在for循环执行时,每次循环都会执行fab函数内部的代码,执行到yield b时,fab函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。参考实例如下:

def fab(max):
   n, a, b = 0, 0, 1
   while n < max:
      # print b
      yield b
      # print b
      a, b = b, a + b
      n = n + 1
print(fab(5))  # 输出:<generator object fab at 0x00000000069D8A68>
for n in fab(5):
    print n    # 依次1,1,2,3,5
#对于含有yield的函数,外部要以迭代的方式调用,当函数执行结束时,generator 自动抛出 StopIteration 异常,表示迭代完成。
# 在 for 循环里,无需处理 StopIteration 异常,循环会正常结束。

def ff(max):
   a,b = 0,1
   yield max  # yield不在循环中,这里已经到函数最后所以直接返回,相当于return
for n in ff(5):
   print n    # 输出:5

综上可知,yield要使用在循环中,这样生成器才有使用的意义。

那么迭代器与生成器又有什么区别呢?可以看我的这篇文章:

迭代器与生成器区别

相关文章:

  • JSON 简介
  • COBOL语言的安全开发
  • Spring Boot(十六):拦截器Interceptor
  • Android7 Input(一)Android Input服务初始化
  • anythingLLM结合searXNG实现联网搜索
  • LeetCode Hot 100 - 子串 | 560.和为K的子数组、239.滑动窗口最大值、76.最小覆盖子串
  • 【Vue3入门1】03-Vue3的基本操作(下)
  • Cocos Creator Shader入门实战(五):材质的了解、使用和动态构建
  • 推荐一个智能抠图与背景替换工具,支持docker部署、API调用
  • go复习目录
  • 基于Sentinel-1A GRD洪涝淹没范围提取(SDWI阈值法和OSTU自动阈值法)
  • .NET 10 新的 JsonIgnoreCondition
  • Python Lambda 函数详解
  • 智慧高速,安全护航:视频监控平台助力高速公路高效运营
  • 知识分享导航
  • Jmeter简介、学习目标及安装启动
  • Maven下载以及项目创建(笔记)
  • 开发一款自然语言转sql的网页版数据库管理工具,不需要使用网上那些付费版,自己搭建一套数据库管理工具,免费开源
  • Unix 域套接字(本地套接字)
  • YOLOv12环境配置,手把手教你使用YOLOv12训练自己的数据集和推理(附YOLOv12网络结构图),全文最详细教程
  • 【社论】个人破产探索,要守住“诚实而不幸”的底线
  • 字母哥动了离开的心思,他和雄鹿队的缘分早就到了头
  • 福州千余公共道路泊车位装“智能地锁”续:运营公司被责令改正并罚款
  • 扶桑谈|从石破茂“越菲行”看日本周边外交布局战略新动向
  • 科普|“小”耳洞也会引发“大”疙瘩,如何治疗和预防?
  • 广西壮族自治区党委政法委副书记李文博接受审查调查