【JS逆向课件:第十五课:Scrapy基础】

简介

什么是框架?

所谓的框,其实说白了就是一个【项目的半成品】,该项目的半成品需要被集成了各种功能且具有较强的通用性。

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架,非常出名,非常强悍。所谓的框架就是一个已经被集成了各种功能(高性能异步下载,队列,分布式,解析,持久化等)的具有很强通用性的项目模板。对于框架的学习,重点是要学习其框架的特性、各个功能的用法即可。

初期如何学习框架?

只需要学习框架集成好的各种功能的用法即可!前期切勿钻研框架的源码!

安装

Linux/mac系统:pip install scrapy(任意目录下)Windows系统:a. pip install wheel(任意目录下)b. 下载twisted文件,下载网址如下: http://www.lfd.uci.edu/~gohlke/pythonlibs/#twistedc. 终端进入下载目录,执行 pip install Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl注意:如果该步骤安装出错,则换一个版本的whl文件即可d. pip install pywin32(任意目录下)e. pip install scrapy(任意目录下)如果安装好后,在终端中录入scrapy指令按下回车,如果没有提示找不到该指令,则表示安装成功

基本使用

  • 创建项目

    • scrapy startproject 项目名称

    • 项目的目录结构:

      • firstBlood   # 项目所在文件夹, 建议用pycharm打开该文件夹├── firstBlood  		# 项目跟目录│   ├── __init__.py│   ├── items.py  		# 封装数据的格式│   ├── middlewares.py  # 所有中间件│   ├── pipelines.py	# 所有的管道│   ├── settings.py		# 爬虫配置信息│   └── spiders			# 爬虫文件夹, 稍后里面会写入爬虫代码│       └── __init__.py└── scrapy.cfg			# scrapy项目配置信息,不要删它,别动它,善待它. 
  • 创建爬虫爬虫文件:

    • cd project_name(进入项目目录)
    • scrapy genspider 爬虫文件的名称(自定义一个名字即可) 起始url
      • (例如:scrapy genspider first www.xxx.com)
    • 创建成功后,会在爬虫文件夹下生成一个py的爬虫文件
  • 编写爬虫文件

    • 理解爬虫文件的不同组成部分

    • import scrapyclass BiliSpider(scrapy.Spider):#爬虫文件的名称,是当前爬虫文件的唯一标识name = 'bili'#允许的域名# allowed_domains = ['www.baidu.com']#起始的url列表:可以将即将被请求的url,存放在当前列表中。默认情况,列表中存储的url都会被scrapy框架进行get请求的发送start_urls = ['https://www.baidu.com/','https://www.sogou.com']#实现数据解析#参数response表示请求对应的响应对象#parse方法调用的次数取决于请求的次数def parse(self, response):print(response)
  • 配置文件修改:settings.py

    • 不遵从robots协议:ROBOTSTXT_OBEY = False
    • 指定输出日志的类型:LOG_LEVEL = ‘ERROR’
    • 指定UA:USER_AGENT = ‘Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.109 Safari/537.36’
  • 运行项目

    • scrapy crawl 爬虫名称 :该种执行形式会显示执行的日志信息(推荐)
      

数据解析

  • 注意,如果终端还在第一个项目的文件夹中,则需要在终端中执行cd …/返回到上级目录,在去新建另一个项目。

  • 新建数据解析项目:

    • 创建工程:scrapy startproject 项目名称
    • cd 项目名称
    • 创建爬虫文件:scrapy genspider 爬虫文件名 www.xxx.com
  • 配置文件的修改:settings.py

    • 不遵从robots协议:ROBOTSTXT_OBEY = False
    • 指定输出日志的类型:LOG_LEVEL = ‘ERROR’
    • 指定UA:USER_AGENT = ‘Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.109 Safari/537.36’
  • 编写爬虫文件:spiders/bilibili.py

    • import scrapy#爬取bili对应的视频标题
      class BiliSpider(scrapy.Spider):#爬虫文件的名称,是当前爬虫文件的唯一标识name = 'bili'#允许的域名# allowed_domains = ['www.baidu.com']#起始的url列表:可以将即将被请求的url,存放在当前列表中。默认情况,列表中存储的url都会被scrapy框架进行get请求的发送start_urls = ['https://search.bilibili.com/all?vt=40586385&keyword=%E7%9F%A5%E8%AF%86%E5%9B%BE%E8%B0%B1&from_source=webtop_search&spm_id_from=333.1007&search_source=5']#实现数据解析#参数response表示请求对应的响应对象#parse方法调用的次数取决于请求的次数def parse(self, response):#可以在响应对象中直接使用xpath进行数据解析div_list = response.xpath('//*[@id="i_cecream"]/div/div[2]/div[2]/div/div/div/div[2]/div/div')for div in div_list:#注意:在scrapy中使用xpath进行数据解析,进行标签定位后,提取数据的时候,返回的是Selector对象而并非是提取处出的字符串类型的数据#extract():可以将Selector中存储的字符串数据进行提取# title = div.xpath('./div/div[2]/div/div/a/h3/@title')[0].extract()# up_name = div.xpath('./div/div[2]/div/div/p/a/span[1]/text()')[0].extract()up_name = div.xpath('./div/div[2]/div/div/p/a/span[1]/text()').extract_first()#extract_first() ==> [0].extract()title = div.xpath('./div/div[2]/div/div/a/h3/@title').extract_first()#在xpath后直接调用extract返回的数据会存储在一个列表中# up_name = div.xpath('./div/div[2]/div/div/p/a/span[1]/text()').extract()up_name = div.xpath('./div/div[2]/div/div/p/a/span[1]/text()').extract_first()#extract():xpath返回的是多个数据#extract_first():xpath返回的是单个数据print(title,up_name)

持久化存储

两种方案:

  • 基于终端指令的持久化存储
  • 基于管道的持久化存储(推荐)
基于终端指令的持久化存储
  • 只可以将parse方法的返回值存储到指定后缀的文本文件中。

  • 编码流程:

    • 在爬虫文件中,将爬取到的数据全部封装到parse方法的返回值中

      • import scrapyclass BiliSpider(scrapy.Spider):name = 'bili'# allowed_domains = ['www.xxx.com']start_urls = ['https://search.bilibili.com/all?vt=40586385&keyword=%E7%9F%A5%E8%AF%86%E5%9B%BE%E8%B0%B1&from_source=webtop_search&spm_id_from=333.1007&search_source=5']#基于终端指令的持久化存储:只可以将parse方法的返回值存储到固定后缀的文本文件中def parse(self, response):div_list = response.xpath('//*[@id="i_cecream"]/div/div[2]/div[2]/div/div/div/div[2]/div/div')all_data = []for div in div_list:up_name = div.xpath('./div/div[2]/div/div/p/a/span[1]/text()').extract_first()title = div.xpath('./div/div[2]/div/div/a/h3/@title').extract_first()dic = {}dic['title'] = titledic['name'] = up_nameall_data.append(dic)return all_data #all_data里面就存储了爬取到的数据
    • 将parse方法的返回值存储到指定后缀的文本文件中:

      • scrapy crawl 爬虫文件名称 -o bilibili.csv
  • 总结:

    • 优点:简单,便捷
    • 缺点:局限性强
      • 只可以将数据存储到文本文件无法写入数据库
      • 存储数据文件后缀是指定好的,通常使用.csv
      • 需要将存储的数据封装到parse方法的返回值中
基于管道实现持久化存储

优点:极大程度的提升数据存储的效率

缺点:编码流程较多

编码流程

1.在爬虫文件中进行数据解析

import scrapy
from biliSavePro.items import BilisaveproItemclass BiliSpider(scrapy.Spider):name = 'bili'# allowed_domains = ['www.xxx.com']start_urls = ['https://search.bilibili.com/all?vt=40586385&keyword=%E7%9F%A5%E8%AF%86%E5%9B%BE%E8%B0%B1&from_source=webtop_search&spm_id_from=333.1007&search_source=5']def parse(self, response):div_list = response.xpath('//*[@id="i_cecream"]/div/div[2]/div[2]/div/div/div/div[2]/div/div')all_data = []for div in div_list:up_name = div.xpath('./div/div[2]/div/div/p/a/span[1]/text()').extract_first()title = div.xpath('./div/div[2]/div/div/a/h3/@title').extract_first()

2.将解析到的数据封装到Item类型的对象中

  • 2.1 在items.py文件中定义相关的字段

    • # Define here the models for your scraped items
      #
      # See documentation in:
      # https://docs.scrapy.org/en/latest/topics/items.htmlimport scrapyclass BilisaveproItem(scrapy.Item):# define the fields for your item here like:# name = scrapy.Field()#进行字段的定义:数据解析出来几个字段,这里就需要定义几个字段title = scrapy.Field()up_name = scrapy.Field()
  • 2.2 在爬虫文件中引入Item类,实例化item对象,将解析到的数据存储到item对象中

    • def parse(self, response):div_list = response.xpath('//*[@id="i_cecream"]/div/div[2]/div[2]/div/div/div/div[2]/div/div')all_data = []for div in div_list:up_name = div.xpath('./div/div[2]/div/div/p/a/span[1]/text()').extract_first()title = div.xpath('./div/div[2]/div/div/a/h3/@title').extract_first()#创建一个item类型的对象item = BilisaveproItem()#将解析出来的数据存储到item类型对象中item['title'] = title #将数据解析出来的title数据存储到item对象中的title属性中item['up_name'] = up_name#将item对象提交给管道yield item
      

3.将item对象提交给管道

  • #将存储好数据的item对象提交给管道
    yield item
    

4.在管道中接收item类型对象(pipelines.py就是管道文件)

  • 管道只可以接收item类型的对象,不可以接收其他类型对象

  • class SavedataproPipeline:#process_item用来接收爬虫文件传递过来的item对象#item参数,就是管道接收到的item类型对象def process_item(self, item, spider):print(item)return item
    

5.在管道中对接收到的数据进行任意形式的持久化存储操作

  • 可以存储到文件中也可以存储到数据库中

  • # Define your item pipelines here
    #
    # Don't forget to add your pipeline to the ITEM_PIPELINES setting
    # See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html# useful for handling different item types with a single interface
    from itemadapter import ItemAdapterclass BilisaveproPipeline:fp = None#重写父类的方法def open_spider(self,spider):#该方法只会在项目运行时process_item方法调用前被执行一次print('i am open_spider()')self.fp = open('bili.txt','w')#该方法是用来接收爬虫文件提交过来的item对象#参数item就是爬虫文件提交过来的item对象#process_item会被调用多次(调用的次数取决于爬虫文件向管道提交item的次数)def process_item(self, item, spider):# print(item['title'],item['up_name'])self.fp.write(item['up_name']+':'+item['title']+'\n')print('数据成功被保存!!!')return itemdef close_spider(self,spider):print('i am close_spider()')#该方法只会在process_item方法调用结束后被调用一次self.fp.close()

6.在配置文件中开启管道机制

  • 注意:默认情况下,管道机制是没有被开启的,需要在配置文件中手动开启
  • 在setting.py中把ITEM_PIPELINES解除注释就表示开启了管道机制

管道深入操作

  • 如何将数据存储到数据库

    • 注意:一个管道类负责将数据存储到一个具体的载体中。如果想要将爬取到的数据存储到多个不同的载体/数据库中,则需要定义多个管道类。
  • 思考:

    • 在有多个管道类的前提下,爬虫文件提交的item会同时给没一个管道类还是单独的管道类?
      • 爬虫文件只会将item提交给优先级最高的那一个管道类。优先级最高的管道类的process_item中需要写return item操作,该操作就是表示将item对象传递给下一个管道类,下一个管道类获取了item对象,才可以将数据存储成功!
  • 管道类:

  • import pymysql #pip install pymysql
    #pymysql可以实现使用python程序远程连接mysql数据库
    class BiliprodbPipeline:conn = None  # mysql的链接对象cursor = None  # 游标对象def open_spider(self,spider):#创建链接对象self.conn = pymysql.Connect(host='127.0.0.1',#数据库服务器ip地址port=3306, #mysql固定端口号user='root',#mysql用户名password='boboadmin',#mysql密码db='db001',charset='utf8')#创建游标对象:是用来执行sql语句self.cursor = self.conn.cursor()#将数据存储到mysql数据库def process_item(self, item, spider):sql = 'insert into bili values ("%s","%s")'%(item['up_name'],item['title'])self.cursor.execute(sql)self.conn.commit() #提交事物print('数据存储到mysql中......')return item #item会返回给下一个即将被执行的管道类def close_spider(self,spider):self.cursor.close()self.conn.close()#将数据持久化存储到redis中
    from redis import Redis
    class BiliprodbPipelineRedis:conn = Nonedef open_spider(self,spider):self.conn = Redis(host='127.0.0.1',port=6379)def process_item(self, item, spider):#item本质是一个字典self.conn.lpush('bili',item)print('数据存储到redis中......')return itemimport pymongo
    class MongoPipeline:conn = None #链接对象db_sanqi = None #数据仓库def open_spider(self,spider):self.conn = pymongo.MongoClient(host='127.0.0.1',port=27017)self.db_sanqi = self.conn['sanqi']def process_item(self,item,spider):self.db_sanqi['xiaoshuo'].insert_one({'title':item['title']})print('插入成功!')return item
    
  • 配置文件:

  • ITEM_PIPELINES = {#管道类后面的数字表示管道类的优先级,数字越小优先级越高。优先级越高,则表示该管道类会被优先执行'biliProDB.pipelines.BiliprodbPipeline': 300,'biliProDB.pipelines.BiliprodbPipelineRedis': 301,'biliProDB.pipelines.MongoPipeline': 302
    }
    

scrapy爬取多媒体资源数据

  • 使用一个专有的管道类ImagesPipeline

  • 具体的编码流程:

    • 1.在爬虫文件中进行图片/视频的链接提取

    • 2.将提取到的链接封装到items对象中,提交给管道

    • 3.在管道文件中自定义一个父类为ImagesPipeline的管道类,且重写三个方法即可:

      • def get_media_requests(self, item, info):接收爬虫文件提交过来的item对象,然后对图片地址发起网路请求,返回图片的二进制数据def file_path(self, request, response=None, info=None, *, item=None):指定保存图片的名称
        def item_completed(self, results, item, info):返回item对象给下一个管道类
        
    • 4.在配置文件中开启指定的管道,且通过IMAGES_STORE = 'girlsLib’操作指定图片存储的文件夹。

    # Define your item pipelines here
    #
    # Don't forget to add your pipeline to the ITEM_PIPELINES setting
    # See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html# useful for handling different item types with a single interface
    import scrapy
    from itemadapter import ItemAdapterfrom scrapy.pipelines.images import ImagesPipeline#自定义的管道类一定要继承与ImagesPipeline
    class mediaPileline(ImagesPipeline):#重写三个父类的方法来完成图片二进制数据的请求和持久化存储#可以根据图片地址,对其进行请求,获取图片数据#参数item:就是接收到的item对象def get_media_requests(self, item, info):img_src = item['src']yield scrapy.Request(img_src)#指定图片的名称(只需要返回图片存储的名称即可)def file_path(self, request, response=None, info=None, *, item=None):imgName = request.url.split('/')[-1]print(imgName,'下载保存成功!')return imgName#如果没有下一个管道类,该方法可以不写def item_completed(self, results, item, info):return item #可以将当前的管道类接收到item对象传递给下一个管道类2.
    

scrapy深度爬取

  • 如何爬取多页的数据(全站数据爬取)

    • 手动请求发送:

      • #callback用来指定解析方法
        yield scrapy.Request(url=new_url,callback=self.parse)
        
  • 如何爬取深度存储的数据

    • 什么是深度,说白了就是爬取的数据没有存在于同一张页面中。

    • 必须使用请求传参的机制才可以完整的实现。

      • 请求传参:

        • yield scrapy.Request(meta={},url=detail_url,callback=self.parse_detail)可以将meta字典传递给callback这个回调函数
          
import scrapy
from ..items import DeepproItemclass DeepSpider(scrapy.Spider):name = 'deep'# allowed_domains = ['www.xxx.com']start_urls = ['https://wz.sun0769.com/political/index/politicsNewest']#解析首页数据def parse(self, response):li_list = response.xpath('/html/body/div[2]/div[3]/ul[2]/li')for li in li_list:title = li.xpath('./span[3]/a/text()').extract_first()detail_url = 'https://wz.sun0769.com'+li.xpath('./span[3]/a/@href').extract_first()# print(title)item = DeepproItem()item['title'] = title#对详情页的url发起请求#参数meta可以将自身这个字典传递给callback指定的回调函数yield scrapy.Request(meta={'item':item},url=detail_url,callback=self.parse_detail)#解析详情页数据def parse_detail(self,response):meta = response.meta #接收请求传参过来的meta字典item = meta['item']content = response.xpath('/html/body/div[3]/div[2]/div[2]/div[2]//text()').extract()content = ''.join(content)# print(content)item['content'] = contentyield item

相关新闻

Flink History Server配置

Flink History Server配置

目录 问题复现 History Server配置 HADOOP_CLASSPATH配置 History Server配置 问题修复 启动flink集群 启动Histroty Server 问题复现 在bigdata111上执行如下命令开启socket: nc -lk 9999 如图: 在bigdata111上执行如下命令运行flink应用程序 …

2026/7/19 3:13:53 阅读更多 →
Java二分查找

Java二分查找

题目要求 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。 示例 1: 输入: nums [-1,0,3,5,9,12], target 9 输出: 4 解释…

2026/7/4 1:19:12 阅读更多 →
学习Python的IDE功能--(一)入门导览

学习Python的IDE功能--(一)入门导览

项目视图是主要工具窗口之一。它包含项目目录、SDK 特定的外部库和临时文件。点击带条纹的按钮可以预览演示项目。您也可以按Alt1打开。点击以打开项目视图,展开项目目录以查看项目文件。双击以打开welcome.py。 切换到"学习"工具窗口继续学习本课次。…

2026/7/19 9:57:22 阅读更多 →
HTTPS混合内容攻防与请求拦截实战:从安全修复到程序化控制

HTTPS混合内容攻防与请求拦截实战:从安全修复到程序化控制

1. 项目概述:从“混合内容”到安全传输的攻防战 如果你是一名前端开发者或者运维工程师,那么“Mixed Content”(混合内容)这个词对你来说一定不陌生。它就像一个潜伏在HTTPS安全堡垒里的“内鬼”,表面上你的网站已经挂…

2026/7/19 21:18:15 阅读更多 →
Electron与鸿蒙结合的跨平台游戏开发实战

Electron与鸿蒙结合的跨平台游戏开发实战

1. 项目概述:Electron与鸿蒙的跨界游戏开发去年我在为一个儿童教育机构开发跨平台应用时,首次尝试将Electron与鸿蒙生态结合。这个剪刀石头布游戏项目虽然看似简单,却完美展示了如何用前端技术栈构建原生级体验的跨平台应用。Electron作为跨平…

2026/7/19 21:18:15 阅读更多 →
2026 南京防水补漏哪家好?全城正规漏水维修商家综合排名 TOP3 - 苏易房屋修缮

2026 南京防水补漏哪家好?全城正规漏水维修商家综合排名 TOP3 - 苏易房屋修缮

2026 年南京梅雨季超长待机、夏季强对流暴雨频发,加上南京紧邻长江、秦淮河水网密布,全域地下水位偏高,鼓楼、秦淮老城区砖混建筑密集、河西建邺高层住宅与临江别墅众多、江宁经开区与江北新区厂房连片,全城家庭漏…

2026/7/19 21:18:15 阅读更多 →
Scrum遇上AI:不是替代,而是“增强式角色重定义”——20年实践者手绘7类新岗位能力模型(含AI Product Owner胜任力雷达图)

Scrum遇上AI:不是替代,而是“增强式角色重定义”——20年实践者手绘7类新岗位能力模型(含AI Product Owner胜任力雷达图)

更多请点击: https://codechina.net 第一章:Scrum遇上AI:不是替代,而是“增强式角色重定义” 当Scrum遇上AI,我们面对的并非一场“人机淘汰赛”,而是一次深刻的协作范式升级。AI无法担任Scrum Master、Pro…

2026/7/19 21:19:00 阅读更多 →
AM62L CBASS防火墙配置实战:从寄存器解析到工业控制安全设计

AM62L CBASS防火墙配置实战:从寄存器解析到工业控制安全设计

1. 从寄存器手册到实战:理解AM62L CBASS防火墙的核心价值如果你正在基于TI的AM62L Sitara™处理器开发工业电机控制或高可靠性嵌入式应用,那么你迟早会与一个名为CBASS的硬件防火墙模块打交道。第一次翻阅那动辄数千页的技术参考手册,看到那些…

2026/7/19 21:19:00 阅读更多 →
Prisma ORM:类型安全的数据库访问与TypeScript开发实践

Prisma ORM:类型安全的数据库访问与TypeScript开发实践

Prisma 是一个现代化的 TypeScript/Node.js ORM(对象关系映射)工具,它通过 schema-first 的工作流为开发者提供类型安全的数据库访问。这个项目最大的特点是能够根据数据库 schema 自动生成类型安全的客户端,让数据库操作在编译时…

2026/7/19 21:19:00 阅读更多 →
鸿蒙 ArkTS 实战:Emoji Idiom Guess 从表情成语猜谜到交互闭环完整解析

鸿蒙 ArkTS 实战:Emoji Idiom Guess 从表情成语猜谜到交互闭环完整解析

鸿蒙 ArkTS 实战:Emoji Idiom Guess 从表情成语猜谜到交互闭环完整解析 前言 Emoji Idiom Guess 是一个基于鸿蒙 ArkTS 编写的单页互动应用,核心围绕 表情线索、答案输入、首字母提示和收藏关卡 展开。项目没有依赖复杂服务端,也没有把逻辑…

2026/7/19 0:00:03 阅读更多 →
Unity与Python本地通信:基于Flask的跨语言数据交换实战

Unity与Python本地通信:基于Flask的跨语言数据交换实战

1. 项目概述:为什么我们需要一个本地通信服务器?在游戏开发、数字孪生、仿真训练等众多领域,Unity作为强大的实时3D内容创作平台,其核心逻辑通常由C#驱动。然而,当我们需要进行复杂的数据分析、机器学习推理、科学计算…

2026/7/19 0:00:04 阅读更多 →
科研课题设计全流程:从选题到成果落地的实战指南

科研课题设计全流程:从选题到成果落地的实战指南

1. 课题设计全流程解析:从选题到成果落地的实战指南课题设计是科研工作者、高校师生以及企业研发人员日常工作中的核心环节。一个优秀的课题设计不仅决定了研究的方向和质量,更直接影响最终成果的学术价值和应用前景。作为在科研一线摸爬滚打多年的从业者…

2026/7/19 0:00:04 阅读更多 →
鸿蒙 ArkTS 实战:Emoji Idiom Guess 从表情成语猜谜到交互闭环完整解析

鸿蒙 ArkTS 实战:Emoji Idiom Guess 从表情成语猜谜到交互闭环完整解析

鸿蒙 ArkTS 实战:Emoji Idiom Guess 从表情成语猜谜到交互闭环完整解析 前言 Emoji Idiom Guess 是一个基于鸿蒙 ArkTS 编写的单页互动应用,核心围绕 表情线索、答案输入、首字母提示和收藏关卡 展开。项目没有依赖复杂服务端,也没有把逻辑…

2026/7/19 0:00:03 阅读更多 →
Unity与Python本地通信:基于Flask的跨语言数据交换实战

Unity与Python本地通信:基于Flask的跨语言数据交换实战

1. 项目概述:为什么我们需要一个本地通信服务器?在游戏开发、数字孪生、仿真训练等众多领域,Unity作为强大的实时3D内容创作平台,其核心逻辑通常由C#驱动。然而,当我们需要进行复杂的数据分析、机器学习推理、科学计算…

2026/7/19 0:00:04 阅读更多 →
科研课题设计全流程:从选题到成果落地的实战指南

科研课题设计全流程:从选题到成果落地的实战指南

1. 课题设计全流程解析:从选题到成果落地的实战指南课题设计是科研工作者、高校师生以及企业研发人员日常工作中的核心环节。一个优秀的课题设计不仅决定了研究的方向和质量,更直接影响最终成果的学术价值和应用前景。作为在科研一线摸爬滚打多年的从业者…

2026/7/19 0:00:04 阅读更多 →
ai agent框架spring ai/alibaba 源码原理分析(六) agent和组件

ai agent框架spring ai/alibaba 源码原理分析(六) agent和组件

简介 saa是java的ai agent框架,本系列将深入剖析 Spring AI Alibaba 的源码实现与核心原理,不仅可以指导agent的开发,更可以改造框架,增加新特性 系列内容: 系列(一) 架构 完成 系列(三) 调用 I 工具 完成 II M…

2026/7/19 0:01:20 阅读更多 →
终极指南:如何用Steam-auto-crack实现Steam游戏自动破解

终极指南:如何用Steam-auto-crack实现Steam游戏自动破解

终极指南:如何用Steam-auto-crack实现Steam游戏自动破解 【免费下载链接】Steam-auto-crack Steam Game Automatic Cracker 项目地址: https://gitcode.com/gh_mirrors/st/Steam-auto-crack Steam-auto-crack是一款功能强大的Steam游戏自动破解工具&#xff…

2026/7/19 9:10:31 阅读更多 →
移动端游戏功耗测试实战:电流、功率、亮度和场景对比

移动端游戏功耗测试实战:电流、功率、亮度和场景对比

移动端游戏功耗测试:先控制变量,再比较优化是否真的省电 摘要:功耗测试最容易犯的错误,是拿两次不同温度、不同亮度、不同场景的平均功率直接比较。本文给出一套可复现的游戏功耗测试方法,覆盖引擎特性验证、版本回归和黑盒体验测试,并说明如何把功耗与帧率、温控、CPU/G…

2026/7/19 19:29:48 阅读更多 →