解决方案:软件测试---------数据采集
优采云 发布时间: 2022-11-08 04:05解决方案:软件测试---------数据采集
数据采集,又称数据采集,是使用设备从系统外部采集数据并输入系统的接口。您说的数据采集 工作现在可以由软件机器人完成。它可以模拟手动操作,自动化你需要的采集字段信息。最后整理出一张Excel表格或其他数据库表格。
数据采集,很多都是在中间环节执行的,没有接口测试,方便验证数据的正确性。开发需要写一个demo(模拟前端接口发送请求的程序)进行测试。这个项目没有任何借口,开发包Test成jar(jar包是一些别人已经写好的类,然后打包这些类。你可以将这些jar包导入到你的项目中,然后就可以直接使用了这些jar包中的类、属性和方法..),所以测试这类项目需要一定的代码知识,才能测试并快速发现问题。
测试前(每个项目都要先了解业务才能做好测试):
①了解需求文档的需求和注意点(业务的顺序,业务逻辑是否清晰,从而明确测试思路,也提高工作效率)。
②由于没有来自前端接口的支持,测试人员需要提前和开发人员沟通,让他们在沟通中更快的了解业务。在数据方面,开发总是比测试人员更彻底,业务数据比测试人员更彻底。清楚了,毕竟整个逻辑都是开发者实现的,所以沟通很重要。
③数据量大的项目,需要提前规划好时间。每个项目都有时间限制。管理较好的公司可以按计划行事。管理不善可能留给测试人员测试的时间不多,因为处理代码需要一点时间,所以这些都需要提前计划和监督。
测试时(和一般项目一样,主要业务需要测试):
① 明确业务逻辑,准备数据。开发者在测试的时候一般都有数据,但是我们需要根据需求文档准备数据。其实这有点像接口数据测试,可以发现很多bug(比如对字段没有限制或者限制)。错误)。
②因为是处理数据,所以需要验证是否实现了数据的增删改查功能(在数据库中查询)。
③程序出错时,提示信息是否合理。
④很多验证需要基于具体的业务,了解业务是成功的一半。
数据采集测试需要测试人员的呵护,还有很多需要学习的地方。你要知道,学无止境,你需要提高你的测试技能,你可以关注我。
解决方案:使用 Rust 做异步数据采集的实践
Data采集,最完整最成熟的生态工具,我觉得Python,尤其是强大成熟的Scrapy库,是很多项目和产品的必备。作者曾在大数据项目的数据采集部分与团队同事合作。不管项目中的愿景如何,笔者认为scrapy是完全满意的。
本文是在Rust生态中使用data采集相关crate进行data采集的实践,目的如下:在新项目中统一Rust技术栈;我想试试 Rust 的性能优势,数据采集是否也有优势。
所以,这篇文章更多是讲 Rust 的生态实践,并不是说 Rust 在做数据方面比 Python 有优势采集。
好的,我们正在从头开始进行数据采集 完整练习,针对网站,采集 每周都使用 Rust。
创建项目
我们使用货物,创建一个新项目。在这个项目中,我们将使用 Rust 的异步运行时 async-std、HTTP 客户端库 reqwest、数据库 采集 库刮板和控制台输出文本颜色标记库 coloured。创建项目后,我们使用 cargo-edit crate 将它们添加为依赖项:
cargo-edit的安装和使用请参考文章《构建Rust异步GraphQL服务:基于潮汐+async-graphql+mongodb(一)——入门与Crate选择》
cargo new rust-async-crawl-example
cd ./rust-async-crawl-example
cargo add async-std reqwest scraper colored
复制
执行成功后,Cargo.toml文件manifest的dependencies区会有以上4个 crate。但是对于 async-std,在这个实践中,我们将使用它的属性特性;对于 reqwest,我们将启用其阻塞功能。我们修改 Cargo.toml 文件,最终内容如下:
[package]
name = "rust-crawl-week"
version = "0.1.0"
authors = ["zzy "]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-std = { version = "1.9.0", features = ["attributes"] }
reqwest = { version = "0.11.2", features = ["blocking"] }
scraper = "0.12.0"
colored = "2.0.0"
复制
简要设计
数据采集,我们绝不能局限于一个站点。因此,我们参考Python中库scrapy的思路,每个具体爬虫对应一个站点。因此,我们将文件结构组织为 main.rs 是执行入口;sites.rs 或站点模块是具体的站点爬取位置。在此示例中,我们只是 采集 站点,因此将其写入 sites.rs 文件。在实际项目产品中,推荐使用sites模块,该模块收录以各自站点命名的特定爬虫。
对于 采集 结果,我们想通过输出接口将其输入到控制台、数据库、文档(文本、excel等)。这些输出和写入接口也需要在一个统一的位置,以便后续扩展。
在此示例中,我们将其打印到控制台。并且在打印时,为不同的站点、标题和 url 链接着色。
因此,在这个实例中,最终的工程结构是:
此时,我们还没有编译构建,所以没有 Cargo.lock 文件,也没有目标目录。如果按照本文的做法,它们会在 cargo build 之后生成。下面将不作进一步解释。
main.rs
data采集 入口文件,其代码应尽量简洁明了。
mod sites;
#[async_std::main]
async fn main() {
// this-week-in-rust.org
match sites::this_week_in_rust_org().await {
Ok(site) => println!("{:#?}", site),
Err(_) => eprintln!("Error fetching this-week-in-rust.org data."),
}
// 其它站点
// ……
}
复制
对于多个站点,我们可以一个一个地增加,有利于后续的简单扩展。再说一遍:在这个例子中,我们只是 采集 站点,所以它被写在了 sites.rs 文件中。在实际项目产品中,推荐使用sites模块,该模块收录以各自站点命名的特定爬虫。
注意:println!("{:#?}", site),输出到控制台时,我们采用了Rust中默认的最美观可读的输出方式。这段代码是带注释的,因为我们稍后将迭代第一次显示如何不够“符合人体工程学”。
sites.rs第一次编码,采集数据并输出
首先,我们需要定义两个结构体,分别代表站点信息和采集目标数据信息(本例中为标题、url链接)。
#[derive(Debug)]
pub struct Site {
name: String,
stories: Vec,
}
#[derive(Debug)]
struct Story {
title: String,
link: Option,
}
复制
对于目标数据采集,我们的思路很简单,三步:
获取 HTML 文档;提取数据标题;提取数据 url 链接。
我们定义了这三个方法,在具体的站点爬虫this_week_in_rust_org中调用:
use reqwest::{blocking, Error};
use scraper::{ElementRef, Html, Selector};
use std::result::Result;
pub async fn this_week_in_rust_org() -> Result {
let s = Selector::parse("div.col-md-12 a").unwrap();
let body = get_html("https://this-week-in-rust.org/blog/archives/index.html").await?;
let stories = body
.select(&s)
.map(|element| Story {
title: parse_title(element),
link: parse_link(element),
})
.collect();
let site = Site {
name: "this-week-in-rust.org".to_string(),
stories,
};
Ok(site)
}
async fn get_html(uri: &str) -> Result {
Ok(Html::parse_document(&blocking::get(uri)?.text()?))
}
fn parse_link(element: ElementRef) -> Option {
let mut link: Option = None;
if let Some(link_str) = element.value().attr("href") {
let link_str = link_str.to_owned();
link = Some(link_str);
}
link
<p>
}
fn parse_title(element: ElementRef) -> String {
element.inner_html()
}</p>
复制
这段代码可读性很强,代码是最好的文档。注意获取HTML文档的get_html函数和爬虫调用函数this_week_in_rust_org是异步的,而提取标题的函数parse_link和函数parse_title不是。因为具体的提取是在一个数据解析过程中进行的,所以作者认为是不是异步的意义不大。当然,如果你有兴趣,也可以改成一个异步函数来进行性能对比。
第一次编码完成后,我们编译运行,看看一些输出结果:
有许多安装依赖项。如果耗时较长,请配置 Cargo 国内镜像源。
输出数据为json格式,文字无色差。非常适合其他输入调用接口。比如数据库和导出文件。但是对于人眼阅读来说,还不够友好。我们希望输出是标题及其链接。
二次编码,优化输出数据格式
在我们的第一个代码中,我们使用了 Rust 的默认 Display trait。我们要实现一个自定义的输出数据格式,也就是我们需要为 Site 和 Story 这两个结构实现一个自定义的 Display trait。
use colored::Colorize;
use std::fmt;
impl fmt::Display for Site {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "{}", self.name.blue().bold())?;
for story in &self.stories {
writeln!(f, "{}", story)?;
}
Ok(())
}
}
impl fmt::Display for Story {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.link {
Some(link) => write!(f, "\t{}\n\t\t({})", self.title.green(), link),
None => write!(f, "\t{}", self.title),
}
}
}
复制
此时,我们main.rs中的打印甚至不需要指定Display方法:
mod sites;
#[async_std::main]
async fn main() {
// this-week-in-rust.org
match sites::this_week_in_rust_org().await {
Ok(site) => println!("{}", site),
Err(_) => eprintln!("Error fetching this-week-in-rust.org data."),
}
// 其它站点
// ……
}
复制
现在让我们看看输出:
对于人眼阅读,这种方式比较适合,直接点击url链接即可。
感兴趣的朋友可以参考github上完整的代码仓库。
谢谢阅读!