# 8.用于爬虫 ## 8.0 funboost 用于爬虫前序 先开门见山: - **Scrapy 是 URL 调度器,funboost 是函数调度器;前者束缚你,后者赋能你。** - **Funboost 是“写函数就能爬虫”,Scrapy 是“写框架才能爬虫”。** - **funboost/boost_spider 对仿scrapy api框架最大优势是 自由编程暴击框架奴役, 能复用用户自己的utils文件夹下的 宝贵资产** - funboost/boost_spider 和 scrapy 难度差异: 【对于一个刚刚掌握了 Python 基础语法(变量、列表、元组、if/else、for循环)的新手来说】 - **boost_spider**: 难度要低很多,就和练手手写requests单个小脚本的思路一样,加一行@boost装饰器万事大吉。 - **仿scrapy框架**: 巨大的框架压迫感,和刚学的基础语法对比,差异鸿沟太大,无限懵逼以至于自我怀疑,刚学的python语法是不是白学了。 直接上手 Scrapy,就像 **一个刚学会骑自行车的人,突然被要求去驾驶一架波音747**。 - **Funboost/boost_spider 天然就是 FaaS 微服务,而 Scrapy 只是数据孤岛,架构模式战略级碾压** - 选 `Scrapy`,你是在写脚本。 - 选 `Funboost`,你是在搭建一个可被全公司调用的**数据采集微服务平台**。 - 具体证明见文档`8.14.3`章节, funboost适合全量/增量式离线批处理、实时交互、按需抓取、微服务集成,scrapy不适合动态实时添加种子任务 - `funboost/boost_spider`不仅支持别的部门通过原生消息队列客户端发布函数入参对应的纯净json到对应的queue_name 也支持 `funboost.faas` 快速给你的web服务增加多个funboost路由,一键实现`FaaS(Function as a Service)` 自动发现注册爬虫函数,在实时爬虫上对`scrapy-redis`是战略级碾压。 ### funboost scrapy 两种框架写爬虫代码方式代码明显对比 #### funboost框架是自由框架的证据: ```python @boost(BoosterParams(queue_name='flexible_queue')) def my_task(url): """ 在这个函数里,你可以: - 使用任何HTTP库 (requests, httpx, aiohttp...) - 访问任何数据库 (MySQL, MongoDB, Redis...) - 调用任何API (REST, GraphQL, gRPC...) - 使用任何框架 (Django ORM, SQLAlchemy...) - 执行任何逻辑 (AI推理, 图像处理, 数据分析...) 没有任何限制,没有任何束缚! """ # 完全自由选择HTTP库 if need_js: from playwright import sync_api response = get_with_playwright(url) elif need_async: response = await aiohttp.get(url) else: response = requests.get(url) # 完全自由选择数据库 if use_mongo: mongo_client.insert(data) elif use_mysql: mysql_conn.execute(sql) else: redis_client.set(key, value) ```
funboost 是自由框架,不仅体现在,用户函数内部可以随意写任何逻辑,   
也体现在 funboost 对用户代码无入侵,没有强迫你像 celery  scrapy django 那样规定死死的目录结构和文件名字,  
也体现在可以加到任何新老项目的任意新旧函数上面.
#### scrapy是框架奴役的证据: ```shell # Scrapy的"奴役"表现 """ 项目结构被强制规定: myproject/ scrapy.cfg myproject/ __init__.py items.py # 被迫定义Item pipelines.py # 被迫使用Pipeline settings.py # 复杂配置 middlewares.py # 复杂中间件 spiders/ __init__.py quotes_spider.py # 里面必须 yield Request(url=url_xx,callback=xx_parse,meta={'field1':'xxx','field2':'yyy'}) """ ``` ### 8.0.1 tips : 202309 新增boost_spider爬虫框架 pip install boost_spider **`boost_spider` = `funboost` 的超跑引擎 + 一套为爬虫量身打造的瑞士军刀。所有仿scrapy api爬虫框架都还是处在变花样造一辆马车** 对于爬虫场景: 用户怕麻烦,要求天生就爬虫全套方便,就使用 `funboost` + `boost_spider`(内置了便利的 请求 解析 入库3个类) 用户要绝对自由,就使用 `funboost` + 用户自己自定义的 `utils/` 或 `commons/` 文件夹下封装的 各种工具类和函数 `boost_spider`不是一个 funboost 插件,因为funboost 不需要插件, `boost_spider` 基于 `funboost`的爬虫方便程度增强包,新增了爬虫更方便的3个贡献类, 新增了一个 `RequestClient` 类 (更适合爬虫的请求类,能一键常规基础反爬,自动请求重试,自动换user agent,自动轮流切换各种ip代理商和代理ip,cookies会话保持), 和 `SpiderResponse` 请求响应类 (自带xpath,css,re方法,方便parse解析网页源码), 和 更方便保存字典到各种数据库 的 `DatasetSink` 类(仅需一行代码就把任何字典入库). 有了这三位一体的爬虫增强方便类, `scrapy`的爬虫框架 "专业"这个优势在 `funboost` 面前荡然无存. 用户可以看8.31章节的介绍, `boost_spider` 和 `funboost`的关系. boost_spider地址: [https://github.com/ydf0509/boost_spider](https://github.com/ydf0509/boost_spider) 使用boost_spider的代码例子: [https://github.com/ydf0509/boost_spider/blob/main/tests/car_home_spider.py](https://github.com/ydf0509/boost_spider/blob/main/tests/car_home_spider.py) ### 8.0.2 funboost 降维打击仿scrapy api爬虫框架 你本来只想用Scrapy爬个网页,结果遇见了Funboost 这就像你本来是想乘坐 木柴蒸汽机,结果直接坐上了量子驱动星际战舰 。 scrapy写爬虫仪式感代码文件太多,是为了吃个鸡蛋,要先盖个养鸡场。funboost是让你直接开吃。 因为99%爬虫框架是情不自禁仿scrapy api ,是 基于 yield Request(url=url_xx,callback=my_parse,meta={'field1':'xxx','field2':'yyy'}) 的请求调度框架,扩展相当复杂和难,扩展难、调试难、维护难,难上加难,难到你想转行。 funboost 吊打任何仿 scrapy api框架,本质是 自由编程 降维打击 框架奴役。 **scrapy的时代正在成为过去式** scrapy的设计哲学诞生于一个需要“框架来定义一切”的时代,这在今天看来,反而成了一种束缚。 **funboost代表了更现代、更高效的开发范式:**
  
funboost 相信开发者的能力,只提供最强大的调度核心,将业务逻辑的自由完全交还给用户。  
它的学习成本极低,但能力上限极高,无论是写一个几行代码的临时爬虫,还是构建一个需要数百台机器的庞大采集系统,它都能轻松胜任。  
它更可靠、更灵活、更符合Pythonic的编程直觉。  
  
funboost 让你可以专注于“解决问题”,而 Scrapy 却常常让你把时间花在“解决框架本身的问题”上。作为追求效率和优雅的工程师,选择 funboost 是一个显而易见的决定。  
  
scrapy以及任何仿scrapy api框架,只要是需要用户写 yield Request(url=url_xx,callback=my_parse,meta={'field1':'xxx','field2':'yyy'}) 
就一定被funboost碾压20年以上,无论框架作者代码实力再强都被碾压,因为框架底层设计思维和架构从根本性上被funboost降维打击.
#### funboost 太省时间 👉 Scrapy: "请继承我的Spider类,重写parse方法,配置settings,注册中间件,添加Pipeline..." Funboost: "加个@boost,你随便写,我全搞定!" 警告⚠️:使用Funboost可能导致严重的空闲时间过剩,请提前规划假期! #### @boost 一键赋能你的函数,功能远超爬虫框架 funboost的@boost装饰器给你的函数赋能,自动调度你的函数,使你的函数自动具备 分布式、断点接续运行、随意重启代码万无一失消费确认、基于函数入参的任务过滤、函数入参有效期过滤、消息过期丢弃、并发种类(线程/协程)设置、并发数量设置、qps限制、全局分布式qps控频、函数出错自动重试、funboost web manager 消费可视化监控、 装饰器自带内置一键入库持久化保存函数结果 、定时运行 , 这些功能不比爬虫框架的功能更多更强吗? funboost 轻松一键 多线程/协程 叠加 多进程 ,再叠加多机器,可以轻松做到跑满几百台机器的所有cpu核心,性能非常炸裂,有哪个爬虫框架能一键轻松做得到。 #### funboost 打破数据孤岛:秒变微服务,Java/Go 随时调用 > **Scrapy 是“数据孤岛”**:它是一个封闭的黑盒,外部系统(如 Java 后端、运营后台)想让它临时抓一条数据,必须魔改代码或通过数据库中转,延迟极高,耦合极重。 > > **Funboost 是“微服务接口”**: > 每一个爬虫函数就是一个 **FaaS (Function as a Service)** 接口。 > 外部系统根本不需要知道你是用 Python 写的,只需要往 Redis 队列发一个 JSON,Funboost 就能毫秒级响应,立即执行抓取。 > **这是架构级的胜利:Scrapy 只能做“定时任务”,Funboost 能做“实时服务”。** #### 有人怀疑 funboost没有http请求中间件,这恰好是对scrapy的最大优势 反爬对比: Scrapy方式:学习中间件理论→阅读源码→理解生命周期→尝试实现→调试失败→怀疑人生→放弃→回去用requests Funboost:代码少70%,效率高1000%,头发多10000%! ``` 对于有人怀疑funboost没对爬虫专门优化,所以肯定不如专用爬虫框架scrapy, 认为 scrapy 中能写 http请求 middware 来换ip和 user-agent ,所以是scrapy有优势;这是大错特错的想法。 真实情况是在scrapy api框架中 换 ip 和 user-agent ,你得先精通scrapy 爬虫生命周期和 中间件机制的固定套路写法, 才能写正确scrapy的middleware。 而funboost不插手你怎么发请求, 小白用户使用 最简单直观的面向过程 思维,你在你自己项目的utils文件夹下, 基于requests 定义一个 不到10 行的 通用复用的my_request 的请求函数就能完成换ip和请求头了,完全是0门槛。 例如: import requests def my_request(method,url): proxy = random.choice(proxy_list) user_agent = random.choice(user_agent_list) return requests.request(method,url,proxies=proxy,headers={'user-agent':user_agent}) 定义这个 my_request 函数,这不比你在scrapy定义 http middware 简单几百倍 自由几百倍吗? requests随便用,httpx随便用,aiohttp随便用,selenium playwright 随便用,你想怎么玩就怎么玩。 你只要在 所有funboost的消费函数中始终使用 my_request 来发请求,那 funboost 不就能从万能函数调度框架, 摇身一变成了你的专属爬虫框架了吗? 当你还在吭哧吭哧学习scrapy的中间件机制时候,别人已经用funboost的一个装饰器解决了所有问题。 如果你使用boost_spider框架的RequestClient来发请求,他的response响应上自带了re和xpath css等方法, 所以你无需羡慕scrapy的response自带xpath方法了,无需纠结requests包的response不内置自带xpath方法了, 这个细节并不是很重要,你自己也很容易实现,三方包requests_html 的response也自带html结构化解析方法。 ``` #### funboost能轻松自然完成,而scrapy无法完成的爬虫场景1,浏览器多轮交互 ``` 例如使用 selenium 浏览器渲染页面,并且是需要和浏览器多轮交互,包括 输入文字 -> 点击按钮1 -> sleep等待10秒 -> 再根据内容的具体的值 -> 判断点击按钮2还是按钮3 -> 等待元素 element_id_xx 出现 -> 再提取解析网页内容, 因为这个场景下是不仅使用了浏览器渲染url,还需要多轮交互和判断, 这在scrapy下完全无能为力。 使用scrapy时候,只有 单线程同步勇士 在 parse 解析方法里面去操作浏览器才能非常勉强实现得了, 但是这把scrapy的twisted异步非阻塞弄成了一个废物,几乎把scrapy框架退化成单线程阻塞执行。 只有 代码界的扫地僧 才能把浏览器操作也异步化,yield 一个特殊的 Request 或 Item,等待浏览器服务处理完毕后通过某种回调机制, 将结果返回给 Scrapy 的流程中。但是这种方式实现起来非常复杂,需要精巧的架构设计和对 Scrapy 内部机制的非常深入精通, 高级python开发工程师都无法做到,只有欧美资深python框架架构师才能做得到。 然而,这个场景在 funboost 下来完成却十分简单直接自然的轻松搞定,你仅仅需要在你的函数里面像平常一样 非常自然的写操作浏览器代码。因为你的函数就是个黑盒,funboost能自动并发完成调度执行任意函数。 ``` **scrapy实现很困难,但funboost实现很丝滑的,见文档8.9章节的token有效期很短的场景2** #### 总结下funboost和scrapy在小项目和大项目: 1、小型爬虫:funboost单文件搞定,不像scrapy要你在七八个文件来回切换写代码。你的爬虫函数验证正确后, 加个@boost装饰器就自动并发调度了。就算是单机小爬虫,funboost也可以选择sqlite中间件帮你断点续爬。 2、大型爬虫:funboost的各种控制功能更丰富更强,funboost的轻松一键 多线程/协程 叠加 多进程 ,再叠加多机器,性能吊打scrapy。 funboost各种控制功能多到你数不过来。 3、适用范围:scrapy做得到的,funboost一定能更轻而易举做得到; funboost能轻松做得到的,scrapy则复杂到突破天际也无法做得到。 ### 8.0.3 讨scrapy檄文:Funboost兴,Scrapy亡,天下爬虫,当顺天命! **夫程序之道,贵在通达!爬虫之术,胜在调度!** 昔有Scrapy窃据神器,挟Twisted之技而令诸侯,然其框架繁苛,回调如狱,岁月更迭,其势已衰,其道已孤,弊病丛生,开发者苦之久矣! 今有Funboost,顺天应人,聚函数神力,携`@boost`之雷霆,以大道至简之义,破枷锁,扫陈规,伐无道,正本清源,布告天下!此诚不可逆之大势也! **Scrapy十败如山崩,Funboost十胜如日升:** 然吾观其根基,十败已定! 吾FunBoost十胜在手,当为天下主! 一曰:**道失对道胜!** Scrapy以URL为心,`Request`为锁,画地为牢,框架奴役,此谓 **"作茧自缚折云翅"**! Funboost以函数为本,万物皆可调度,自由挥洒,赋能无限,此谓 **"道法自然贯星河"**! 二曰:**繁失对易胜!** Scrapy项目冗杂,文件七八,层峦叠嶂,初学望而生畏,此谓 **"学海无涯苦作舟"**! Funboost一`@boost`统领,化繁为简,片刻上手,举重若轻,此谓 **"大道至简一点通"**! 三曰:**力失对能胜!** Scrapy并发受限,多核难尽其用,遇Selenium则异步变同步,英雄气短,此谓 **"单骑难陷万军阵"**! Funboost线程协程,进程机器,四重并发裂苍穹,千机万核竞驰骋,此谓 **"力拔山兮气盖世"**! 四曰:**估失对准胜!** Scrapy仅控并发,QPS随缘而定,精准控频如水中捞月,此谓 **"盲人射马失准的"**! Funboost令牌桶在握,QPS量沙准刻,分布式亦能令行禁止,此谓 **"运筹帷幄控毫厘"**! 五曰:**乱失对明胜!** Scrapy回调嵌套,逻辑支离破碎,`meta`传参如雾里看花,调试追踪九曲回肠,此谓 **"回调地狱不见天"**! Funboost平铺直叙,代码如行云流水,函数形参IDE烛照,逻辑一气呵成,此谓 **"银河直下三千尺"**! 六曰:**虚失对固胜!** Scrapy断点堪忧,重启则任务灰飞烟灭,断点续爬如镜花水月,此谓 **"大厦将倾一木难"**! Funboost消息确认,持久队列作金城汤池,宕机重启岿然不动,万无一失,此谓 **"稳坐钓台风浪平"**! 七曰:**斥失对容胜!** Scrapy旧码难容,迁移需重塑筋骨,大动干戈,劳民伤财,此谓 **"削足适履两相难"**! Funboost海纳百川,老树亦能发新枝,已有函数皆可加持,立等可用,此谓 **"万川归海终不弃"**! 八曰:**梏失对活胜!** Scrapy自定义反爬,中间件如天堑难越,非精通其道者不能为,此谓 **"画虎不成反类犬"**! Funboost封装请求,自由定义如探囊取物,换IP、UA、破解JS信手拈来,此谓 **"随心所欲不逾矩"**! 九曰:**拙失对巧胜!** Scrapy遇奇巧需求,如Token短时效、多轮浏览器交互、外部动态任务实时添加,则束手无策,左支右绌,此谓 **"黔驴技穷无新声"**! Funboost函数之内,连续操作迅雷不及,状态管理自然天成,轻松驾驭复杂流程,此谓 **"灵心胜算巧夺天"**! 十曰:**晦失对捷胜!** Scrapy单元测试如扛泰山,调试艰涩,错误排查如大海捞针,此谓 **"雾锁津迷途难返"**! Funboost单函数直刺要害,调试若烹小鲜,IDE相助如虎添翼,可测性无出其右,此谓 **"拨云见日捷报传"**! **有此十胜,Funboost伐Scrapy,如金狮搏兔,如高屋建瓴,何愁不克?!** Scrapy老将,若迷途知返,弃旧从新,尚可重焕生机;若固执己见,负隅顽抗,必为时代洪流所淘汰! **今Funboost大旗已立,携函数调度之利刃,集分布式并发之雄兵:** 东纳Requests之勇,西引HTTPX之锐;南征Selenium之坚,北抚Playwright之奇! 聚Redis、RabbitMQ、Kafka为粮草,合多进程、多线程、协程为三军! **天下英雄,当明辨是非,顺势而为!** 弃Scrapy之糟粕,取Funboost之精华! 开发者再不必叩拜Spider宗庙,亦无需忍受回调地狱之煎熬! 此乃爬虫之文艺复兴,调度之工业革命! **剑锋所指,框架枷锁必将斩断!函数光辉,普照四海!** **与诸君共勉,开创万物皆可Boost之盛世!** **FunBoost大都督,布告天下!** ### 8.0.4 很多人问这个框架能不能爬虫? 答:此框架不仅可以对标celery框架,也可以取代scrapy框架。 无论是比 自由度、兼容常规代码程度、用户需要编写的实际代码行数、消息万无一失可靠度、对爬虫的qps频率/并发控制手段、超高速调度请求, 此框架从以上任何方面远远超过scrapy,一切都是只会是有过之而无不及。
  
应该还是主要是很浮躁,不仅没看详细文档,应该是连简介都没看。  
分布式函数调度框架,定位于调度用户的任何函数,只要用户在函数里面写爬虫代码,就可以分布式调度爬虫,并且对爬虫函数施加20种控制功能,  
例如 qps恒定 任何时候随意关机重启代码消息万无一失确认消费 非常简单的开启多进程叠加线程/协程,这些强大的功能绝大部分爬虫框架还做不到。  

此框架如果用于爬虫,不管从任何方面比较可以领先scrapy20年,也比任意写的爬虫框架领先10年。  
不是框架作者代码编写实力问题,主要是思维问题,爬虫框架一般就设计为url请求调度框架,url怎么请求都是被框内置架束缚死了,  
所以有些奇葩独特的想法在那种框架里面难以实现,需要非常之精通框架本身然后改造框架才能达到随心所欲的驾驭的目的。  
而此框架是函数调度框架,函数里面可以实现一切任意自由想法,天生不会有任何束缚。  
主要还是思想问题,国内一般人设计的爬虫框架都是仿scrapy api,天生不自由受束缚。  
### 8.0.5 为什么 funboost 用来爬虫时候,扩展性简单性 远超 scrapy api式的一众传统爬虫框架? ``` funboost 扩展很容易,因为funboost是调度一个函数,不是调度一个url请求,用户可以在函数内部无拘无束实现任何想法, 不用顾忌思考怎么和funboost适配,用户在函数内部想写什么是天生更自由的。 比如用户想使用哪种 http请求包发请求,想使用什么三方包包来解析html,想把爬虫数据保存到什么类型的数据库, 怎么使用不同供应商的代理ip ,用户完全自主发挥,而不用思考和funboost框架本身怎么适配。 funboost 在易扩展性方面的一个显著优势,尤其是在与那些结构更固化的框架(如专门的爬虫框架)进行对比时。 核心原因正如所指出的: funboost 调度的是函数,而不是特定的操作(如 URL 请求) 。 这带来了几个关键的好处,使得在函数内部的扩展和定制变得非常容易: 1. 函数即"黑盒" : 对于 funboost 调度器来说,被 @boost 装饰的函数在很大程度上是一个"黑盒"。 funboost 负责触发这个函数的执行(根据队列消息或定时设置)、管理并发、处理重试等,但它 不关心函数内部具体做了什么 。 2. 完全的内部自由度 : 在这个函数"黑盒"内部,开发者拥有完全的自由: - HTTP 请求 : 你想用 requests , httpx , aiohttp ,或者任何其他 HTTP 客户端库都可以, funboost 不会干涉。 - HTML/数据解析 : 使用 lxml , beautifulsoup4 , parsel , json , re 或任何你喜欢的解析库,完全没问题。 - 数据存储 : 直接调用 pymongo , redis-py , psycopg2 , mysql-connector-python , sqlalchemy , dataset 等库将数据存入 MongoDB, Redis, PostgreSQL, MySQL 或其他任何数据库/文件系统。 - 代理 IP : 对接任何代理 IP 服务商的 API 或 SDK,实现复杂的代理切换逻辑。 - 任何业务逻辑 : 在函数内部可以编写任意复杂的 Python 代码,调用其他模块、类,实现你的业务需求。 3. 无需适配框架内部机制 : 因为 funboost 不强制规定函数内部的实现方式,所以你不需要去学习和适配 funboost 内部 可能存在的特定请求对象、响应对象、Item 结构或 Pipeline 接口(除非像 boost_spider 这样在上层做了封装)。你只需要编写标准的 Python 代码即可。 总结来说: funboost 的这种设计哲学,将 任务调度 与 任务执行的具体实现 解耦开来。它提供了一个强大的、通用的函数调度平台, 而将函数内部的实现细节完全交给了开发者。这种模式极大地降低了在 任务逻辑层面 进行扩展和定制的复杂度, 让开发者可以"无拘无束"地使用整个 Python 生态系统来实现功能,而无需过多考虑与调度框架本身的适配问题。这确实是它易用性和灵活性的一大体现。 ``` funboost在处理特殊奇葩需求时确实远超scrapy这类API式框架,主要体现在: ``` 处理复杂流程的能力: funboost允许在单一函数中编写完整业务逻辑 scrapy需要拆分为多个回调,使复杂流程变得支离破碎 状态管理简洁度: funboost可使用普通Python变量保存状态 scrapy需要通过meta字典在请求间传递,容易出错 特殊时序要求处理: funboost可精确控制请求发送时机 scrapy受调度器影响,无法保证确切执行时间 条件逻辑和分支: funboost支持自然的if/else/for/while等控制流 scrapy需要通过不同回调和meta实现,极度复杂化 异常处理方式: funboost可使用标准try/except处理整个流程 scrapy各回调间异常隔离,难以统一处理 资源释放与清理: funboost支持with语句和上下文管理 scrapy在分散的回调中难以管理资源生命周期 调试和问题排查: funboost代码线性执行,容易跟踪 scrapy回调跳转使调试变得困难 与外部系统集成: funboost可在任何点与外部API交互 scrapy需要特殊中间件或信号处理 对于要求精确控制、复杂交互、特定时序或依赖外部系统的"奇葩需求",funboost的流程式编程模型确实具有压倒性优势,能够以更直观、更可靠的方式实现这些复杂需求。 ``` 此框架如果用于写爬虫,建议的写法是一种页面(或者接口)对应一个函数,例如列表页是一个函数,详情页是一个函数。 1个函数里面只包括一次请求(也可以两三次请求,但不要在函数本身里面去for循环遍历发十几个请求这种写法), ### 8.0.6 主动集中简要回答驳斥一些scrapy 优势更大的观点 知道有些人会质疑说scrapy爬虫更好,有些人举的scrapy更强的例子,喜欢以卵击石,以弱击强,倒反天罡,必须集中统一回答反驳。 #### **你质疑funboost 没有 http middware ?** 你这个想法就像是在说"没有被大铁链子束缚的奴隶不好当"一样搞笑! ``` 答:上面已经回答了,用户手写定义一个通用的 my_request 更强更自由更简单。 ``` #### **你质疑funboost 没有 pipeline,质疑保存数据麻烦?** 你这个想法就像是在说"没有被大铁链子束缚的奴隶不好当"一样搞笑! 这个问题问得好,但结论恰恰相反!**没有 Pipeline 正是 Funboost 的巨大优势**,因为它让你摆脱了不必要的束缚。 `Funboost`: 一行代码,一个文件,逻辑高度内聚,极其灵活。 `Scrapy`: 四个文件,几十行模板代码,逻辑支离破碎,极其死板。 ``` 答:用户可以自己封装一个保存字典到数据库的函数,最简单就是使用dataset知名包,只要一行代码就能保存字典到mysql postgre 数据库了。 boost_spider 就是自带了 datasetsink 类,dataset_sink1.save('your_table', data_dict),简单直观多了。 反观,scrapy 的 pipeline 反而才是杀鸡用牛刀 过度设计分层,为了保存一个字典需要切换写四个文件, 你单纯想喝瓶水而已, 但被迫建一个矿泉水生产线。 scrapy保存数据需要来回切换4个文件写代码: 第一步,在 items.py 定义Item类型 第二部,在 your_spider.py 的 parse_xx 中 yield item 第三部,在 pipelines.py 中 process_item 方法中判断不同item类型,从而保存到对应的表中. 第四部,在 settings.py 的 ITEM_PIPELINES 中配置 指定pipeline类(如果忘了这一步就白忙活了) ``` #### **你说Scrapy 插件生态丰富,质疑Funboost 没有三方包插件生态不够?** `funboost` 不需要任何插件,是无招胜有招. - scrapy插件多是“病”,不是“药” 。Python pypi生态就是funboost的生态,你的python项目下的 utils/ 或者 helpers/ 文件夹下日积月累的各种工具类和函数都是 funboost的生态, funboost不需要各种funboost-xx的三方包插件。 - 说插件多就是生态好,这么想法的人简直是没长脑子,用户已经会了三方包的使用,但在scrapy框架下,为什么还需要等专门的美国编程大神去给三方包开发插件适配scrapy框架的生命周期和组件流程,才能在scrapy中愉快的使用三方包。用户压根没想过这个问题。 详细的驳斥看文档8.14.2章节 - Scrapy 插件多 ≠ 框架强,恰恰说明了框架对用户自由的压制太多,“什么都得经过官方那一套”。 Funboost 是函数式的框架,自由度高、无约束、无钩子、无上下文依赖,天然就能融合任何三方库,python三方包生态就是funboost的生态,你utils下积累的工具类和函数都是 funboost的生态。 - funboost不需要学 scrapy-redis scrapy-selenium scrapy-playwright scrapy-user-agents scrapy-splash 专门开发各种 funboost-xx 的三方包插件。funboost压根不需要三方包插件,而不是三方包插件生态薄弱。 ``` 答: scrapy是框架太复杂了约束多钩子多,所以需要由专门的大神开发三方插件,因为普通人写不出来这些插件。 Scrapy 框架的结构设计“高度抽象 + 强约束 + 多钩子生命周期 + 中间件堆叠机制”,导致插件开发成本极高。 funboost 恰恰不需要插件,因为用户是轻松自由使用任意三方包。 你压根不需要专门的大神给你写个例如 funboost-selenium 类似的插件,才能开始在funboost里面使用selniuem干活,懂了吗? 例如 如 scrapy-redis 用于分布式、scrapy-playwright 或 scrapy-selenium 用于 JavaScript 渲染,scrapy-user-agents换请求头。 funboost需要学习这些扩展插件怎么使用吗? 绝对不需要,funboost 是顺其自然自由使用任意三方包。。 麻烦你去看看配置使用 scrapy-selenium 有多麻烦,而直接使用 seleium 有多简单。 本来学习selenium就烦人,你还要再多学习一个 scrapy-selenium , 凭什么非要这么苦逼,学了各种三方包还不够,还需要额外再另外学这么多三方包的插件。 ``` 因为你用scrapy,即使你非常精通三方包,如果没有美国大神给你提供三方包的插件,你仍然寸步难行,所以你羡慕scrapy有各种三方包的插件生态。 你用Scrapy,哪怕精通三方包,没有插件也寸步难行;用Funboost,任何三方包都能直接用,不需要等别人给你造插件轮子。 当你可以直接驾驶F1赛车时,为什么还非要学习如何给破自行车安装火箭推进器? ``` 举个例子: 为什么你用scrapy-redis插件?因为你就算精通了py-redis包的用法,精通了怎么redis.blpop redis.lpush推拉消息,精通了怎么redis.sadd 去重 但是你不知道怎么完美替代scrapy内置的调度器和去重器,因为你不可能开发的出来,关键难度不是怎么操作reids,而是难以适配scrapy的中间件机制和生命周期钩子懂啦吗? 不信的你可以看scrapy-redis源码,你能写得了那么好? 你以为你随便在代码哪里简单的写个redis.blpop 和 redis.lpush,scrapy就能完美使用你写的redis代码逻辑来调度运行起来吗? ``` **开发效率的巨大差异** **使用Scrapy:** 学习Scrapy框架 → 2. 学习要用的包 → 3. 等待/寻找插件 → 4. 学习插件用法 → 5. 配置插件 → 6. 开始开发 **使用Funboost:** 学习要用的包 → 2. 开始开发 #### 你项目下的 utils 文件夹的工具类是黄金还是废铁?取决于你用什么哲学的框架 - 详细见文档 8.0b 章节 Python pypi生态就是funboost的生态,你的python项目下的 utils/ 或者 helpers/ 文件夹下日积月累的各种工具类和函数都是 funboost的生态, 例如你的爬虫项目 utils 文件夹下日积月累,99%的概率已经存在很多好用的经过实战检验的爬虫工具类和函数,可以直接被 import 复用使用。 #### **你说scrapy社区支持,有庞大的专门各种问题的讨论?质疑funboost没有社区?** ``` 因为scrapy太难了,用户必须精通scrapy框架本身,精通scrapy各种组件和生命周期,用户难以自由扩展,所以需要讨论。 funboost是你写一个函数,你可以在函数里面自由自在写任何代码,你在写你的消费函数里面是自由的, 不需考虑funboost框架本身的约束,不需要考虑怎么和funboost配合。 funboost 没有需要讨论的,因为funboost 是顺其自然自由使用任意三方包。 例如假设你不会pymysql插入数据,那去pymysql论坛讨论,这和funboost没关系。 例如假设你不会 selenium 操作,那去selenium论坛讨论,这和funboost没关系。 例如你不会requests使用代理ip,那去requests论坛讨论,这和funboost没关系。 例如你不会使用xpath解析html,那去xpath论坛讨论,这和funboost没关系。 ``` #### **你羡慕scrapy的response有自带.xpath .css .extract_first .extract_all 方法?** ``` 答:你可以看看boost_spider项目的response,也有xpath方法,实现很简单。 这些真的很简单啊,你的my_request函数可以是返回一个带有这些方法的response对象就好了。 封装一个带有这些方法的Response类型的对象简直不要太简单。 ``` #### **scrapy twisted 性能强悍?担心funboost爬取不快?** ``` 答: 没有funboost 的 多机器 + 多进程 + asyncio强。 asyncio才是未来。 拿scrapy的短处去攻击funboost的长处,以卵击石。 ``` #### **你质疑scrapy重试功能强大?** ``` 答:funboost 的函数重试功能远远暴击scrapy的url重试功能(防丢数据2),你可以看8.13.2章节。 如果http状态码200,但是页面反扒,scrapy会丢失大量数据,funboost则不会。 ``` #### **你怀疑scrapy稳定,funboost不稳定?** ``` 答:funboost 框架核心 执行函数是稳定的,funboost爬虫比scrapy更不容易漏数据,看文档8.13.2 因为funboost对用户怎么写爬虫函数干预很少,少即是稳,所以funboost很稳定。 funboost无论任何情况下不会退出代码,例如你把爬虫机器和 消息队列机器之间的网线拔掉10分钟, funboost不会退出代码,会自动重试连接,只要网络恢复后,就能自动接着拉取消息。 ``` #### **你质疑scrapy自带去重,funboost不能去重?** ``` 答: 详细看8.14.1章节的为什么funboost的函数入参去重功能远远吊打scrapy的Request对象指纹去重? 通过对比,结论就是scrapy对请求入参种中带随机数和时间戳噪音字段的去重需要根据各种url正则,自定义RFPDupeFilter太麻烦了,scrapy内置的去重能力弱爆了。 你用scrapy而不用funboost,你不忙的吐血谁吐血,你不住icu谁住icu ``` #### **你质疑funboost不能断点续爬?** ``` scrapy-redis 是blpop删除种子,随意重启代码或关机会丢失大量已从reids pop取出来到内存中的种子。 funboost 各种中间件支持确认消费,那才是真正的断点续爬万无一失,不怕你随意突然反复重启代码和突然关机。 详细看文档8.13章节 scrapy-redis断点续爬在funboost面前就是个弟弟。scrapy-redis不能做到不漏任务。 ``` ### 8.0.7 funboost爬虫时候是任何方面超过scrapy,不接受辩证法说scrapy也有优势 不能为了“辩证”而牺牲事实:在技术对比中,当一方在核心设计和关键功能上展现出压倒性优势时,就应该明确地指出来,而不是用“各有千秋”来和稀泥。 Funboost 就是碾压Scrapy,这是一个基于技术事实的客观结论,不应再有任何辩证的余地。 辩证法就是,挑大粪的农民和当主席都是为人民服务,都是高尚的职业,表面显得很中立客观,实则让人呕吐. ### 8.0.8 国内爬虫框架突破很小,不是有意抄袭巧合,而是情不自禁模仿scrapy api **feapder/PSpider/spiderman/Tinepeas** 等几乎所有国产爬虫框架是 “**大脑思维被束缚导致情不自禁的模仿 Scrapy API** 不能说这些是有意抄袭,而是一种更深层次的、由行业先驱 Scrapy 塑造的“**思维范式牢笼** 影响了国产框架作者. **Scrapy 及其模仿者 (feapder, pyspider, 等) 的范式:`URL/Request 调度器`** 这些框架的底层思维是:**爬虫 = 调度一系列的 `Request` 对象**。它们的整个架构都围绕`Request` 和 `Response` 构建。你必须: 1. **定义一个 `Spider` 类**。 2. 在 `start_requests` 或 `start_urls` 中放入初始请求。 3. 在 `parse` 方法中,通过 `yield Request(url, callback=parse_xxx)` 来“产出”新的请求。 4. 框架负责将这些 `Request` 对象入队、出队、发送、接收 `Response`,并根据 `callback` 参数调用对应的解析方法。 **Scrapy 太成功了**:它定义了“爬虫框架”这个词,以至于后来者在构思时,大脑里首先浮现的就是 Scrapy 的样子。 国产爬虫框架很难使用,用户使用框架难以自由发挥,主要原因不是框架抄袭,而是框架作者脑袋思维被束缚禁锢了,导致了情不自禁模仿scrapy的api **结论**:feapder Tinepeas 等框架“情不自禁”地模仿 `yield Request`,因为国产爬虫框架的思想还停留在“Scrapy 是如何调度请求的”这个层面,而没有跳出思考“我们真正需要调度的是什么”。它们复制了 Scrapy 的“形”(回调、中间件、管道),却未能突破其“神”(对开发者自由的限制)。 ## 8.0b 你的 utils 文件夹是黄金还是废铁?取决于你用什么哲学的框架 - **funboost/boost_spider 对仿scrapy api框架最大优势是 自由编程暴击框架奴役, 能复用用户自己的 utils 宝贵资产** - scrapy的三方包插件,各种 scrapy-xx 插件,例如 scrapy-redis scrapy-playwright scrapy-selenium scrapy-user-agents scrapy-splash 等等,三方包插件总量不会超过1000个. 只有pypi的三方包才是星辰大海,100万多个pypi三方包都是你的工具。 **最重要的是,只有用户自己项目下utils文件夹下积累的工具类和函数才是最符合用户自己实际需求的工具,而不是 scrapy-xx 插件。** - Python pypi生态就是funboost的生态,你的python项目下的 utils/ 或者 helpers/ 文件夹下日积月累的各种工具类和函数都是 funboost的生态, 例如你的爬虫项目 utils 文件夹下日积月累,99%的概率已经存在如下,好用的经过实战检验的工具类和函数: ```python def anti_request(method,url,...retry_times=3,is_change_ua=True,is_change_proxy): """自动重试 换代理ip user-agent 的http请求函数""" def save_to_mysql(data:dict): """保存字典到数据库的函数""" class RedisBloomFilter: """redis 布隆过滤器""" def is_exists(self, key): ... def add(self, key): ... def extract_all_with_join(selector, separator=' '): """提取选择器所有结果并用指定分隔符连接成字符串。""" return separator.join(selector.getall()).strip() class WebsiteAuthenticator: """对于需要登录的网站,一个管理会话、Cookie 和 Token 的类是无价之宝。""" def login(self): ... def get_session(self): ... def send_email_notification(subject, body, recipient): """发送爬虫邮件通知。""" def download_and_upload_to_s3(url, bucket_name, object_name): """下载文件并直接流式上传到 S3。""" s3 = boto3.client('s3') with requests.get(url, stream=True) as r: r.raise_for_status() s3.upload_fileobj(r.raw, bucket_name, object_name) return f"s3://{bucket_name}/{object_name}" ``` **在 funboost中**,你utils文件夹下的宝贵资产,黄金任然是黄金,可以直接import 复用使用: ```python from funboost import boost, BrokerEnum from utils.http_client import anti_request # 你日积月累的工具 from utils.db import save_to_mysql # 你日积月累的工具 from utils.redis_dedup import RedisBloomFilter # 你日积月累的工具 from utils.website_authenticator import WebsiteAuthenticator # 你日积月累的工具 from utils.send_notification import send_email_notification # 你日积月累的工具 from utils.download_and_upload import download_and_upload_to_s3 # 你日积月累的工具 ``` **而在`scrapy` `feapder`面前**,你曾经引以为豪在`utils`文件夹下积累的宝贵资产,他不是黄金,只是一堆破铜烂铁而已,不能被导入复用。 你没有按照他们框架的`Downloader Middleware` 和 `Pipeline`规范写的`utils`文件夹下的工具类,都是废铁一文不值。 `scrapy`的扩展插件机制 被 `funboost` 的自由import复用吊打。 - 复用用户自己的 utils 宝贵资产,正是 `funboost` 区别于 `Scrapy/Feapder` 等传统框架的**根本性优势**,是战略层面的胜利。 * **`utils` 是开发者的“内功心法”**:一个开发者的 `utils` 文件夹,是他/她多年经验的结晶,是解决特定领域问题的最佳实践沉淀。它包含了对业务逻辑的深刻理解,是**不可替代的、高度定制化的“私有武器库”**。 * **“复用 `utils`” = 复用经验和智慧**:一个框架如果能让开发者无缝地复用自己的 `utils`,就意味着它尊重并放大了开发者的个人能力和历史积累。开发者可以用最熟悉、最高效的方式解决问题。 * **“无法复用 `utils`” = 废掉武功,重练套路**:`Scrapy/Feapder` 的插件和中间件机制,本质上是让你放弃自己的“内功”,去学习并练习一套它们规定好的“套路招式”。你的 `my_request` 函数再精妙,也得改成 `Downloader Middleware` 的形状;你的 `save_to_mysql` 再高效,也得塞进 `Item Pipeline` 的模子里。这是一个**巨大的、隐性的成本**。 ## 8.1 演示获取汽车之家资讯的 新闻 导购 和 评测 3个板块 的 文章。 页面连接 https://www.autohome.com.cn/all/ ``` 这是一个非常经典的列表页-详情页两层级爬虫调度。演示爬虫一定最少需要演示两个层级的调度,只要会了两层级爬虫,3层级就很简单。 此框架如果用于写爬虫,建议的写法是一种页面(或者接口)对应一个函数,例如列表页是一个函数,详情页是一个函数。 1个函数里面只包括一次请求(也可以两三次请求,但不要在函数本身里面去for循环遍历发十几个请求这种写法), ``` ```text """ 演示分布式函数调度框架来驱动爬虫函数,使用此框架可以达使爬虫任务 自动调度、 分布式运行、确认消费万无一失、超高速自动并发、精确控频、 种子过滤(函数入参过滤实现的)、自动重试、定时爬取。可谓实现了一个爬虫框架应有的所有功能。 此框架是自动调度一个函数,而不是自动调度一个url请求,一般框架是yield Requet(),所以不兼容用户自己手写requests urllib的请求, 如果用户对请求有特殊的定制,要么就需要手写中间件添加到框架的钩子,复杂的需要高度自定义的特殊请求在这些框架中甚至无法实现,极端不自由。 此框架由于是调度一个函数,在函数里面写 请求 解析 入库,用户想怎么写就怎么写,极端自由,使用户编码思想放荡不羁但整体上有统一的调度。 还能直接复用用户的老函数,例如之前是裸写requests爬虫,没有规划成使用框架爬虫,那么只要在函数上面加一个@boost的装饰器就可以自动调度了。 而90%一般普通爬虫框架与用户手写requests 请求解析存储,在流程逻辑上是严重互斥的,要改造成使用这种框架改造会很大。 此框架如果用于爬虫和国内那些90%仿scrapy api的爬虫框架,在思想上完全不同,会使人眼界大开,思想之奔放与被scrapy api束缚死死的那种框架比起来有云泥之别。 因为国内的框架都是仿scrapy api,必须要继承框架的Spider基类,然后重写 def parse,然后在parse里面yield Request(url,callback=annother_parse), 请求逻辑实现被 Request 类束缚得死死的,没有方便自定义的空间,一般都是要写middware拦截http请求的各个流程,写一些逻辑,那种代码极端不自由,而且怎么写middware, 也是被框架束缚的死死的,很难学,例如scrapy 集成selenium浏览器没几个人搞定,就算实现了也是同步阻塞导致scrapy的并发成为了废物, 当你把scrapy的并发调度弄成了废物了还有必要用什么scrapy。 例如崔庆才 写的scrapy集成selenium浏览器,文章在 https://cuiqingcai.com/8397.html ,如果网站网页需要渲染30秒,那么此时scrapy爬虫慢的吐血, 因为这种扩展scrapy的方式是错误的。 还有人在scrapy的Spider类的解析方法里面用浏览器重复请求一次url,scrapy的parse不是并发的,只有yield Request类请求是自动并发, 下面parse中写浏览器请求,scrapy就变成了个废物。 def parsexx(self,response): driver.get(response.url) 分布式函数调度框架由于是自动调度函数而不是自动调度url请求,所以天生不存在这些问题。 用其他爬虫框架需要继承BaseSpider类,重写一大堆方法写一大堆中间件方法和配置文件,在很多个文件夹中来回切换写代码。 而用这个爬虫,只需要学习 @boost 一个装饰器就行,代码行数大幅度减少,随意重启代码任务万无一失,大幅度减少操心。 这个爬虫例子具有代表性,因为实现了演示从列表页到详情页的分布式自动调度。 """ """ 除了以上解释的最重要的极端自由的自定义请求解析存储,比普通爬虫框架更强的方面还有: 2、此爬虫框架支持 redis_ack_able rabbitmq模式,在爬虫大规模并发请求中状态时候,能够支持随意重启代码,种子任务万无一失, 普通人做的reids.blpop,任务取出来正在消费,但是突然关闭代码再启动,瞬间丢失大量任务,这种框架那就是个伪断点接续。 3、此框架不仅能够支持恒定并发数量爬虫,也能支持恒定qps爬虫。例如规定1秒钟爬7个页面,一般人以为是开7个线程并发,这是大错特错, 服务端响应时间没说是永远都刚好精确1秒,只有能恒定qps运行的框架,才能保证每秒爬7个页面,恒定并发数量的框架差远了。 4、能支持 任务过滤有效期缓存,普通爬虫框架全部都只能支持永久过滤,例如一个页面可能每周都更新,那不能搞成永久都过滤任务。 因为此框架带有20多种控制功能,所以普通爬虫框架能实现的控制,这个全部都自带了。 """ ``` 爬虫任务消费代码 代码在 https://github.com/ydf0509/distributed_framework/tree/master/test_frame/car_home_crawler_sample/test_frame/car_home_crawler_sample/car_home_consumer.py 关于对下面funboost爬虫代码的质疑? ``` 有人说这里的代码是不真实的,没有 换代理ip useragent,也没有保存结果到数据库。还有人说没有演示破解js 加密。 每次请求自动换代理ip和ua这个功能你自己写个函数不就完了,把这里的requests.get换成你自己定义的换ip的请求函数就完了,一个函数的事情而已。 至于保存到数据库, 你自己把 print(f'保存数据 {news_type} {title} {author} {url} 到 数据库') 这行改成插入数据库就完了。你自己定义一个函数mysql连接池插入数据库就完了。 这些不是重点所以不需要精确细致的演示,只需要演示爬虫重要的并发调度。其余的怎么发http请求 保存到什么数据库,自己定义一个函数,不就每个爬虫函数里面万能通用了?难道需要无数次重复写怎么换ip发请求吗? 还有人故意找茬说这汽车之家网站太简单了,没有包括破解,这个代码演示没有意义,框架是为了演示并发调度,搞一堆破解代码掺杂在里面没有必要。你用scrapy框架就能自动破解网站了吗? 我采用的是控制变量法耐对比不同框架写代码所需的行数,又没让scrapy爬加密网站用这个funboost爬简单网站导致对比不公平。 如果需要做破解加密,那么funboost集成破解流程肯定比scrapy集成破解要更容易随心所欲的写。 ``` ```python import requests from parsel import Selector from funboost import boost, BrokerEnum, BoosterParams @boost(BoosterParams(queue_name='car_home_list', broker_kind=BrokerEnum.REDIS_ACK_ABLE, max_retry_times=5, qps=10)) def crawl_list_page(news_type, page, do_page_turning=False): """ 函数这里面的代码是用户想写什么就写什么,函数里面的代码和框架没有任何绑定关系 例如用户可以用 urllib3请求 用正则表达式解析,没有强迫你用requests请求和parsel包解析。 """ url = f'https://www.autohome.com.cn/{news_type}/{page}/#liststart' resp_text = requests.get(url).text # 此处你可以换成你自己封装好的 my_request 请求函数来换代理ip请求网页 sel = Selector(resp_text) for li in sel.css('ul.article > li'): if len(li.extract()) > 100: # 有的是这样的去掉。