pymongo实战
Posted 临风而眠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pymongo实战相关的知识,希望对你有一定的参考价值。
pymongo实战(2)
是对pymongo实战那篇博客的补充和优化
文章目录
1.上一篇博文的问题
上一篇总结出来的最终代码是:
import pymongo
import requests
from lxml import etree
import pandas as pd
myClient = pymongo.MongoClient("localhost")
myDb = myClient["vegetable"]
myCol =myDb["price"]
headers={"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/91.0.4472.164 Safari/537.36"}
#每轮获取一页数据
for i in range(1,4):
resp = requests.get(f'http://www.vegnet.com.cn/Price/List_p{i}.html',headers=headers)
resp.encoding=resp.apparent_encoding
# 生成etree对象
pageText = etree.HTML(resp.text)
# xpath列表
rows = pageText.xpath('/html//div[@class="pri_k"]/p')
#每轮获取一行数据
for row in rows:
# 日期
date = row.xpath('./span[1]/text()')[0].replace('[', '').replace(']', '')
# 品种
variety = row.xpath('./span[2]/text()')[0]
# 批发市场
market = row.xpath('./span[3]/a/text()')[0]
# 最低价格
min_price = row.xpath('./span[4]/text()')[0]
# 最高价格
max_price = row.xpath('./span[5]/text()')[0]
# 平均价格
mean_price = row.xpath('./span[6]/text()')[0]
# 计量单位
measure_unit = row.xpath('./span[7]/text()')[0]
kv_dict={"date":date,"variety":variety,"market":market,
"min_price":min_price,"max_price":max_price,
"mean_price":mean_price,"measure_unit":measure_unit}
myCol.insert_one(kv_dict)
print(f"第{i}页数据抓取完毕")
resp.close()
其中myCol.insert_one(kv_dict)
,
如果再运行一次,那么就多了一倍的重复数据,只是系统自动分配的_id不同罢了
2.解决方案
①想法一
利用_id
唯一 这一个性质,自己指定_id,写入kv_dict
kv_dict={"_id":f"{i}{num}","date":date,"variety":variety,"market":market,
"min_price":min_price,"max_price":max_price,
"mean_price":mean_price,"measure_unit":measure_unit}
num+=1
#在第一个for循环前面加上num=0
myCol.insert_one(kv_dict)
但也正是因为_id是唯一的,这样子运行第一遍没问题,第二遍就会报错了:
pymongo.errors.DuplicateKeyError: E11000 duplicate key error collection: vegetable.price index: _id_ dup key: { _id: "10" }, full error: {'index': 0, 'code': 11000, 'keyPattern': {'_id': 1}, 'keyValue': {'_id': '10'}, 'errmsg': 'E11000 duplicate key error collection: vegetable.price index: _id_ dup key: { _id: "10" }'}
Process finished with exit code 1
②想法二
就用update_one
num=0
for row in rows:
# 日期
date = row.xpath('./span[1]/text()')[0].replace('[', '').replace(']', '')
# 品种
variety = row.xpath('./span[2]/text()')[0]
# 批发市场
market = row.xpath('./span[3]/a/text()')[0]
# 最低价格
min_price = row.xpath('./span[4]/text()')[0]
# 最高价格
max_price = row.xpath('./span[5]/text()')[0]
# 平均价格
mean_price = row.xpath('./span[6]/text()')[0]
# 计量单位
measure_unit = row.xpath('./span[7]/text()')[0]
kv_dict={"date":date,"variety":variety,"market":market,
"min_price":min_price,"max_price":max_price,
"mean_price":mean_price,"measure_unit":measure_unit}
# myCol.insert_one(kv_dict)
myCol.update_one({"_id":f"{i}{num}"},{"$set":kv_dict},upsert=True)
num+=1
这样子,也是因为_id的唯一性,多次运行代码,数据库还是不会变
注:
upsert:
是一种特殊的更新,如果没有找到符合条件的更新条件的文档,就会以这个条件和更新文档为基础创建一个新的文档;如果找到了匹配的文档,就正常更新,upsert非常方便,不必预置集合,同一套代码既能用于创建文档又可以更新文档
③想法三
其实也不必就像想法二里面用_id
自己设置一个也可以,因为{i}{num}
已经保证了每一行数据都有自己的唯一性
所以只要让update_one()的第一个参数是能够体现每一行的数据的唯一性的字段就可以了!
验证一下想法,再次把vegetable数据库先删掉,再试验
myCol.update_one改动如下:
myCol.update_one({"index":f"{i}{num}"},{"$set":kv_dict},upsert=True)
肯定满足上面说的唯一性
完整代码
import pymongo
import requests
from lxml import etree
import pandas as pd
myClient = pymongo.MongoClient("localhost")
myDb = myClient["vegetable"]
myCol =myDb["price"]
headers={"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36"}
#每轮获取一页数据
for i in range(1,4):
resp = requests.get(f'http://www.vegnet.com.cn/Price/List_p{i}.html',headers=headers)
resp.encoding=resp.apparent_encoding
# 生成etree对象
pageText = etree.HTML(resp.text)
# xpath列表
rows = pageText.xpath('/html//div[@class="pri_k"]/p')
#每轮获取一行数据
num=0
for row in rows:
# 日期
date = row.xpath('./span[1]/text()')[0].replace('[', '').replace(']', '')
# 品种
variety = row.xpath('./span[2]/text()')[0]
# 批发市场
market = row.xpath('./span[3]/a/text()')[0]
# 最低价格
min_price = row.xpath('./span[4]/text()')[0]
# 最高价格
max_price = row.xpath('./span[5]/text()')[0]
# 平均价格
mean_price = row.xpath('./span[6]/text()')[0]
# 计量单位
measure_unit = row.xpath('./span[7]/text()')[0]
kv_dict={"date":date,"variety":variety,"market":market,
"min_price":min_price,"max_price":max_price,
"mean_price":mean_price,"measure_unit":measure_unit}
# myCol.insert_one(kv_dict)
myCol.update_one({"index":f"{i}{num}"},{"$set":kv_dict},upsert=True)
num+=1
print(f"第{i}页数据抓取完毕")
resp.close()
多次运行,数据条数不变,成功了
3.第一篇博文里为何一开始否定了update_one?
因为当时设定的参数就是需要重复的品种variety…
每次碰到相同的variety,就会在上面一条数据的基础上更新…
当时其实就是没想清楚…没深入思考
而且对update_one的语法还没搞清楚
那个虎扑论坛的教程把论坛的url作为第一个参数是很合理的,因为第一个参数是不会变的,也不重复,而需要更新的是发帖时间等等
以上是关于pymongo实战的主要内容,如果未能解决你的问题,请参考以下文章
[Python3网络爬虫开发实战] 1.5.2-PyMongo的安装
solr分布式索引实战分片配置读取:工具类configUtil.java,读取配置代码片段,配置实例
Express实战 - 应用案例- realworld-API - 路由设计 - mongoose - 数据验证 - 密码加密 - 登录接口 - 身份认证 - token - 增删改查API(代码片段