问题来源
在爬取一个网站数据的时候,需要把每个页面的数据压缩为Json格式进行保存.我的做法是先将数据保存在dict内,再将dict插入到数组,最后通过json.dumps()将数据压缩为Json格式,具体数据流图如下:
详细代码:
detail = [] for station in stations.find_all("td"): if station.text.strip(): detail.append(station.text.strip()) if not detail: continue for num, key in enumerate(city_station_detail): city_station_detail[key] = detail[num] # 将数据填充到result city_detail["station_detail"].append(city_station_detail) # 将result填充到results中 results.append(city_detail)
发现问题如下:
- result内的数据都是detail的最后一组数据
- results中的数据都是result的最后一组数据
{'city': '韶关', 'site': 'http://www.86pm25.com/city/shaoguan.html', 'time': '2019年03月21日 23时', 'quality': '52', 'station_detail': [{'station': '园林处', 'AQI': '52', 'PM2.5': '31μg/m³', 'PM10': '54μg/m³'}, {'station': '园林处', 'AQI': '52', 'PM2.5': '31μg/m³', 'PM10': '54μg/m³'}, {'station': '园林处', 'AQI': '52', 'PM2.5': '31μg/m³', 'PM10': '54μg/m³'}, {'station': '园林处', 'AQI': '52', 'PM2.5': '31μg/m³', 'PM10': '54μg/m³'}, {'station': '园林处', 'AQI': '52', 'PM2.5': '31μg/m³', 'PM10': '54μg/m³'}]
分析原因
在Python中一切皆为对象,在数据的填充过程中,append方法向list填充的是对象的地址,既数据的指针.由于多次循环填充的都是一个对象,所以list存放的是相同对象的指针,而不是真实数据.
即:list = [dict1, dict2, dict3, .......], 且: id(dict1) = id(dict2) = id(dict3)= .......
正是因为Python的这种特性导致最后的结果都是相同的数据,这个问题就是我们常说的Python的深浅拷贝问题.
直接赋值:其实就是对象的引用(别名)。
浅拷贝:数据半共享(复制其数据独立内存存放,但是只拷贝成功第一层)
深拷贝:数据完全不共享(复制其数据完完全全放独立的一个内存,完全拷贝,数据不共享)
问题解决
之所以遇到这个问题是因为上面的代码都是通过append实现,仅仅复制了数据的存放地址,并没有拷贝数据本身的内存块.解决问题也很简单,把append()内的数据改成dict的浅拷贝就解决了.这里用到了一个方法copy().
detail = [] for station in stations.find_all("td"): if station.text.strip(): detail.append(station.text.strip()) if not detail: continue for num, key in enumerate(city_station_detail): city_station_detail[key] = detail[num] # 将数据填充到result city_detail["station_detail"].append(city_station_detail.copy()) # 将result填充到results中 results.append(city_detail.copy())
问题总结
这个问题在看Python的入门课程中就有介绍了,但是时间久了就不记得了,并且自己的水平是半桶水,遇到这个问题确实有点小懵逼,最后还是在pycharm的调试器单步运行找到的原因.PyCharm真的太强大了.问题虽小,但是背后的意义确实十分重要的,Python的特性真的让人痴迷,也希望自己在以后能越来越精通吧.
共有 0 条评论