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实战的主要内容,如果未能解决你的问题,请参考以下文章

pymongo实战

[Python3网络爬虫开发实战] 1.5.2-PyMongo的安装

solr分布式索引实战分片配置读取:工具类configUtil.java,读取配置代码片段,配置实例

pymongo 存取

Express实战 - 应用案例- realworld-API - 路由设计 - mongoose - 数据验证 - 密码加密 - 登录接口 - 身份认证 - token - 增删改查API(代码片段

golang代码片段(摘抄)