由头

公司内部有人前一阵推荐cursor,前一段时间windsurf又因为免费赠送PRO会员火了一阵。

分别试用了一下,交互确实很好,但是项目代码多了以后cursor和windsurf咔咔一顿乱改,经常把自己改的代码来回反复修改,无头苍蝇一样乱撞,项目代码超过300行基本就很难保证稳定了

最近vscode的Github Copilot插件更新了,也增加了和cursor类似的交互方式。试用下来,Github Copilot的代码逻辑比cursor和windsurf更加稳定,生成的代码更加符合预期。

用我个人最喜欢的一个爬虫项目来测试

给Github Copilot的指令:

创建一个rust爬虫项目
项目有几个角色
1. Requester,用来构造一个url的请求,这个角色包含url、ua、cookie、是否使用代理ip等信息
2. Downloader,用来下载一个url的内容,这个角色包含一个Requester,用来构造请求,然后下载内容,包含配置信息,可以配置并发数、间隔时间等
3. Parser,用来解析一个url的内容,这个角色包含一个Downloader,用来下载内容,然后解析内容
4. Saver,用来保存一个url的内容,这个角色包含一个Parser,用来解析内容,然后保存内容
5. Scheduler,Requester、Downloader、Parser、Saver的调度器,用来调度这几个角色的工作

然后让它补充一个README.md

补充一个README.md,内容包括项目特性、设计架构、使用示例、待办事项等

然后这个项目就一把可以上传github了,并且基本不需要太多修改就可以cargo run

补充中文注释

当前项目的所有rust代码增加中文注释

补充完注释基本就是一个可读性非常好的项目了,而且Github Copilot生成的代码质量和cursor、windsurf生成的代码质量相比要好很多

最终生成的代码仓库地址

Github Copilot生成的代码:https://github.com/pengxiaochao/rust_crawler

示例代码

use rust_crawler::{Downloader, Parser, Scheduler, Saver, downloader::DownloaderConfig};
use std::sync::Arc;
use anyhow::Result;
use futures::future::join_all;

/// 简单解析器:将内容直接作为字符串返回
#[derive(Clone)]
struct SimpleParser;

#[async_trait::async_trait]
impl Parser for SimpleParser {
    type Output = String;
    async fn parse(&self, content: &str) -> Result<Self::Output> {
        Ok(content.to_string())
    }
}

/// 简单保存器:打印内容长度
struct SimpleSaver;

#[async_trait::async_trait]
impl Saver<String> for SimpleSaver {
    async fn save(&self, data: String) -> Result<()> {
        println!("Saved content length: {}", data.len());
        Ok(())
    }
}

/// 主函数:启动爬虫系统
#[tokio::main]
async fn main() -> Result<()> {
    // 待爬取的 URL 列表
    let urls = vec![
        "https://www.baidu.com".to_string(),
        "https://www.163.com".to_string(),
        "https://www.au92.com".to_string(),
    ];

    // 初始化组件
    let config = DownloaderConfig::new(3, 1000);
    let downloader = Arc::new(Downloader::with_config(config));
    let mut scheduler = Scheduler::<SimpleParser>::new(10);
    let parser = Arc::new(SimpleParser);
    let saver = Arc::new(SimpleSaver);

    // 分离调度器
    let (sender, receiver) = scheduler.split();
    let sender = Arc::new(sender);
    let receiver = Arc::new(tokio::sync::Mutex::new(receiver));

    // 添加URLs到调度器
    sender.add_requests(urls).await?;

    // 启动下载任务
    let download_tasks = (0..3).map(|_| {
        let downloader = downloader.clone();
        let receiver = receiver.clone();
        let sender = sender.clone();
        let parser = parser.clone();
        
        tokio::spawn(async move {
            loop {
                let req = {
                    let mut receiver = receiver.lock().await;
                    receiver.get_request().await
                };
                
                match req {
                    Some(req) => {
                        if let Ok(content) = downloader.download(&req).await {
                            if let Ok(parsed) = parser.parse(&content).await {
                                let _ = sender.add_parsed_data(parsed).await;
                            }
                        }
                    }
                    None => break,
                }
            }
        })
    });

    // 启动保存任务
    let save_tasks = (0..3).map(|_| {
        let receiver = receiver.clone();
        let saver = saver.clone();
        
        tokio::spawn(async move {
            loop {
                let data = {
                    let mut receiver = receiver.lock().await;
                    receiver.get_parsed_data().await
                };
                
                match data {
                    Some(data) => {
                        let _ = saver.save((*data).clone()).await;
                    }
                    None => break,
                }
            }
        })
    });

    // 等待所有任务完成
    let all_tasks = download_tasks.chain(save_tasks).collect::<Vec<_>>();
    join_all(all_tasks).await;

    Ok(())
}