我的编程空间,编程开发者的网络收藏夹
学习永远不晚

ChatGPT最小元素的设计方法是什么

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

ChatGPT最小元素的设计方法是什么

本篇内容介绍了“ChatGPT最小元素的设计方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

膨胀的野心与现实的窘境

上一节随着我能抓openai的列表之后,我的野心开始膨胀,既然我们写了一个框架,可以开始写面向各网站的爬虫了,为什么只面向ChatGPT呢?几乎所有的平台都是这么个模式,一个列表,然后逐个抓取。那我能不能把这个能力泛化呢?可不可以设计一套机制,让所有的抓取功能都变得很简单呢?我抽取一系列的基础能力,而不管抓哪个网站只需要复用这些能力就可以快速的开发出爬虫。公司内的各种平台都是这么想的对吧?

那么我们就需要进行设计建模,如果按照正常的面向对象,我可能会这么设计建模:

ChatGPT最小元素的设计方法是什么

看起来很美好不是吗?是不是可以按照设计去写代码了?其实完全是扯淡,魔鬼隐藏在细节中,每个网站都有各种复杂的HTML、他们可能是简单的列表,也可能是存在好几个iframe,而且你在界面上看到的列表和你真正点开的又不一样,比如说:

  • 有的小说网站,它的列表上假如有N个列表项,但是你真的点击去之后,你会发现有的章节点击他只有一半内容,再点下一页的时候它会调到一个不在列表页上的展示的页面,展示后半段内容,而你如果只根据列表链接去抓,你会丢掉这后半段内容。

  • 有的网站会在你点了几个页面后随机出现一个按钮,点击了才能展开后续内容,防止机器抓取。你不处理这种情况,直接去抓就抓不全。

  • 而有的网站根本就是图片展示文本内容,你得把图片搞下来,然后OCR识别,或者插入了各种看不见的文本需要被清洗掉。

而且每个网站还会升级换代,他们一升级换代,你的抓取方式也要跟着变。 等等等等……而且所有这些要素之间还可以排列组合:

ChatGPT最小元素的设计方法是什么

所以最上面的那个建模只能说过于简化而没有用处,起码,以前是这样的。

在以前,我们可能会进一步完善这个设计,得到一系列复杂的内部子概念、子机制、子策略,比如:

  • 反防抓机制

  • 详情分页抓取策略

  • 清洗机制

然后对这些机制进行组合。

然而这并不会让问题变简单,人们总是低估胶水代码的复杂度,最终要么整个体系非常脆弱,要么就从胶水处开始腐化。

新时代,新思路

那么在今天,我们有没有什么新的做法呢?我们从一个代码示例开始讲起,比如,我这里有一个抓取某小说网站的代码:

const fs = require('fs/promises');async function main() {    const novel_section_list_url = 'https://example.com/list-1234.html';    await driver.goto(novel_section_list_url);    const novelSections = await driver.evaluate(() => {        let title = getNovelTitle(document)        let section_list = getNovelSectionList(document);        return {            title, section_list        }        function getNovelTitle(document) {            return document.querySelector("h2.index_title").textContent;        }        function getNovelSectionList(document) {            let result = [];            document.querySelectorAll("ul.section_list>li>a").forEach(item => {                const { href } = item;                const name = item.textContent;                result.push({ href, name });            });            return result;        }    });    console.log(novelSections.section_list.length);    const batchSize = 50;    const title = novelSections.title;    let section_list = novelSections.section_list;    if (intention.part_fetch) {        section_list = novelSections.section_list.slice(600, 750);    }    await batchProcess(section_list, batchSize, async (one_batch, batchNumber) => {        await download_one_batch_novel_content(one_batch, driver);        async function download_one_batch_novel_content(one_batch, driver) {            let one_text_file_content = "";            for (section of one_batch) {                await driver.goto(section.href);                await driver.waitForTimeout(3000);                const section_text = await driver.evaluate(() => {                    return "\n\n" + document.querySelector("h2.chapter_title").textContent                        + "\n"                        + document.querySelector("#chapter_content").textContent;                });                one_text_file_content += section_text;            }            await fs.writeFile(`./output/example/${title}-${batchNumber}.txt`, one_text_file_content);        }    });}main().then(() => { });async function batchProcess(list, batchSize, asyncFn) {    const listCopy = [...list];    const batches = [];    while (listCopy.length > 0) {        batches.push(listCopy.splice(0, batchSize));    }    let batchNumber = 12;    for (const batch of batches) {        await asyncFn(batch, batchNumber);        batchNumber++;    }}

在实际工作中这样的代码应该是比较常见的,由于上述的设计没有什么用处,我们经常见到的就是另一个极端,那就是代码写的过于随意,整个代码的实现变得无法阅读,当我想要做稍微地调整,比如说我昨天抓了100个,今天接着从101个往后抓,就要去读代码,然后从代码中看改点什么好让这个抓取可以从101往后抓。

那在以前呢,我们就要像上面说的要设计比较精密的机制,而越是精密的机制,就越不健壮。而且,以我的经验,你想让人们使用那么精细的机制也不好办,因为大多数人的能力并不足以驾驭精细的机制。

而在今天,我们可以做的更粗放一些。

首先,我们意识到有些代码,准确的说,是有些变量,是我们经常修改的,所以我们在不改变整体结构的情况下,我们把这些变量提到上面去,变成一个变量:

//意图描述const intention = {    list_url:'https://example.com/list-1234.html',    batchSize: 50,    batchStart: 12,    page_waiting_time: 3000,    part_fetch:{ //如果全抓取,就注释掉整个part_fetch属性        from:600,//不含该下标        to:750    },    output_folder: "./output/example"}const fs = require('fs/promises');const driver = require('../util/driver.js');async function main() {    const novel_section_list_url = intention.list_url;    await driver.goto(novel_section_list_url);    const novelSections = await driver.evaluate(() => {        let title = getNovelTitle(document)        let section_list = getNovelSectionList(document);        return {            title, section_list        }        function getNovelTitle(document) {            return document.querySelector("h2.index_title").textContent;        }        function getNovelSectionList(document) {            let result = [];            document.querySelectorAll("ul.section_list>li>a").forEach(item => {                const { href } = item;                const name = item.textContent;                result.push({ href, name });            });            return result;        }    });    console.log(novelSections.section_list.length);    const batchSize = intention.batchSize;    const title = novelSections.title;    let section_list = novelSections.section_list;    if (intention.part_fetch) {        section_list = novelSections.section_list.slice(intention.part_fetch.from, intention.part_fetch.to);    }    await batchProcess(section_list, batchSize, async (one_batch, batchNumber) => {        await download_one_batch_novel_content(one_batch, driver);        async function download_one_batch_novel_content(one_batch, driver) {            let one_text_file_content = "";            for (section of one_batch) {                await driver.goto(section.href);                await driver.waitForTimeout(intention.page_waiting_time);                const section_text = await driver.evaluate(() => {                    return "\n\n" + document.querySelector("h2.chapter_title").textContent                        + "\n"                        + document.querySelector("#chapter_content").textContent;                });                one_text_file_content += section_text;            }            await fs.writeFile(`${intention.output_folder}/${title}-${batchNumber}.txt`, one_text_file_content); //一个批次一存储        }    });}main().then(() => { });async function batchProcess(list, batchSize, asyncFn) {    const listCopy = [...list];    const batches = [];    while (listCopy.length > 0) {        batches.push(listCopy.splice(0, batchSize));    }    let batchNumber = intention.batchStart;    for (const batch of batches) {        await asyncFn(batch, batchNumber);        batchNumber++;    }}

于是我们把程序分成了两部分结构:

ChatGPT最小元素的设计方法是什么

接下来我会发现,在网站不变的情况下,下面这个意图执行代码相当的稳定。我经常需要做的不管是偏移量的计算,还是修改抓取目标等等,这些都只需要修改上面的意图描述数据结构即可。而且我们可以做进一步的封装,得到下面的代码(下面的JsDoc也是ChatGPT给我写的):

//意图执行module.exports =  (intention, context) => {    Object.assign(this, context);    const {fs,console} = context;    async function main() {        const novel_section_list_url = intention.list_url;        await driver.goto(novel_section_list_url);        const novelSections = await driver.evaluate(() => {            let title = getNovelTitle(document)            let section_list = getNovelSectionList(document);            return {                title, section_list            }            function getNovelTitle(document) {                return document.querySelector("h2.index_title").textContent;            }            function getNovelSectionList(document) {                let result = [];                document.querySelectorAll("ul.section_list>li>a").forEach(item => {                    const { href } = item;                    const name = item.textContent;                    result.push({ href, name });                });                return result;            }        });        console.log(novelSections.section_list.length);        const batchSize = intention.batchSize;        const title = novelSections.title;        // const section_list = novelSections.section_list.slice(0, 3);        let section_list = novelSections.section_list;        if (intention.part_fetch) {            section_list = novelSections.section_list.slice(intention.part_fetch.from, intention.part_fetch.to);        }        await batchProcess(section_list, batchSize, async (one_batch, batchNumber) => {            await download_one_batch_novel_content(one_batch, driver);            async function download_one_batch_novel_content(one_batch, driver) {                let one_text_file_content = "";                for (section of one_batch) {                    await driver.goto(section.href);                    await driver.waitForTimeout(intention.page_waiting_time);                    const section_text = await driver.evaluate(() => {                        return "\n\n" + document.querySelector("h2.chapter_title").textContent                            + "\n"                            + document.querySelector("#chapter_content").textContent;                    });                    one_text_file_content += section_text;                }                await fs.writeFile(`${intention.output_folder}/${title}-${batchNumber}.txt`, one_text_file_content); //一个批次一存储            }        });    }    main().then(() => { });    async function batchProcess(list, batchSize, asyncFn) {        const listCopy = [...list];        const batches = [];        while (listCopy.length > 0) {            batches.push(listCopy.splice(0, batchSize));        }        let batchNumber = intention.batchStart;        for (const batch of batches) {            await asyncFn(batch, batchNumber);            batchNumber++;        }    }}

于是我们就有了一个稳定的接口将意图的描述和意图的执行彻底分离,随着我对我的代码进行了进一步的整理后发现,这个意图描述结构竟然相当的通用,我写的好多网站的抓取代码竟然都可以抽取出这样一个结构。 于是我们可以进一步抽象,到了一种适用于我特定领域的DSL,类似下面的结构:

ChatGPT最小元素的设计方法是什么

到此为止,我的意图描述和意图执行彻底解耦,意图执行变成了意图描述中的一个属性,我只需要写一个引擎,根据意图描述中entrypoint的属性值,加载对应的函数,然后将意图数据传给他就可以了,大概的代码如下:

const intentionString = await fs.readFile(templatePath, 'utf8');const intention = yaml.load(intentionString);const intention_exec = require(intention.entrypoint);intention_exec(intention, context);

而我们的每一个意图执行的代码,可以有自己的不同变化原因,不管是网站升级了,还是我们要抓下一个网站了,我们只需要把HTML扔给ChatGPT,他就可以帮我们生成对应的意图执行代码。哪怕我们想基于一些可以复用库函数,比如之前说的反防抓、反详情页分页机制封装的库函数,他也可以给我们生成胶水代码把这些函数粘起来(具体的手法我们在后续的文章里讲),所有这一切的变化,都可以用ChatGPT生成代码这一步解决。那么所谓的在胶水层腐化的问题也就不存在了。

很有趣的是,在我基于该结构的DSL得到一组实例之后,我很快就开始产生了在DSL这一层的新需求,比如:

  • DSL文件的管理需求,因为人总是很懒的,而且我只有业余时间写点这些东西,不能保证自己一直记得哪个网站对应哪个文件,然后怎么设置。

  • 我还希望能够根据我本地已经抓的内容和智能生成偏移量

  • 我也希望能定时去查看更新然后生成抓取意图。

这一切都是很有价值的需求,而如果我们没有一个稳定的下层DSL结构,我们这些更上层需求也注定是不稳定的。

而有了这个稳定的DSL结构后,我们回过头来看我们的设计,其实是在更大的尺度上实现了面向对象设计中的开闭原则,尽管扩展需要大量的代码,而这些代码却并不需要人来写,所以效率依然很高。

“ChatGPT最小元素的设计方法是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

ChatGPT最小元素的设计方法是什么

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

ChatGPT最小元素的设计方法是什么

本篇内容介绍了“ChatGPT最小元素的设计方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!膨胀的野心与现实的窘境上一节随着我能抓o
2023-07-05

python元组添加元素的方法是什么

Python中元组是不可变的,即一旦创建,就不能修改。因此,无法直接向元组中添加元素。但是,可以通过以下方法来间接实现向元组中添加元素的效果:使用运算符 "+" 连接两个元组:可以将两个元组连接起来生成一个新的元组。tuple1 = (1
2023-10-23

react获取数组最后一个元素的方法是什么

可以使用数组的`length`属性和索引来获取数组的最后一个元素。具体的方法有两种:1. 使用索引:使用数组的`length`属性减1作为索引来访问最后一个元素。```javascriptconst arr = [1, 2, 3, 4, 5
2023-10-07

react创建元素的方法是什么

react创建元素的方法:1、使用JSX语法创建React元素,其语法如“const element = <h1>Hello, world</h1>;”;2、通过“React.createElement(type,props,children)”语法创建React元素。
2023-05-14

golang map删除元素的方法是什么

在Go语言中,可以使用`delete()`函数来删除map中的元素。`delete()`函数的语法如下:```godelete(map, key)```其中,`map`是要删除元素的map,`key`是要删除的元素的键。例如:```gopa
2023-10-12

5种隐藏元素的方法是什么

5种隐藏元素的方法分别是:1、使用CSS的display属性;2、使用CSS的visibility属性;3、使用CSS的opacity属性;4、使用CSS的position和clip属性;5、使用HTML的hidden属性。详细介绍:1、使
5种隐藏元素的方法是什么
2023-11-20

css中元素的定位方法是什么

css 中元素定位方法有:正常流定位:元素保持在正常流中。相对定位:元素相对于其原始位置移动。浮动定位:元素从流中移除,但保持宽度。绝对定位:元素从流中移除,相对于已定位祖先元素定位。固定定位:元素从流中移除,相对于浏览器窗口定位。网格布局
css中元素的定位方法是什么
2024-04-26

golang切片删除元素的方法是什么

在Go语言中,可以使用切片的特性来删除元素。以下是几种常见的方法。方法一:通过索引删除元素可以使用切片的切片操作符来删除指定索引的元素。例如,要删除切片中的第一个元素,可以使用以下代码:slice := []int{1, 2, 3,
golang切片删除元素的方法是什么
2024-02-29

php数组添加元素的方法是什么

在PHP中,有多种方法可以向数组中添加元素。以下是一些常用的方法:1. 使用数组索引添加元素:```php$array = array();$array[0] = "element1";$array[1] = "element2";```2
2023-08-17

python列表移除元素的方法是什么

Python中可以使用列表的`remove()`方法来移除元素。该方法接受一个参数,表示要移除的元素,如果列表中存在多个相同的元素,只会移除第一个匹配的元素。如果要移除的元素不存在于列表中,会抛出ValueError错误。另外,还可以使用
2023-10-25

string数组添加元素的方法是什么

在Python中,可以使用`append()`方法向字符串数组添加元素。具体用法如下:```pythonstring_array = [] # 创建一个空的字符串数组string_array.append("element1") # 向
2023-08-15

matlab矩阵元素赋值的方法是什么

在MATLAB中,可以使用以下方法来给矩阵元素赋值:1. 使用下标赋值。例如,对于一个矩阵A,可以使用A(i, j) = value的形式来给矩阵中的元素赋值,其中i和j分别表示行和列的下标,value表示需要赋给该元素的值。2. 使用冒号
2023-09-21

java循环删除list元素的方法是什么

在Java中,可以使用迭代器来循环遍历并删除List中的元素。以下是一个示例:```javaList list = new ArrayList();list.add(1);list.add(2);list.add(3);Iterator i
2023-08-26

Python从列表中取元素的方法是什么

Python从列表中取元素有几种常用的方法:1. 通过索引值获取元素:可以使用方括号[]来访问列表中的元素,索引值从0开始。例如,对于列表`my_list`,要获取第一个元素可以使用`my_list[0]`,获取第二个元素可以使用`my_l
2023-09-26

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录