项目测试时,为了测试常需要造假数据,经常要尽量的模拟真实环境,通常要费大量手工而且造出来的数据,而且通常手工造出来的看起来也很别扭,费时又费事,有没有更好的办法?有,Faker这个造数神器,可以满足你对模拟数据的所有需求。
Faker是一个神奇的python第三方库,可以帮助我们自动生成各种看似真实的假数据,例如:用户信息类、网络基础信息类、浏览器信息类、文件信息类、数字类、文本加密类、时间信息类、其他类别等。
可以在需要大量测试数据的场景下,通过脚本生成快速生成尽可能接近真实的测试数据,节约造数成本。
docs:https://faker.readthedocs.io/en/master/
github:https://github.com/joke2k/faker
版本:python3.5+
pip
安装:
$ pip3 install Faker
验证是否安装成功
$ faker --version
faker 5.6.1
命令行参数 | 说明 |
---|---|
-h ,--help |
显示帮助信息 |
--version |
显示版本信息 |
-o [output] |
输出重定向到指定文件 |
-l [LOCALE] ,--lang [LOCALE] |
指定使用的语言包,不同的语言包随机生成的不一样 |
-r [REPEAT] ,--repeat [REPEAT] |
指定生成的数量 |
-s [SEP] ,--sep [SEP] |
在生成结果中插入指定的分隔符 |
-i [INCLUDE ...] ,--include [INCLUDE ...] |
指定自定义假数据生成器 (这里填你的假数据生成器的包路径,进行一个导包操作) |
可以通过命令行调用faker,直接生成伪数据或者将伪数据写入到指定文件,等等。
指定语言包
# 指定语言包,简体中文,zh_CN,生成人物描述信息(profile)
$ faker -l zh_CN profile
{'job': '保险业务经理/主管', 'company': '天益传媒有限公司', 'ssn': '220106196002131103', 'residence': '河南省广州县上街澳门街P座 643244', 'current_location': (Decimal('-66.54')), 'blood_group': 'AB-', 'website': ['http://www.er.cn/', 'https://www.xiulan.cn/', 'https://www.shen.cn/', 'https://ot.cn/'], 'username': 'vzhu', 'name': '王萍', 'sex''F', 'address': '山东省永安市黄浦齐齐哈尔路O座 451034', 'mail': '[email protected]', 'birthdate': datetime.date(1967, 7, 15)}
# 生成只有name,sex,birthdate,address的人物描述信息(profile)
$ faker -l zh_CN profile name,sex,birthdate,address
{'name': '张岩', 'sex': 'F', 'address': '甘肃省武汉县龙潭江街e座 854752', 'birthdate': datetime.date(1939, 12, 6)}
# 不指定地区,默认英文,en_US
$ faker profile name,sex,birthdate,address
{'name': 'Scott Griffin', 'sex': 'M', 'address': '4788 Stanley Motorway\nBellside, NH 64281', 'birthdate': datetime.date(1988, 12, 12)}
指定数量的数据,指定生成结果的分隔符
# 生成五个手机号码,默认分隔符为换行符
$ faker -l zh_CN phone_number -r 5
13206790377
18066048764
15056230123
15566115750
15903470404
# 指定分割隔符,不指定则默认为换行符
$ faker -l zh_CN phone_number -r 5 -s ','
18941094668,
15098259791,
14708023002,
13798344417,
15726018159,
生成数据输出到指定文件
$ faker -l zh_CN phone_number -r 100 -s '' -o /faker_demo/phone_number.xls
使用faker.Faker()
创建和初始化一个faker
生成器,可通过访问你想要的数据类型来命名的属性生成的数据。
from faker import Faker
# 1.创建和初始化一个faker生成器,地区为中国
fake = Faker('zh_CN')
# 生成name假数据
name = fake.name()
print(f'Randomly generate fake name, 生成name假数据:{name}')
# 生成地址假数据
address = fake.address()
print(f'Randomly generate fake address, 生成地址假数据:{address}')
# 生成一段废话
text = fake.text()
print(f'Randomly generate rubbish, 生成一段废话:{text}')
输出
Randomly generate fake name:李红
Randomly generate fake address:湖北省刚县海陵兴城路E座 381931
Randomly generate rubbish:现在可以类型不断方法更多日本.一般你们发表简介.
次数行业建设对于我的.只是一定社会汽车.大小本站所有软件.
继续什么游戏用户.服务空间之后完成商品记者网上.环境全国而且汽车.
业务无法这里以上成功.评论位置电话可是学习必须自己但是.能够方法法律正在软件城市一定而且.
电子名称可是正在成为加入留言.次数怎么提高程序登录.
目前faker支持78种语言包,Faker可以将语言环境作为可选参数,以返回本地化的数据。
zh_CN - Chinese (China Mainland)
zh_TW - Chinese (China Taiwan)
en_US - English (United States)
fr_FR - French
ko_KR - Korean
ru_RU - Russian
ja_JP - Japanese
v3.0.0
版本后支持支持多种语言环境。
locals = ['zh_CN', 'ja_JP', 'ko_KR', 'en_US']
fake = Faker(locals)
for _ in range(10):
print(fake.name())
输出
안지현
鈴木 真綾
中島 裕樹
刘华
小林 里佳
杜桂芝
魏红梅
赵桂芳
김예지
佐藤 翼
个别方法具有针对性,与语言项有关。
fake.name() # 姓名,赵飞
fake.last_name() # 姓,庞
fake.first_name() # 名字,杰
# 只有部分语言包有罗马文方法
fake.romanized_name() # 姓名(罗马文),Jing Du
fake.last_romanized_name() # 姓(罗马文),Zhong
fake.first_romanized_name() # 名字(罗马文),Yang
fake.name_female() # 姓名(女),Mary Garza
fake.last_name_female() # 姓(女),Crawford
fake.first_name_female() # 名字(女),Catherine
fake.prefix_female() # 称谓(女),英文国家使用较多, Ms./Miss./Mrs.
fake.name_male() # 姓名(男),James White
fake.last_name_male() # 姓(男),Garcia
fake.first_name_male() # 名字(男),Michael
fake.prefix_male() # 称谓(女),英文国家使用较多, Mr.
fake.country_calling_code() # 国际号段,'+229'
fake.msisdn() # 完整手机号码(加了国家和国内区号)
fake.phone_number() # 手机号,'13440672022'
fake.phonenumber_prefix() # 号段,'137'(中文包有该方法)
# 日期字符串(可设置格式和最大日期)
fake.date(pattern="%Y-%m-%d", end_datetime=None) # '1994-09-30'
# 时间字符串(可设置格式和最大日期时间)
fake.time(pattern="%H:%M:%S", end_datetime=None) # '01:13:52'
# 出生日期,return的实际是fake.date_time_ad().date()
fake.date_of_birth(tzinfo=None, minimum_age=0, maximum_age=115) # 1946-02-09
# 日期对象(可设置限定范围),return的实际是fake.date_between_dates()
fake.date_between(start_date="-30y", end_date="today") # 2003-03-31
# 日期对象(可设置限定范围),return的实际是fake.date_time_between_dates().date()
fake.date_between_dates(date_start=None, date_end=None) # 2021-01-26
# 日期时间对象(可设置范围)
fake.date_time_between(start_date="-30y", end_date="now", tzinfo=None) # 2011-09-09 03:05:15
# 日期时间对象(可设置范围)
fake.date_time_between_dates(datetime_start=None, datetime_end=None, tzinfo=None) # 2021-01-26 20:56:13
# 未来日期,return的实际是fake.date_between_date(start_date='+1d', end_date=end_date),默认是未来30天的某天
fake.future_date(end_date="+30d", tzinfo=None) # 2021-02-07
# 未来日期时间,return的实际是fake.date_time_between(start_date='+1s', end_date=end_date, tzinfo=tzinfo)
# 默认是当前时间后1秒-30天后的某个时间
fake.future_datetime(end_date="+30d", tzinfo=None) # 2021-02-03 05:59:43
# 过去日期,return的实际是fake.date_between(start_date=start_date, end_date='-1d'),默认是过去30天-昨天的某天
fake.past_date(start_date="-30d", tzinfo=None) # 2021-01-21
# 过去日期时间,return的实际是fake.self.date_time_between(start_date=start_date, end_date='-1s', tzinfo=tzinfo)
# 默认是默认是前30天-当前时间前1秒的某个时间
fake.past_datetime(start_date="-30d", tzinfo=None) # 2021-01-04 14:53:27
province(),district(),city_name(),方法适用中国,但不适用美国及其他一些国家,具体可查看Localized Providers中的方法。
# 地址,address_formats = ("{{province}}{{city}}{{district}}{{street_address}} {{postcode}}",)
fake.address() # '湖北省荆门县安次巢湖街L座 421492'
fake.country() # 国家名称,'乌克兰'
fake.country_code(representation="alpha-2") # 国家二字码,三字码(representation="alpha-3"),'BZ'
fake.city() # 完整城市名(city_suffixes = ("市", "县")),'福州县'
fake.city_name() # 城市名字(不带市县),哈尔滨
fake.city_suffix() # 城市后缀名(中文简体,city_suffixes = ("市", "县"))
fake.district() # 区,'徐汇'
fake.postcode() # 邮编,'761944'
fake.province() # 省,台湾省
fake.street_address() # 街道地址(street_suffixes = ("街", "路")),'南昌街e座'
fake.street_name() # 街道名称,'沈阳路'
fake.street_suffix() # 街道后缀名,'街'
fake.building_number() # 楼名,'v座'
fake.ean(length=13) # 自定义位数条码,只能选8或者13,'9133134950963'
fake.ean13() # 13位条码,'8190173920161'
fake.ean8() # 8位条码,'12771363'
fake.ssn(min_age=18, max_age=90) # 身份证号码,'350525197603116544'
可以利用这些方法,快速定制特定规则的字符串,在自定义组件中可以灵活使用。
根据text规则生成一个字符串,其中每个占位符都被替换
# 每个(#)替换为随机数字(0至9),每个(?)替换为letters中的随机字符
fake.bothify(text='??####', letters=string.ascii_uppercase) # XF3735
# 每个(?)替换为letters中的随机字符
fake.lexify(text='????', letters=string.ascii_letters) # YUVD
# 每个(#)替换为随机数字(0至9),每个(%)替换为随机的非零数字(1到9),每个(!)替换为随机数字或空字符串,每个(@)处替换为随机的非零数字或空字符串
fake.numerify(text='# % ! @') # 5 7 7 1
在IDE中右键查看
name()
的具体实现逻辑,提示“Cannot find declaration to go on”,没有跳转到具体实现逻辑中。这与Faker的工厂设计模式有关。
上述介绍的几种造数方法实际上都是通过工厂模式,将所有的方法和属性加载到对应的语言包中,也就说Faker的属性和方法实际是在另外一个地方存放着,在使用的时候在拿过来,所以Faker的本身类看起来简洁。
那么外部的语言包都长什么模样?
具体方法的内部表现形式是如何的?
可以发现基本是以元祖的方式存放的原始数据,我们方法运行后最终的结果都是来自于此,大部分方法的实现逻辑是使用random
这个基本库来实现的。
可以看到在
faker.providers.address.Provider
中,provinces
是包含了台湾省、香港特别行政区、澳门特别行政区,国土没争议实锤,哈哈哈!
具体源码解析可以参考:https://www.cnblogs.com/pujenyuan/p/12615835.html
默认为
en_US
语言包,providers
分为标准providers
的和本土化providers
,本土化providers
个别方法的会有差异或者有所增加,具体可以查看具体语言包的providers
的实现。
类别 | Standard Providers |
---|---|
faker.providers.BaseProvider |
BaseProvider |
地址 | address |
汽车 | automotive |
银行 | bank |
条形码 | barcode |
颜色 | color |
公司 | company |
信用卡 | credit_card |
货币 | currency |
时间 | date_time |
文件 | file |
坐标 | location |
网络 | internet |
图书编号 | bookNo |
工作 | job |
图像 | paragraph |
编码相关 | encoding |
人物 | person |
电话号码 | phone |
档案相关 | profile |
python相关 | python |
身份证相关 | ssn |
用户代理相关 | user_agent |
第三方贡献的开源的providers组件:https://faker.readthedocs.io/en/master/communityproviders.html
faker
除了内置的provider
方法,还可以根据自身需求定制provider
,通过add_provider()
方法增加自定义组件,满足造数需求,自定义的组件需要继承自 BaseProvider
。
此处例子是仿造faker本身的风格,新建一个自定义组件集合包
customize_providers
,在这个包内创建具体自定义组件,组件放在__init__.py
文件中。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import string
from faker.providers import BaseProvider
class Provider(BaseProvider):
carrier_formats = ('??', '?#')
flight_no_suffix_formats = ('####', '###')
flight_no_formats = ('{{carrier}}{{flight_no_suffix}}',)
aircraft_codes = ('B###', )
ac_reg_formats = ('B####', )
bay_formats = ('##', '###', '?#', '?##')
def carrier(self):
"""
航司, @example 'O3'
:return:
"""
return self.bothify(self.random_element(self.carrier_formats), string.ascii_uppercase)
def flight_no_suffix(self):
"""
航班号除航司后几位字符, @example '5243'
:return:
"""
return self.bothify(self.random_element(self.flight_no_suffix_formats), string.ascii_uppercase)
def flight_no(self, carrier=None):
"""
航班号, @example 'HA5806'
:param carrier:
:return:
"""
pattern = self.random_element(self.flight_no_formats)
if carrier:
pattern = f'{carrier}####'
return self.numerify(pattern)
return self.generator.parse(pattern)
def aircraft_code(self):
"""
机型, @example 'B052'
:return:
"""
return self.numerify(self.random_element(self.aircraft_codes))
def ac_reg(self):
"""
机号, @example 'B7705'
:return:
"""
return self.numerify(self.random_element(self.ac_reg_formats))
def bay(self):
"""
机位, @example 'O9'
:return:
"""
return self.bothify(self.random_element(self.bay_formats), string.ascii_uppercase)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import json
from typing import AnyStr, Dict
from faker import Faker
from customize_providers.flight import Provider as FlightProvider
fake = Faker('zh_CN')
# 将自定义provider添加至fake
fake.add_provider(FlightProvider)
class Flight(object):
def __init__(self, carrier: AnyStr = None):
"""
航班对象
:param carrier:
"""
self.carrier = fake.carrier()
if carrier:
self.carrier = carrier
self.flight_no = fake.flight_no(self.carrier)
self.ac_type = fake.aircraft_code()
self.ac_reg = fake.ac_reg()
self.land_bay = fake.bay()
self.off_bay = fake.bay()
def get_flight_info(self) -> Dict:
return self.__dict__
flight = Flight()
flight_info = flight.get_flight_info()
print(json.dumps(flight_info, ensure_ascii=False, indent=4))
输出
{
"carrier": "T9",
"flight_no": "T96621",
"ac_type": "B462",
"ac_reg": "B4752",
"land_bay": "744",
"off_bay": "B4"
}
有些场景下,可能需要生成相同的数据集,可以通过seed()
生成相同的种子,使用相同版本的faker
和seed
调用相同的方法将产生相同的结果。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import json
from typing import AnyStr, Dict
from faker import Faker
from customize_providers.flight import Provider as FlightProvider
fake = Faker('zh_CN')
# 将自定义provider添加至fake
fake.add_provider(FlightProvider)
class Flight(object):
def __init__(self, carrier: AnyStr = None):
"""
航班对象
:param carrier:
"""
self.carrier = fake.carrier()
if carrier:
self.carrier = carrier
self.flight_no = fake.flight_no(self.carrier)
self.ac_type = fake.aircraft_code()
self.ac_reg = fake.ac_reg()
self.land_bay = fake.bay()
self.off_bay = fake.bay()
def get_flight_info(self) -> Dict:
return self.__dict__
for i in range(1, 11):
# 奇数生成相同的种子,生成相同的航班信息
if i % 2:
Faker.seed(2)
else:
Faker.seed()
flight = Flight()
flight_no = flight.flight_no
print(f'flight_no index-{i}: {flight_no}')
输出
flight_no index-1: CC2449
flight_no index-2: A04487
flight_no index-3: CC2449
flight_no index-4: HD0548
flight_no index-5: CC2449
flight_no index-6: H15823
flight_no index-7: CC2449
flight_no index-8: LK1698
flight_no index-9: CC2449
flight_no index-10: EZ0544