Python爬虫的任务数据操作的小技巧,


 需求

爬取某网站的项目列表页,获取其url,标题等信息,作为后续爬取详情页的任务url。

先上代码

代码

  1. # -*- coding: utf-8 -*- 
  2. # @Time : 2019-11-08 14:04 
  3. # @Author : cxa 
  4. # @File : motor_helper.py 
  5. # @Software: PyCharm 
  6.  
  7. import asyncio  
  8. import datetime 
  9. from loguru import logger 
  10. from motor.motor_asyncio import AsyncIOMotorClient 
  11. from collections import Iterable 
  12.  
  13. try: 
  14.     import uvloop 
  15.  
  16.     asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) 
  17. except ImportError: 
  18.     pass 
  19.  
  20. db_configs = { 
  21.     'host': '127.0.0.1', 
  22.     'port': '27017', 
  23.     'db_name': 'mafengwo', 
  24.     'user': '' 
  25.  
  26. class MotorOperation: 
  27.     def __init__(self): 
  28.         self.__dict__.update(**db_configs) 
  29.         if self.user: 
  30.             self.motor_uri = f"mongodb://{self.user}:{self.passwd}@{self.host}:{self.port}/{self.db_name}?authSource={self.db_name}" 
  31.         else: 
  32.             self.motor_uri = f"mongodb://{self.host}:{self.port}/{self.db_name}" 
  33.         self.client = AsyncIOMotorClient(self.motor_uri) 
  34.         self.mb = self.client[self.db_name] 
  35.     async def save_data_with_status(self, items, col="seed_data"): 
  36.         for item in items: 
  37.             data = dict() 
  38.             data["update_time"] = datetime.datetime.now() 
  39.             data["status"] = 0  # 0初始 
  40.             data.update(item) 
  41.             print("data", data) 
  42.             await self.mb[col].update_one({ 
  43.                 "url": item.get("url")}, 
  44.                 {'$set': data, '$setOnInsert': {'create_time': datetime.datetime.now()}}, 
  45.                 upsert=True) 
  46.  
  47.      async def add_index(self, col="seed_data"): 
  48.         # 添加索引 
  49.         await self.mb[col].create_index('url') 

因为我的爬虫是异步网络模块aiohttp写的,所以选择了pymongo的异步版本motor进行操作。

异步代码的基本属性就是async/await成对的出现,如果把上面的await和async去掉,就是类似pymongo的写法了,这里异步不是重点,重点是我们怎么处理每条数据。

这里除了网页的url,标题等信息,我需要附加3个字段。分别是create_time, status,update_time。

这三个字段分别代表,数据插入数据,状态和更新时间。

那么我为什么添加三个字段呢?

首先,我们需要判断每次的任务数据是否存在,我这里的情况是存在就更新不存在就插入,那么我就需要一个查询条件,作为更新的条件,很显然这里可以使用任务的url作为唯一条件(你还可以使用url+标题做个md5然后保存),好了查询条件确定。

下面说create_time这个比较好理解就是数据插入时间,关键是为什么还要一个update_time,这个的话和status字段有一定的关系。画重点:这个status作为后续爬虫进行爬取的一个标志用。目前这个status有4个值,0-4,我这是这样定义的,

0:初始状态

1:抓取中的任务

2:抓取成功

3:抓取失败

4:抓取成功但是没有匹配到任务。

后面随着任务的爬取,状态也是不断变化的,同时我们需要更新update_time为最新的时间。这个目前的话是体现不出来什么作用,它的使用场景是,重复任务的抓取,比如今天我抓取了任务列表里的url1、url2,第二天的时候我如果再抓到,为了区分是抓取失败还是抓取成功,我们根据create_time和update_time就可以进行推断了,如果两者相同而且是当前的日期说明刚抓的,如果update_time的日期比create_time新可以说明,抓到了重复的任务。关于字段的设计就啰嗦这么些。

下面是实现,我们可以通过update_one方法,对数据作存在或者插入操作,因为url作为查询条件,后面量大的话就最好添加一个索引。也就是上面的 add_index方法。

好了最好说插入更新的具体代码

需要注意的是

  1. {'$set': data, '$setOnInsert': {'create_time': datetime.datetime.now()}} 

$setOnInsert里面使用的字段是数据不存在的时候才插入的,存在就不动了,只插入$set里面指定的。

另外$setOnInsert里面使用的字段不能在$set里面再次出现

upsert=True代表的是不存在就插入。

评论关闭