如何使用scrapy中的ItemLoader提取数据


Posted in Python onSeptember 30, 2020

1. 简述

我们在用scrapy爬取数据时,首先就要明确我们要爬取什么数据。scrapy提供了Item对象这种简单的容器,我们可以通过Item定义提取数据的格式,需要爬取哪些字段,其提供了类似于字典的API以及用于声明可用字段的简单语法。如下所示:
下面以爬取伯乐在线文章详情页为范例:http://blog.jobbole.com/all-posts/

如何使用scrapy中的ItemLoader提取数据

# 文件items.py
# Item使用简单的class定义语法以及 Field 对象来声明。
import scrapy

class articleDetailItem(scrapy.Item):
 # 标题
 title = scrapy.Field()
 # 文章创建时间
 create_date = scrapy.Field()
 # 文章链接地址
 url = scrapy.Field()
 # url经过md5映射后的值
 url_object_id = scrapy.Field()
 # 文章中图片地址
 front_image_url = scrapy.Field()
 # 文件下载后本地保存的地址
 front_image_path = scrapy.Field()
 # 赞的个数
 praise_nums = scrapy.Field()
 # 评论数
 comment_nums = scrapy.Field()
 # 收藏数
 fav_nums = scrapy.Field()
 # 所有标签
 tags = scrapy.Field()
 # 文章内容
 content = scrapy.Field(serializer = str)

Item字段说明:

  • Field 对象指明了每个字段的元数据(metadata)。例如上面例子中 content 字段中指明了该字段的序列化函数为str。
  • 可以为每个字段指明任何类型的元数据。Field 对象对接受的值没有任何限制。Field 对象中保存的每个键可以由多个组件使用,并且只有这些组件知道这个键的存在。设置 Field 对象的主要目的就是在一个地方定义好所有的元数据。
  • 需要注意的是,用来声明item的 Field 对象并没有被赋值为class的属性。 不过可以通过 Item.fields 属性进行访问。

然后在spider.py中,按照一定的规则来进行数据的提取,如下:

# 文件 boleSpider.py
from ArticleSpider.items import articleDetailItem

#...........此处省略..........
def parseArticelDetail(self, response):
 articleObject = articleDetailItem()
 # 提取出的内容是:6 收藏
 fav_nums = response.xpath("//span[contains(@class, 'bookmark-btn')]/text()").extract()[0]
 # 用正则表达式提取其中的数字6
 match_re = re.match(".*?(\d+).*", fav_nums)
 if match_re:
  fav_nums = match_re.group(1)
 else:
  fav_nums = 0

但是当项目很大,提取的字段数以百计,那么各种提取规则会越来越多,按照这种方式来做,维护的工作将会是一场噩梦!
所以scrapy就提供了ItemLoader这样一个容器,在这个容器里面可以配置item中各个字段的提取规则。可以通过函数分析原始数据,并对Item字段进行赋值,非常的便捷。

可以这么来看 Item 和 Itemloader:Item提供保存抓取到数据的容器,而 Itemloader提供的是填充容器的机制。

Itemloader提供的是一种灵活,高效的机制,可以更方便的被spider或source format (HTML, XML, etc)扩展并重写,更易于维护,尤其是分析规则特别复杂繁多的时候。

2. 环境

  • 系统:win7
  • Scrapy 1.4.0
  • python 3.6.1

3. ItemLoader使用步骤

3.1. 实例化ItemLoader对象

# 文件 boleSpider.py
from scrapy.loader import ItemLoader

要使用Itemloader,必须先将它实例化。可以使用类似字典的对象或者我们之前定义的Item对象来进行实例化。

# 文件 boleSpider.py 
import scrapy
from scrapy.loader import ItemLoader

# 如上面所示,我们首先在items.py中定义了一个articleDetailItem类(继承自scrapy.Item),用于保存我们抓取到的数据

 # 解析函数
 def parse_detail(self, response):
  # 需要实例化ItemLoader, 注意第一个参数必须是实例化的对象...
  atricleItemLoader = ItemLoader(item = articleDetailItem(), response=response)
  # 调用xpath选择器,提起title信息
  atricleItemLoader.add_xpath('title', '//div[@class="entry-header"]/h1/text()')

  # 将提取好的数据load出来
  articleInfo = atricleItemLoader.load_item()
  # 输出:articleInfo = {'title': ['在 Linux 中自动配置 IPv6 地址']}
  print(f"articleInfo = {articleInfo}")

参数说明:重要的参数有两个

  • 第一个参数:item对象, 传递进来的 Item是之前定义的,也可以是一个类似字典的对象。特别需要注意的是,传递的是一个实例,不是类名。……(当然不使用对象也可以,当不用对象进行实例化的时候,Item会自动使用ItemLoader.default_item_class 属性中指定的Item 类在Item Loader constructor中实例化)
  • 第二个参数:response,指定用于提取数据的源数据。

3.2. ItemLoader填充数据的三种方法

实例化ItemLoader对象之后,接下来,就要开始收集数值到ItemLoader了。ItemLoader提供了三个重要的方法将数据填充进来:

# 文件 boleSpider.py 

 # 解析页面函数
 def parse_detail(self, response):
  # 需要实例化ItemLoader, 注意第一个参数必须是实例化的对象...
  atricleItemLoader = ItemLoader(item = articleDetailItem(), response=response)
  # 调用xpath选择器,提取title信息
  atricleItemLoader.add_xpath('title', '//div[@class="entry-header"]/h1/text()')
  # 调用css选择器,提取praise_nums信息
  atricleItemLoader.add_css('praise_nums', '.vote-post-up h10::text')
  # 直接给字段赋值,尤其需要注意,不管赋值的数据是什么,都会自动转换成list类型
  atricleItemLoader.add_value('url', response.url)

  # 将提取好的数据load出来
  articleInfo = atricleItemLoader.load_item()
  # 观察一下,发现三种方式填充的数据,均为List类型
  '''
   输出结果:
    articleInfo = {
     'praise_nums': ['2'],
     'title': ['100 倍价值的工程师'],
     'url': ['http://blog.jobbole.com/113710/']
    }
  '''
  print(f"articleInfo = {articleInfo}")

使用说明:

  • 第一个参数:指定字段名,如title。
  • 第二个参数:指定对应的提取规则,或者传值。
  • 前面调用add_xpath等只是将提取的数据收集起来。最终,当所有数据被收集起来之后,还需要调用 ItemLoader.load_item() 方法, 实际上填充并且返回了之前通过调用 add_xpath(),add_css(),and add_value() 所提取和收集到的数据。
  • 特别注意:默认情况下,这些字段填入的全部是list类型。就算是传值,传递了一个url,但是结果依然是一个list。
  • 从boleSpider.py核心代码来看,我们可以对每个字段进行配置,匹配映射,非常的清晰,大大方便了可配置性和可维护性。

但是实际项目中,一个字段的提取一般不会是直接配置一个规则,还需要更进一步的处理。那如何添加其他处理方法呢?接着往下看…

3.3. ItemLoader填充数据面临的问题。

从上面的示例中,可以看到,存在两个问题:

  • 第一,提取的数据,填充进去的对象都是List类型。而我们大部分的需求是要取第一个数值,取List中的第一个非空元素,那么如何实现取第一个呢?
  • 第二,在做item字段解析时,经常需要再进一步解析,过滤出我们想要的数值,例如用正则表达式将 $10 price中的数字10提取出来。那么又如何对字段加一些处理函数呢?

3.4. 输入处理器input_processor和输出处理器output_processor

首先来改写一下articleDetailItem的定义:

# items.py
import datetime
import scrapy

# 定义一个时间处理转换函数
# 将 '\r\n\r\n   2018/03/06 · ' 转换成 datetime.date(2018, 3, 14)
def date_convert(value):
 try:
  create_date = datetime.datetime.strptime(value, "%Y/%m/%d").date()
 except Exception as e:
  create_date = datetime.datetime.now().date()

 return create_date

# 用于存储解析文章的详细信息
class articleDetailItem(scrapy.Item):
 # 标题
 title = scrapy.Field()
 # 文章创建时间
 create_date = scrapy.Field(
  # 转换前是'create_date':'\r\n\r\n   2018/03/14 · '
  # 转换后是'create_date': datetime.date(2018, 3, 14),
  input_processor = MapCompose(date_convert),
  output_processor = TakeFirst()
 )
 # 文章链接地址
 url = scrapy.Field(
  # 转换前是'url': ['http://blog.jobbole.com/113771/']
  # 转换后是'url': 'http://blog.jobbole.com/113699/'
  output_processor = TakeFirst()
 )

 # url经过md5映射后的值
 url_object_id = scrapy.Field()
 # 文章中图片地址
 front_image_url = scrapy.Field()
 # 文件下载后本地保存的地址
 front_image_path = scrapy.Field()
 # 赞的个数
 praise_nums = scrapy.Field()
 # 评论数
 comment_nums = scrapy.Field()
 # 收藏数
 fav_nums = scrapy.Field()
 # 所有标签
 tags = scrapy.Field()
 # 文章内容
 content = scrapy.Field()

然后在 boleSpider.py 中提取数据:

# 文件boleSpider.py
 # 解析页面函数
 def parse_detail(self, response):
  # 需要实例化ItemLoader, 注意第一个参数必须是实例化的对象...
  atricleItemLoader = ItemLoader(item = articleDetailItem(), response=response)
  # 调用xpath选择器,提取title信息
  atricleItemLoader.add_xpath('title', '//div[@class="entry-header"]/h1/text()')
  # 调用xpath选择器,提取create_date信息
  atricleItemLoader.add_xpath('create_date', "//p[@class='entry-meta-hide-on-mobile']/text()")
  # 调用css选择器,提取praise_nums信息
  atricleItemLoader.add_css('praise_nums', '.vote-post-up h10::text')
  # 直接给字段赋值,尤其需要注意,不管赋值的数据是什么,都会自动转换成list类型
  atricleItemLoader.add_value('url', response.url)

  # 将提取好的数据load出来
  articleInfo = atricleItemLoader.load_item()
  '''
   输出结果:
    articleInfo = {
     'create_date': datetime.date(2018, 3, 14),
     'praise_nums': ['1'],
     'title': ['在 Linux 中自动配置 IPv6 地址'],
     'url': 'http://blog.jobbole.com/113771/'}
  '''
  print(f"articleInfo = {articleInfo}")

Field 字段事实上有两个参数:

  • 第一个是输入处理器(input_processor) ,当这个item,title这个字段的值传过来时,可以在传进来的值上面做一些预处理。
  • 第二个是输出处理器(output_processor) , 当这个item,title这个字段被预处理完之后,输出前最后的一步处理。

总结一下,每个字段的数据的处理过程是:

  • 第一步, 通过 add_xpath(), add_css() 或者 add_value() 方法),提取到数据。
  • 第二步,将提取到的数据,传递到输入处理器(input_processor)中进行处理,处理结果被收集起来,并且保存在ItemLoader内(但尚未分配给该Item)。
  • 第三步,最后调用输出处理器(output_processor)来处理之前收集到的数据(这是最后一步对数据的处理)。然后再存入到Item中,输出处理器的结果是被分配到Item的最终值。
  • 第四步,收集到所有的数据后, 调用ItemLoader.load_item() 方法来填充,并得到填充后的 Item 对象。

需要注意的是:input_processor和output_processor都是可调用对象,调用时传入需要被分析的数据, 处理后返回分析得到的值。因此你可以使用任意函数作为输入、输出处理器。唯一需注意的是它们必须接收一个(并且只是一个)迭代器性质的参数。

3.5. 处理原来的两个问题

再回到原来的问题,如何解决:

3.5.1. 如何取第一个?

# 文件items.py

import scrapy

# TakeFirst()是Scrapy提供的内置处理器,用于提取List中的第一个非空元素
class articleDetailItem(scrapy.Item):
 # 文章链接地址
 url = scrapy.Field(
  # 转换前是'url': ['http://blog.jobbole.com/113771/']
  # 转换后是'url': 'http://blog.jobbole.com/113699/'
  output_processor = TakeFirst()
 )

3.3.2. 如何在字段上加一些处理函数?

# 文件items.py
import datetime
import scrapy

# 定义一个时间处理转换函数
# 将 '\r\n\r\n   2018/03/06 · ' 转换成 datetime.date(2018, 3, 14)
def date_convert(value):
 try:
  create_date = datetime.datetime.strptime(value, "%Y/%m/%d").date()
 except Exception as e:
  create_date = datetime.datetime.now().date()

 return create_date

# 用于存储解析文章的详细信息
class articleDetailItem(scrapy.Item):
 # 文章创建时间
 create_date = scrapy.Field(
  # 转换前是'create_date':'\r\n\r\n   2018/03/14 · '
  # 转换后是'create_date': datetime.date(2018, 3, 14),
  input_processor = MapCompose(date_convert),
  output_processor = TakeFirst()
 )

3.6. scrapy内置的处理器

参考源码: E:\Miniconda\Lib\site-packages\scrapy\loader\processors.py

从上面的例子来看,我们可以自定义一下处理函数,作为输入输出处理器,但是Scrapy还提供了一些常用的处理器。如MapCompose(能把多个函数执行的结果按顺序组合起来,产生最终的输出,通常用于输入处理器),TakeFirst(取第一个非空的元素)。

3.6.1. TakeFirst

返回第一个非空(non-null/ non-empty)值,常用于单值字段的输出处理器,无参数。

# 源码
# class scrapy.loader.processors.TakeFirst
class TakeFirst(object):
 def __call__(self, values):
  for value in values:
   if value is not None and value != '':
    return value
# 单独直接使用
from scrapy.loader.processors import TakeFirst

proc = TakeFirst()

# 接收对象是一个可迭代的对象,如list
result = proc(['', 'one', 'two', 'three'])

# 结果:result = one
print(f"result = {result}")

3.6.2. Identity

最简单的处理器,不进行任何处理,直接返回原来的数据。无参数。

# 源码
# class scrapy.loader.processors.Identity
class Identity(object):
 def __call__(self, values):
  return values
# 单独直接使用
from scrapy.loader.processors import Identity

proc = Identity()

# 接收对象是一个可迭代的对象,如list
result = proc(['', 'one', 'two', 'three'])

# 结果:result = ['', 'one', 'two', 'three']
print(f"result = {result}")

3.6.3. Join

  • 返回用分隔符连接后的值。分隔符默认为空格。不接受Loader contexts。
  • 当使用默认分隔符的时候,这个处理器等同于如下这个:u' '.join1
# 源码
# class scrapy.loader.processors.Join(separator=u' ‘)
class Join(object):
 def __init__(self, separator=u' '):
  self.separator = separator
 def __call__(self, values):
  return self.separator.join(values)
# 单独直接使用
from scrapy.loader.processors import Join

# 如果不指定连接符,默认是使用空格连接
proc = Join(";")

# 接收对象是一个可迭代的对象,如list
result = proc(['', 'one', 'two', 'three'])

# 结果:result = ;one;two;three
print(f"result = {result}")

3.6.4. Compose

用给定的多个函数的组合,来构造的处理器。list对象(注意不是指list中的元素),依次被传递到第一个函数,然后输出,再传递到第二个函数,一个接着一个,直到最后一个函数返回整个处理器的输出。
默认情况下,当遇到None值(list中有None值)的时候停止处理。可以通过传递参数stop_on_none = False改变这种行为。

class Compose(object):
 def __init__(self, *functions, **default_loader_context):
  self.functions = functions
  self.stop_on_none = default_loader_context.get('stop_on_none', True)
  self.default_loader_context = default_loader_context
 def __call__(self, value, loader_context=None):
  if loader_context:
   context = MergeDict(loader_context, self.default_loader_context)
  else:
   context = self.default_loader_context
  wrapped_funcs = [wrap_loader_context(f, context) for f in self.functions]
  for func in wrapped_funcs:
   if value is None and self.stop_on_none:
    break
   value = func(value)
  return value
# 单独直接使用
from scrapy.loader.processors import Compose

# stop_on_none=True, 指定在遇到None时,不用中断,还继续处理
# lambda v: v[0], 指定取第一个元素
# str.upper , 大写
proc = Compose(lambda v: v[0], str.upper, stop_on_none=True)

# 接收对象是一个可迭代的对象,如list
result = proc(['one', 'two', None, 'three'])

# 结果:result = ONE
print(f"result = {result}")

每个函数可以选择接收一个loader_context参数。

3.6.5. MapCompose

与Compose处理器类似,区别在于各个函数结果在内部传递的方式(会涉及到list对象解包的步骤):
输入值是被迭代的处理的,List对象中的每一个元素被单独传入,第一个函数进行处理,然后处理的结果被连接起来形成一个新的迭代器,并被传入第二个函数,以此类推,直到最后一个函数。最后一个函数的输出被连接起来形成处理器的输出。
每个函数能返回一个值或者一个值列表,也能返回None(会被下一个函数所忽略)
这个处理器提供了很方便的方式来组合多个处理单值的函数。因此它常用于输入处理器,因为传递过来的是一个List对象。

# 源码
# class scrapy.loader.processors.MapCompose(*functions, **default_loader_context)
class MapCompose(object):

 def __init__(self, *functions, **default_loader_context):
  self.functions = functions
  self.default_loader_context = default_loader_context

 def __call__(self, value, loader_context=None):
  values = arg_to_iter(value)
  if loader_context:
   context = MergeDict(loader_context, self.default_loader_context)
  else:
   context = self.default_loader_context
  wrapped_funcs = [wrap_loader_context(f, context) for f in self.functions]
  for func in wrapped_funcs:
   next_values = []
   for v in values:
    next_values += arg_to_iter(func(v))
   values = next_values
  return values
# 单独直接使用

from scrapy.loader.processors import MapCompose

def add_firstStr(value):
 return value + "_firstAdd"

def add_secondStr(value):
 return value + "_secondAdd"
# stop_on_none=True, 指定在遇到None时,不用中断,还继续处理
# 依次处理每个list元素
proc = MapCompose(add_firstStr, add_secondStr, str.upper, stop_on_none=True)

# 接收对象是一个可迭代的对象,如list
result = proc(['one', 'two', 'three'])

# 结果:result = ['ONE_FIRSTADD_SECONDADD', 'TWO_FIRSTADD_SECONDADD', 'THREE_FIRSTADD_SECONDADD']
print(f"result = {result}")

与Compose处理器类似,它也能接受Loader context。

3.7. 重用和扩展ItemLoaders

3.7.1. 添加默认的处理机制
从上面的信息来看,ItemLoaders是非常灵活的,但是假设有个需求,所有的字段,我们都要去取第一个,那么如果有300个字段,我们就要添加300次,每个都要写,就会觉得很麻烦。那么有没有办法统一设置呢,答案是有的,如下:

  • 如果想要实现每个字段都只取第一个,那么可以定义一个自己的ItemLoader类:ArticleItemLoader(继承自ItemLoader类)
  • 我们首先可以看一下原始的 ItemLoader 的定义:
# E:\Miniconda\Lib\site-packages\scrapy\loader\__init__.py
class ItemLoader(object):

 default_item_class = Item
 # 可以看到是有默认的输入/输出处理器的,而且默认是什么都不做
 default_input_processor = Identity()
 default_output_processor = Identity()
 default_selector_class = Selector

可以定义一个自己的ItemLoader类:ArticleItemLoader,继承自ItemLoader类, 同时改写(重写)default_output_processor

# 文件items.py
from scrapy.loader import ItemLoader

# 需要继承内置的ItemLoader类
class ArticleItemLoader(ItemLoader):
 # 自定义itemloader,默认的输出处理器为取第一个非空元素
 default_output_processor = TakeFirst()

然后在boleSpider中使用时,我们就不能再简单的使用原有的ItemLoader,而是使用我们自己定义的 ArticleItemLoader 来填充数据:

# 文件boleSpider.py
from ArticleSpider.items import articleDetailItem, ArticleItemLoader

# 使用自定义的ArticleItemLoader实例化一个item_loader 对象
# 然后发现,结果都是从list中取出了一个值:说明我们的设置已经生效了。
item_loader = ArticleItemLoader(item = articleDetailItem(), response=response)
item_loader.add_xpath('title', '//div[@class="entry-header"]/h1/text()')

3.7.2. 重写,覆盖默认的处理机制

  • 上面我们实现了所有字段都只取第一个的功能,但是如果有一些字段,我不需要取第一个,而是有其他的处理方式呢?
  • 那就需要重写这个字段的输出处理器(output_processor)。 下面的例子是,首先在输入处理器中将 “评论” 这样的字符过滤掉,然后将list中所有的元素用”,” 连接起来,成为一个字符串。
def removeCommentTags(value):
 # 去掉Tags中提取的评论字符
 if "评论" in value:
  return ""
 else:
  return value

# Tags是一个list,我们需要用","将他们连接起来, 变成了字符串。
# 但是“评论”我们不需要。去掉。 如何去掉“评论”,在input_processor中,判断value是否==“评论”,如果是,就去掉
class articleDetailItem(scrapy.Item):
 tags = scrapy.Field(
  # 去掉评论
  input_processor = MapCompose(removeCommentTags),
  # 将list中的元素,通过“,”连接起来
  output_processor = Join(",")
 )

而如果,有些字段我们不想做任何处理,也不想去取第一个元素,那么我们怎么做呢?

因为,目前所有的字段都默认设置为去取第一个非空元素,所以,我们需要将这个处理去掉。这个地方尤其要引起重视,因为很容易遗忘自己有这个默认设置。处理方式如下:

def returnValue(value):
 return value

class articleDetailItem(scrapy.Item):
 content = scrapy.Field(
  # 会覆盖掉默认的default_out
  output_processor = MapCompose(returnValue)
  # 或者使用Identity
  # output_processor = Identity()
 )

到此这篇关于如何使用scrapy中的ItemLoader提取数据的文章就介绍到这了,更多相关scrapy ItemLoader提取内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木! 

Python 相关文章推荐
Python 的 Socket 编程
Mar 24 Python
菜鸟使用python实现正则检测密码合法性
Jan 05 Python
Python使用openpyxl读写excel文件的方法
Jun 30 Python
分析Python读取文件时的路径问题
Feb 11 Python
解决pycharm安装后代码区不能编辑的问题
Oct 28 Python
两个元祖T1=('a', 'b'),T2=('c', 'd')使用匿名函数将其转变成[{'a': 'c'},{'b': 'd'}]的几种方法
Mar 05 Python
python远程连接MySQL数据库
Apr 19 Python
浅谈TensorFlow之稀疏张量表示
Jun 30 Python
python调用win32接口进行截图的示例
Nov 11 Python
python pyg2plot的原理知识点总结
Feb 28 Python
Python中快速掌握Data Frame的常用操作
Mar 31 Python
Python中递归以及递归遍历目录详解
Oct 24 Python
python Matplotlib数据可视化(2):详解三大容器对象与常用设置
Sep 30 #Python
python pip如何手动安装二进制包
Sep 30 #Python
python Matplotlib数据可视化(1):简单入门
Sep 30 #Python
详解Python中的编码问题(encoding与decode、str与bytes)
Sep 30 #Python
python 生成器需注意的小问题
Sep 29 #Python
python 两种方法删除空文件夹
Sep 29 #Python
如何使用python写截屏小工具
Sep 29 #Python
You might like
php截取utf-8中文字符串乱码的解决方法
2010/03/29 PHP
php的大小写敏感问题整理
2011/12/29 PHP
深入理解curl类,可用于模拟get,post和curl下载
2013/06/08 PHP
PHP中提问频率最高的11个面试题和答案
2014/09/02 PHP
php+mysqli使用面向对象方式更新数据库实例
2015/01/29 PHP
php定时执行任务设置详解
2015/02/06 PHP
PHP PDOStatement::errorInfo讲解
2019/01/31 PHP
PHP之认识(二)关于Traits的用法详解
2019/04/11 PHP
关于实现代码语法标亮 dp.SyntaxHighlighter
2007/02/02 Javascript
Javascript创建Silverlight Plugin以及自定义nonSilverlight和lowSilverlight样式
2010/06/28 Javascript
instanceof和typeof运算符的区别详解
2014/01/06 Javascript
jquery操作checked属性以及disabled属性的多种方法
2014/06/20 Javascript
JS读取XML文件数据并以table形式显示数据的方法(兼容IE与火狐)
2016/06/02 Javascript
在web中js实现类似excel的表格控件
2016/09/01 Javascript
浅谈javascript控制HTML5的全屏操控,浏览器兼容的问题
2016/10/10 Javascript
bootstrap flask登录页面编写实例
2016/11/01 Javascript
vue项目中vue-i18n和element-ui国际化开发实现过程
2018/04/25 Javascript
Vue.js 通过jQuery ajax获取数据实现更新后重新渲染页面的方法
2018/08/09 jQuery
JavaScript创建对象的四种常用模式实例分析
2019/01/11 Javascript
javascript自定义加载loading效果
2020/09/15 Javascript
Python的加密模块md5、sha、crypt使用实例
2014/09/28 Python
用python实现简单EXCEL数据统计的实例
2017/01/24 Python
python+openCV调用摄像头拍摄和处理图片的实现
2019/08/06 Python
50行Python代码实现视频中物体颜色识别和跟踪(必须以红色为例)
2019/11/20 Python
详解CSS3选择器:nth-child和:nth-of-type之间的差异
2017/09/18 HTML / CSS
鱼油专家:Omegavia
2016/10/10 全球购物
公司离职证明范本(5篇)
2014/09/17 职场文书
干部作风整顿自我剖析材料和整改措施
2014/09/18 职场文书
2015年电信员工工作总结
2015/05/26 职场文书
销售人员管理制度
2015/08/06 职场文书
2016年教师节慰问信
2015/12/01 职场文书
nginx基于域名,端口,不同IP的虚拟主机设置的实现
2021/03/31 Servers
oracle通过存储过程上传list保存功能
2021/05/12 Oracle
MySQL系列之二 多实例配置
2021/07/02 MySQL
python 中的jieba分词库
2021/11/23 Python
Java结构型设计模式之组合模式详解
2022/09/23 Java/Android