Python基础学习(二),读取文件数据,清洗数据,然后重新保存
第二阶段:进阶语法(数据采集与清洗场景)
目标:掌握列表推导、异常处理、文件读写、常用内置库。
场景:市场部需要收集竞品价格,但官网数据夹杂着空值、单位(“元/件”)和emoji符号。
具体例子:使用requests抓取HTML(模拟),用正则表达式提取数字,用try/except处理缺失值,最后清洗成纯浮点数保存。
练习:编写清洗管道,自动将“¥1,299.00”转换为1299.00。
业务涉及内容:
1)读取csv文件;
2)对读取的数据列表,进行特殊字符清洗;具体的特殊字符要根据文件,尽可能全面,防止缺少造成清洗不完整;
3)清洗后的数据,进行写操作,存为新文件;
4)统计清洗数据的情况;哪些有问题,具体数量等;
测试使用附件:https://files.cnblogs.com/files/violetlength/raw_prices.zip?t=1787886036&download=true
具体代码实现:myclean.py
1 import os 2 import csv 3 import re 4 import emoji 5 6 def clean_pricestr(mystr): 7 """清理价格里面的特殊数据,转为数字 8 主要处理: 9 空值或者None 10 货币符号:¥、¥、$ 11 千位分割符:, 12 单位后缀:元/件 元 13 emoji符号 14 括号等特殊字符 15 入参:价格字符串, 16 17 返回:float或者None(清理失败时,直接返回None) 18 """ 19 if mystr is None: 20 return None 21 22 if not isinstance(mystr,str): 23 return None 24 25 cleaned = mystr.strip() 26 27 if not cleaned or cleaned == "()": 28 return None 29 30 cleaned = cleaned.replace('¥','') 31 cleaned = cleaned.replace('¥','') 32 cleaned = cleaned.replace('$','') 33 cleaned = cleaned.replace('€','') 34 cleaned = cleaned.replace('£','') 35 36 cleaned = cleaned.replace('元/件','') 37 cleaned = cleaned.replace('元件','') 38 cleaned = cleaned.replace('元','') 39 cleaned = cleaned.replace('件','') 40 41 #处理emoji符号,需要py 3.10+版本,可以引用该第三方插件 import emoji 42 cleaned = emoji.replace_emoji(cleaned, replace='') 43 44 cleaned = cleaned.replace(',','') 45 46 #相关的特殊字符替换后,再做一次首尾空格格式化 47 cleaned = cleaned.strip() 48 49 #再次判断格式化之后,是否有有效值,如果没有,则返回None 50 if not cleaned: 51 return None 52 53 try: 54 price = float(cleaned) 55 if price < 0: #处理负数情况 56 return None 57 return price 58 except ValueError: 59 return None 60 61 def read_csv(file): 62 """读原始文件数据 63 入参:csv文件 64 返回:行对象列表 65 """ 66 records = [] 67 try: 68 with open(file,"r",encoding="gbk") as ff: 69 reader = csv.DictReader(ff) 70 for row in reader: 71 records.append(row) 72 except FileNotFoundError: 73 print(f"错误:未找到文件{file}") 74 return [] 75 except UnicodeDecodeError: #此时说明文档是UTF-8格式,再用UTF-8处理读取一下 76 try: 77 with open(file,"r",encoding="utf-8") as ff: 78 reader = csv.DictReader(ff) 79 for row in reader: 80 records.append(row) 81 except Exception as e: 82 print(f"读文件失败:{e}") 83 return [] 84 return records 85 86 def clean_price_list(records): 87 """清洗价格列表数据 88 入参:数据列表 89 返回:清洗后的列表 90 """ 91 cleaned_records = [] 92 for record in records: 93 product_name = record.get("产品名称","未知名称") 94 original_price = record.get("价格","") 95 source = record.get("来源","未知来源") 96 cleaned_price = clean_pricestr(original_price) 97 98 cleaned_records.append({ 99 "产品名称":product_name, 100 "原始价格":original_price, 101 "清洗后价格":cleaned_price, 102 "来源":source, 103 "是否有效":cleaned_price is not None 104 }) 105 106 return cleaned_records 107 108 def save_cleaned_data(records, output_file): 109 """把清洗后的数据,存到另外一个文档中 110 入参:清理的数据,要存储的文档 111 """ 112 try: 113 with open(output_file,"w",encoding="utf-8", newline="") as ff: 114 fieldnames=["产品名称", "原始价格", "清洗后价格", "来源", "是否有效"] 115 writer=csv.DictWriter(ff,fieldnames= fieldnames) 116 writer.writeheader() 117 writer.writerows(records) 118 print(f"数据保存地址:{output_file}") 119 except Exception as e: 120 print(f"保存出差:{e}") 121 122 def show_statistics(cleaned_records): 123 """显示统计数据 124 入参:清理的数据记录 125 """ 126 total = len(cleaned_records) 127 valid = sum(1 for r in cleaned_records if r["是否有效"]) 128 invalid = total - valid 129 130 valid_prices = [r["清洗后价格"] for r in cleaned_records if r["是否有效"]] 131 132 print(f"总数:{total}") 133 print(f"合规数:{valid}") 134 print(f"不合规数:{invalid}") 135 136 if valid_prices: 137 print(f"价格范围:{min(valid_prices):.2f} -- {max(valid_prices):.2f}") 138 print(f"平均价格:{sum(valid_prices)/len(valid_prices):.2f}") 139 140 def main(): 141 pathdir = os.path.dirname(os.path.abspath(__file__)) 142 inputfile = os.path.join(pathdir,"raw_prices.csv") 143 outputfile = os.path.join(pathdir,"cleaned_prices.csv") 144 145 print("1、读原始文件数据") 146 rawrecords = read_csv(inputfile) 147 if not rawrecords: 148 print("没有读取到数据") 149 reuturn 150 151 print(f"读取到{len(rawrecords)}条数据") 152 153 print("2、清洗数据") 154 cleanedrecords = clean_price_list(rawrecords) 155 156 invalidcount = sum(1 for r in cleanedrecords if not r["是否有效"]) 157 print(f"清洗完成。{invalidcount}条数据失败") 158 159 print("3、显示清洗结果") 160 for i, record in enumerate(cleanedrecords[:5]): 161 status = "✓" if record["是否有效"] else "✗" 162 price_display = f"{record['清洗后价格']:.2f}" if record["是否有效"] else "无效" 163 original_display = record["原始价格"].encode('ascii','replace').decode('ascii') 164 print(f"{status}{record['产品名称']}:{original_display} -> {price_display}") 165 166 if len(cleanedrecords) > 5: 167 print(f"...还有{len(cleanedrecords) - 5}条") 168 169 print("4、保存清洗结果") 170 save_cleaned_data(cleanedrecords, outputfile) 171 172 print("5、显示统计信息") 173 show_statistics(cleanedrecords) 174 175 print("\n6. 清洗规则说明:") 176 print(" - 移除货币符号:YEN, DOLLAR, EURO, POUND") 177 print(" - 移除千位分隔符:,") 178 print(" - 移除单位后缀:YUAN/PIECE, YUAN, PIECE") 179 print(" - 移除emoji符号") 180 print(" - 处理空值和异常值") 181 182 if __name__ == "__main__": 183 main()
浙公网安备 33010602011771号