如何用 Python 批量生成符合 USPS 标准的测试地址数据
用 Python 批量生成符合 USPS 标准的测试地址
在开发电商系统、CRM 或物流平台时,测试数据的质量直接影响测试效果。随机生成的地址如果不符合 USPS 格式规范,会导致表单校验测试流于形式。本文提供一套完整的 Python 工具,可生成高质量的美国测试地址。
核心设计思路
一个好的地址生成器应该:
基础实现
地址数据池
```python
import random
import json
from dataclasses import dataclass
from typing import Optional
真实城市与邮编映射(节选)
CITY_ZIP_DATA = {
'CA': [
{'city': 'Los Angeles', 'zip': '90001', 'streets': ['Main St', 'Broadway', 'Hollywood Blvd']},
{'city': 'San Francisco', 'zip': '94102', 'streets': ['Market St', 'Mission St', 'Fillmore St']},
{'city': 'San Diego', 'zip': '92101', 'streets': ['El Cajon Blvd', 'University Ave']},
],
'NY': [
{'city': 'New York', 'zip': '10001', 'streets': ['5th Ave', 'Broadway', 'Wall St']},
{'city': 'Buffalo', 'zip': '14201', 'streets': ['Main St', 'Elmwood Ave']},
],
'TX': [
{'city': 'Houston', 'zip': '77001', 'streets': ['Westheimer Rd', 'Richmond Ave']},
{'city': 'Dallas', 'zip': '75201', 'streets': ['Elm St', 'Commerce St']},
],
'FL': [
{'city': 'Miami', 'zip': '33101', 'streets': ['Ocean Dr', 'Collins Ave']},
{'city': 'Orlando', 'zip': '32801', 'streets': ['International Dr', 'Orange Ave']},
]
}
FIRST_NAMES = ['James', 'Mary', 'John', 'Patricia', 'Robert', 'Jennifer', 'Michael', 'Linda']
LAST_NAMES = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller', 'Davis']
STREET_TYPES = ['St', 'Ave', 'Blvd', 'Rd', 'Dr', 'Ln', 'Ct', 'Way', 'Pl', 'Cir']
APT_TYPES = ['Apt', 'Suite', 'Unit', 'Ste']
```
生成器类
```python
@dataclass
class USAddress:
first_name: str
last_name: str
address_line1: str
address_line2: Optional[str]
city: str
state: str
zip5: str
zip4: Optional[str]
def to_dict(self):
return {
'name': f"{self.first_name} {self.last_name}",
'address_line1': self.address_line1,
'address_line2': self.address_line2,
'city': self.city,
'state': self.state,
'zip': f"{self.zip5}-{self.zip4}" if self.zip4 else self.zip5
}
def to_usps_format(self):
"""输出 USPS 标准格式"""
lines = [
f"{self.first_name} {self.last_name}",
self.address_line1
]
if self.address_line2:
lines.append(self.address_line2)
lines.append(f"{self.city}, {self.state} {self.zip5}")
return "\n".join(lines)
class AddressGenerator:
def __init__(self, seed: Optional[int] = None):
if seed is not None:
random.seed(seed)
def generate_name(self) -> tuple:
return (random.choice(FIRST_NAMES), random.choice(LAST_NAMES))
def generate_address(self, state: Optional[str] = None) -> USAddress:
# 随机选择州
if state is None:
state = random.choice(list(CITY_ZIP_DATA.keys()))
# 选择该州的城市
city_data = random.choice(CITY_ZIP_DATA[state])
# 生成门牌号(1-9999)
house_number = random.randint(1, 9999)
# 选择街道
street = random.choice(city_data['streets'])
# 生成 ZIP+4(50%概率)
zip4 = f"{random.randint(1000, 9999)}" if random.random() > 0.5 else None
# 生成公寓号(30%概率)
address_line2 = None
if random.random() < 0.3:
apt_type = random.choice(APT_TYPES)
apt_num = random.choice([f"{random.randint(1, 999)}", f"{random.choice('ABCDEFGH')}{random.randint(1, 9)}"])
address_line2 = f"{apt_type} {apt_num}"
first, last = self.generate_name()
return USAddress(
first_name=first,
last_name=last,
address_line1=f"{house_number} {street}",
address_line2=address_line2,
city=city_data['city'],
state=state,
zip5=city_data['zip'],
zip4=zip4
)
def generate_batch(self, count: int, state: Optional[str] = None) -> list:
return [self.generate_address(state) for _ in range(count)]
```
扩展功能
生成军事地址
```python
def generate_military_address(self) -> dict:
military_states = {
'AE': ['09001', '09112', '09227'],
'AP': ['96201', '96319', '96518'],
'AA': ['34001', '34022']
}
state = random.choice(list(military_states.keys()))
zip_code = random.choice(military_states[state])
box_number = random.randint(1, 9999)
return {
'type': 'military',
'name': f"{self.generate_name()[0]} {self.generate_name()[1]}",
'address_line1': f"Unit {random.randint(1000, 9999)} Box {box_number}",
'city': 'APO' if state == 'AE' else ('FPO' if state == 'AP' else 'DPO'),
'state': state,
'zip': zip_code
}
```
生成 PO Box 地址
```python
def generate_po_box_address(self, state: Optional[str] = None) -> dict:
if state is None:
state = random.choice(list(CITY_ZIP_DATA.keys()))
city_data = random.choice(CITY_ZIP_DATA[state])
box_number = random.randint(1, 99999)
return {
'type': 'po_box',
'name': f"{self.generate_name()[0]} {self.generate_name()[1]}",
'address_line1': f"PO Box {box_number}",
'city': city_data['city'],
'state': state,
'zip': city_data['zip']
}
```
输出格式
导出为 CSV
```python
import csv
def export_to_csv(addresses, filename='addresses.csv'):
with open(filename, 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['name', 'address_line1', 'address_line2', 'city', 'state', 'zip'])
writer.writeheader()
for addr in addresses:
writer.writerow(addr.to_dict())
使用示例
gen = AddressGenerator(seed=42)
addresses = gen.generate_batch(100)
export_to_csv(addresses, 'test_addresses.csv')
```
导出为 SQL INSERT
```python
def export_to_sql(addresses, table_name='addresses'):
sql_statements = []
for addr in addresses:
d = addr.to_dict()
sql = f"""INSERT INTO {table_name} (name, address_line1, address_line2, city, state, zip) VALUES ('{d['name']}', '{d['address_line1']}', '{d.get('address_line2') or ''}', '{d['city']}', '{d['state']}', '{d['zip']}');"""
sql_statements.append(sql)
return "\n".join(sql_statements)
```
导出为 JSON
```python
def export_to_json(addresses, filename='addresses.json'):
data = [addr.to_dict() for addr in addresses]
with open(filename, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
```
完整使用示例
```python
初始化生成器
gen = AddressGenerator(seed=2024)
生成100个加州地址
ca_addresses = gen.generate_batch(100, state='CA')
生成混合类型地址(80%街道 + 10%PO Box + 10%军事)
mixed_addresses = []
for i in range(1000):
r = random.random()
if r < 0.8:
mixed_addresses.append(gen.generate_address())
elif r < 0.9:
mixed_addresses.append(gen.generate_po_box_address())
else:
mixed_addresses.append(gen.generate_military_address())
导出为多种格式
export_to_csv(ca_addresses, 'ca_test_data.csv')
export_to_json(mixed_addresses, 'mixed_test_data.json')
print(f"生成了 {len(mixed_addresses)} 条测试地址")
print("\n示例地址:")
print(mixed_addresses[0].to_usps_format())
```
性能优化
对于需要生成百万级地址数据的场景:
```python
from multiprocessing import Pool
def generate_chunk(args):
seed, count = args
gen = AddressGenerator(seed=seed)
return gen.generate_batch(count)
使用多进程生成100万条地址
if __name__ == '__main__':
total = 1_000_000
chunks = 10
chunk_size = total // chunks
with Pool(chunks) as pool:
results = pool.map(generate_chunk, [(i, chunk_size) for i in range(chunks)])
all_addresses = [addr for chunk in results for addr in chunk]
print(f"生成了 {len(all_addresses)} 条地址")
```
总结
通过本文提供的 Python 工具,你可以:
这套工具可以直接集成到你的测试框架中,为自动化测试提供高质量的数据基础。