Linux 安装 VMware workstation 12

VMware Workstation 12 虚拟机,适用于 RHEL/CentOS 7, Fedora 20-24, Debian 7-9, Ubuntu 16.04-14.14 and Linux Mint 17-18.

Prerequisites

  • 确保系统 64 位
  • VMware 12 不支持 32 位 CPU
  • 确保有 root 权限

安装

  • 更新

    apt-get update && apt-get upgrade # On Debian Systems

  • 下载

    wget ‘http://www.vmware.com/go/tryworkstation-linux-64’

  • 执行权限

    chmod +x VMware-Workstation-Full-12.5.5-5234757.x86_64.bundle

  • 执行安装

    ./VMware-Workstation-Full-12.5.5-5234757.x86_64.bundle

启动之后,如果没有自动找到 gcc ,需要手动指定 gcc 版本, gcc-4.8 版本,在 /usr/bin/ 目录下。

安装系统

这一步只要有系统镜像,一步步安装是很快的。省略。

宿主共享文件

在 VM 菜单下, Setting 中 Option 可以添加宿主机的共享文件夹。

reference


2017-04-29 Linux , Mint , VMware

每天学习一个命令:tcpdump 命令行下抓包

Tcpdump 是一个运行在命令行下的抓包工具。它允许用户拦截和显示发送或收到过程中网络连接到该计算机的TCP/IP和其他数据包。Tcpdump 适用于大多数的类Unix系统操作系统(如linux,BSD等)。类Unix系统的 tcpdump 需要使用libpcap这个捕捉数据的库就像 Windows 下的WinPcap。

常见用法

过滤主机

tcpdump -i eth1 host 192.168.1.1

过滤端口

tcpdump -i eth1 port 25

网络过滤

tcpdump -i eth1 net 192.168

同类工具

Tshark是wireshark的命令行版本,类似于tcpdump,可以用于网络抓包,封包解析等。

抓取指定设备的网络包

tshark -i eth0

抓取目的端口80的包

tshark tcp dst port 80

2017-04-23 tcpdump , linux , network

Scrapy 学习笔记及简单使用

Scrapy 是纯 Python 实现的爬虫框架(scraping and crawling framework),可以非常轻松地提取网页结构信息。最初设计时 Scrapy 仅仅作为网页抓取工具,但因其功能强大,配置简单,逐渐的被扩大使用范围,也经常被用于以下方面:

  • 数据挖掘 Data Mining
  • 信息处理 information processing
  • 历史信息存储 historical archival
  • 检测及自动化测试 monitoring and automated testing

因为网上的教程已经非常详细了,这里就重点记录解决的几个问题。

  • Scrapy的官网地址:http://scrapy.org
  • Scrapy在Github上的项目地址:https://github.com/scrapy/scrapy.git
  • Scrapy的官方文档地址:http://doc.scrapy.org/

搭建环境

安装 python 2.7

一般 Ubuntu/Linux Mint 都会预装,查看一下即可

python -V
Python 2.7.12

如果没有安装 Python,可以使用之前推荐的 pyenv 来安装。下面的步骤也同样可以放到 pyenv 中执行。

安装 virtualenv

在开发目录中虚拟化python环境,避免和系统依赖冲突

sudo pip install virtualenv
source ./bin/active # 开启
# 此后再使用 pip install 时会安装在独立的目录下

具体用法可参考官网

安装依赖

sudo apt-get install libxml2-dev libxslt1-dev python-dev
pip install scrapy

项目结构

安装完成之后使用如下命令生成初始项目

scrapy startproject demo

初始目录结构如下:

$ tree demo
demo
├── demo
│   ├── __init__.py
│   ├── items.py
│   ├── middlewares.py
│   ├── pipelines.py
│   ├── settings.py
│   └── spiders
│       └── __init__.py
└── scrapy.cfg

2 directories, 7 files

文件说明:

  • scrapy.cfg 项目的配置信息,主要为 Scrapy 命令行工具提供一个基础的配置信息。(爬虫相关的配置信息在settings.py 文件中)
  • items.py 设置数据存储模板,用于结构化数据
  • middlewares 中间件,全局处理请求
  • pipelines 数据处理行为,如:一般结构化的数据持久化,存储数据库等操作
  • settings.py 爬虫的配置文件,如:递归的层数、并发数,延迟下载等
  • spiders 爬虫目录,如:创建文件,编写爬虫规则

进入目录

cd demo
scrapy genspider example example.com   # 使用该命令安装模板生成 Spider

更详细的入门见官网:https://doc.scrapy.org/en/latest/intro/tutorial.html

架构

Scrapy使用了Twisted异步网络库来处理网络,可以对网站页面进行大量非阻塞的异步请求,能够对目标网站按照网站结构的层级次序逐级向下采集,并可以在已采集到的页面中提取其他符合要求的目标网页地址资源,从而实现从单个或多个入口进入,对目标网站进行全面扫描并获取所需的数据。结构如下:

Scrapy的核心组件:

  • 引擎(Scrapy Engine) 用来处理整个系统的数据流,触发事务(框架核心),负责控制和调度各个组件

  • 调度器(Scheduler) 用来接受引擎发过来的请求,压入队列中,并在引擎再次请求的时候返回,如:要抓取的链接(URL)的优先队列,由它来决定下一个要抓取的URL是什么,并进行去重。

  • 下载器(Downloader) 下载器负责对目标页面发出请求并获取页面反馈的数据,之后传递给Scrapy引擎,最终传递给爬虫进行数据提取。

  • 爬虫(Spider) 爬虫是Scrapy的用户自行编写的一段数据提取程序,针对下载器返回的数据结构进行分析(一般为HTML),并提取出其中的结构化数据,并可以指定其他需要跟进的URL和处理方法。每个爬虫负责处理一个或多个特定的网站。

  • 项目管道(Pipline) 负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体(Item)、验证实体的有效性、清除垃圾信息。当页面被爬虫解析后,解析后内容将会发送到项目管理通道,经过几个特定的次序处理。

  • 数据 (Item) Item是爬虫针对网页数据做解析后返回的数据,需要在使用之前预先定义好Item的数据结构,爬虫的解析程序负责将提取到的数据填充到Item中,并将Item返回,传递给数据管道进行后续处理。

  • 下载器中间件(Downloader Middlewares) 位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎和下载器之间的请求与响应。

  • 爬虫中间件(Spider Middlewares) 介于Scrapy引擎和Spider之间的框架,处理爬虫的响应输入和请求输出。

  • 调度中间件(Scheduler Middlewares) 介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。

图解见官网:https://doc.scrapy.org/en/latest/topics/architecture.html

使用 ImagesPipeline 下载图片

在 scrapy 中有实现的 ImagesPipeline , 默认即可下载大量的图片,如果想要实现自己的下载图片 Pipeline,并且自定义输出图片的文件的名字,可以重写 file_path() 方法。

import scrapy
from scrapy.pipelines.images import ImagesPipeline

class ImagePipeline(ImagesPipeline):
    default_headers = {
        'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
    }

    # 对各个图片URL返回一个Request
    def get_media_requests(self, item, info):
        try:
            for image_url in item['image_urls']:
                f = image_url.split('.')[-1]
                yield scrapy.Request(image_url, meta={'image_name': item['image_name'], 'format': f}, headers=self.default_headers)
        except Exception as error:
            print error

    # 当一个单独项目中的所有图片请求完成时(success, image_info_or_failure)
    def item_completed(self, results, item, info):
        image_paths = [x['path'] for ok, x in results if ok]
        if not image_paths:
            # raise DropItem("Item contains no images")
            print "Image path no exist"
        return item


    # Override the convert_image method to disable image conversion

    # scrapy convert image to jpg 重写此方法,可以下载自定的图片格式,不过可能需要特殊处理格式
    # def convert_image(self, image, size=None):
    #     buf = StringIO()
    #     try:
    #         image.save(buf, image.format)
    #     except Exception, ex:
    #         raise ImageException("Cannot process image. Error: %s" % ex)
    #
    #     return image, buf

    # 默认情况下,使用ImagePipeline组件下载图片的时候,图片名称是以图片URL的SHA1值进行保存的。
    # scrapy 0.12 可以覆盖 image_key 方法, 在此后版本中 使用 file_path 来自定义下载图片名称
    # def image_key(self, url):
    #     image_guid = hashlib.sha1(url).hexdigest()
    #     return 'full/%s.jpg' % (image_guid)

    # http://stackoverflow.com/questions/6194041/scrapy-image-download-how-to-use-custom-filename/22263951#22263951
    def file_path(self, request, response=None, info=None):
        name = request.meta['image_name']
        f = request.meta['format']
        return 'full/%s.jpg' % name

定义 middlewares

middlewares 是 Scrapy 在请求时中间必须经过的步骤,在 settings 中有设置 DOWNLOADER_MIDDLEWARES

import random

from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware

from scrapy.conf import settings


class RandomUserAgentMiddleware(UserAgentMiddleware):

    def __init__(self, user_agent=''):
        self.user_agent = user_agent

    # 每一请求都会走这个函数,在这里随机挑选 UA
    def process_request(self, request, spider):
        ua = random.choice(settings.get('USER_AGENT_LIST'))
        if ua:
            print "******Current UserAgent: %s **************" % ua

            request.headers.setdefault("User-Agent", ua)


class ProxyMiddleware(object):
    def process_request(self, request, spider):
        request.meta['proxy'] = random.choice(settings.get('HTTP_PROXY_LIST'))

多 pipeline 协同处理

Item 在 Spider 中构造之后会被传送到 Pipeline 中,按照一定的顺序执行。一般情况下 pipeline 会做一些数据处理或存储的事情,一般写数据库操作都放到 Pipeline 中。

当一个 Item 要被多个 pipeline 处理时,需要定义:

ITEM_PIPELINES = {
    'imdb.pipelines.MoviePipeline': 300,
    'imdb.image_pipeline.ImagePipeline': 300
}

此时,Item 就会被两个 pipeline 处理,如果某个 pipeline 处理某一类事件,比如上述例子中, MoviePipeline 处理数据的存储,而 ImagePipeline 处理图片的下载。


2017-04-23 scrapy , python , crawler , spider , 学习笔记

使用 pyenv 管理 Python 版本

记录一下使用过程,留备以后使用。

pyenv 是 Python 版本管理工具。 pyenv 可以改变全局的 Python 版本,安装多个版本的 Python, 设置目录级别的 Python 版本,还能创建和管理 virtual python environments 。所有的设置都是用户级别的操作,不需要 sudo 命令。

pyenv 主要用来管理 Python 的版本,比如一个项目需要 Python 2.x ,一个项目需要 Python 3.x 。 而 virtualenv 主要用来管理 Python 包的依赖,不同项目需要依赖的包版本不同,则需要使用虚拟环境。

pyenv 通过系统修改环境变量来实现 Python 不同版本的切换。而 virtualenv 通过将 Python 包安装到一个目录来作为Python 包虚拟环境,通过切换目录来实现不同包环境间的切换。

pyenv 的美好之处在于,它并没有使用将不同的 $PATH 植入不同的 shell 这种高耦合的工作方式,而是简单地在 $PATH 的最前面插入了一个垫片路径(shims):~/.pyenv/shims:/usr/local/bin:/usr/bin:/bin。所有对 Python 可执行文件的查找都会首先被这个 shims 路径截获,从而使后方的系统路径失效。

安装之前

不同系统请参考 Common build problems,安装必须的工具。

pyenv 安装

根据官网的 安装说明 或者 自动安装 。 如果使用 Mac 直接使用 Homebrew。安装成功后记得在 .bashrc 或者 .bash_profile 中添加三行来开启自动补全。

export PATH="$HOME/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

根据自己的环境配置。

自动安装

pyenv 提供了自动安装的工具,执行命令安装即可:

curl -L https://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | bash

保证系统有 git ,否则需要新安装 git。

手动安装

如果想要更加详细的了解安装过程,可以使用手动安装。将 pyenv 检出到你想安装的目录。建议路径为:$HOME/.pyenv

cd ~
git clone git://github.com/yyuu/pyenv.git .pyenv
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(pyenv init -)"' >> ~/.bashrc
source ~/.bashrc

添加环境变量。PYENV_ROOT 指向 pyenv 检出的根目录,并向 $PATH 添加 $PYENV_ROOT/bin 以提供访问 pyenv 命令的路径。

这里的 shell 配置文件(~/.bash_profile)依不同 Linux 而需作修改,如果使用 Zsh 则需要相应的配置 ~/.zshrc

在使用 pyenv 之后使用 pip 安装的第三方模块会自动安装到当前使用 python 版本下,不会和系统模块产生冲突。使用 pip 安装模块之后,如果没有生效,记得使用 pyenv rehash 来更新垫片路径。

pyenv 常用命令

使用 pyenv commands 显示所有可用命令

查看本机安装 Python 版本

使用如下命令查看本机安装版本

pyenv versions

星号表示当前正在使用的 Python 版本。使用 python -V 确认版本。

查看可安装 Python 版本

使用如下命令查看可安装版本

pyenv install -l

python 安装与卸载

$ pyenv install 2.7.3   # 安装python
$ pyenv uninstall 2.7.3 # 卸载python

python切换

$ pyenv global 2.7.3  # 设置全局的 Python 版本,通过将版本号写入 ~/.pyenv/version 文件的方式。
$ pyenv local 2.7.3 # 设置 Python 本地版本,通过将版本号写入当前目录下的 .python-version 文件的方式。通过这种方式设置的 Python 版本优先级较 global 高。

python优先级

shell > local > global

pyenv 会从当前目录开始向上逐级查找 .python-version 文件,直到根目录为止。若找不到,就用 global 版本。

$ pyenv shell 2.7.3 # 设置面向 shell 的 Python 版本,通过设置当前 shell 的 PYENV_VERSION 环境变量的方式。这个版本的优先级比 local 和 global 都要高。–unset 参数可以用于取消当前 shell 设定的版本。
$ pyenv shell --unset

$ pyenv rehash  # 创建垫片路径(为所有已安装的可执行文件创建 shims,如:~/.pyenv/versions/*/bin/*,因此,每当你增删了 Python 版本或带有可执行文件的包(如 pip)以后,都应该执行一次本命令)

pyenv-virtualenv

pyenv 插件: pyenv-virtualenv

使用自动安装 pyenv 后,它会自动安装部分插件,通过pyenv-virtualenv 插件可以很好的和 virtualenv 结合:

einverne@ev  ~  cd ~/.pyenv/plugins
einverne@ev  ~/.pyenv/plugins   master  ll
total 24K
drwxr-xr-x 4 einverne einverne 4.0K Apr 22 10:55 pyenv-doctor
drwxr-xr-x 5 einverne einverne 4.0K Apr 22 10:55 pyenv-installer
drwxr-xr-x 4 einverne einverne 4.0K Apr 22 10:55 pyenv-update
drwxr-xr-x 7 einverne einverne 4.0K Apr 22 10:55 pyenv-virtualenv
drwxr-xr-x 4 einverne einverne 4.0K Apr 22 10:55 pyenv-which-ext
drwxr-xr-x 5 einverne einverne 4.0K Apr 22 10:54 python-build

创建虚拟环境

$ pyenv virtualenv 2.7.10 env-2.7.10

若不指定python 版本,会默认使用当前环境python版本。如果指定Python 版本,则一定要是已经安装过的版本,否则会出错。环境的真实目录位于 ~/.pyenv/versions 下

列出当前虚拟环境

pyenv virtualenvs
pyenv activate env-name  # 激活虚拟环境
pyenv deactivate #退出虚拟环境,回到系统环境

删除虚拟环境

pyenv uninstall my-virtual-env
rm -rf ~/.pyenv/versions/env-name  # 或者删除其真实目录

使用pyenv 来管理python,使用 pyenv-virtualenv 插件来管理多版本 python包。此时,还需注意,当我们将项目运行的 env 环境部署到生产环境时,由于我们的 python 包是依赖python 的,需要注意生产环境的 python 版本问题。

所有命令

$ pyenv commands
activate
commands
completions
deactivate
doctor
exec
global
help
hooks
init
install
installer
local
offline-installer
prefix
rehash
root
shell
shims
uninstall
update                 # 更新 pyenv 及插件
version
--version
version-file
version-file-read
version-file-write
version-name
version-origin
versions
virtualenv
virtualenv-delete
virtualenv-init
virtualenv-prefix
virtualenvs
whence
which

PyCharm

PyCharm 中可以非常方面的切换 Python 环境非常方便。强推。

Tips

更换 pip 源

因为国内网络环境,如果在局域网内下载 pip 慢,可以尝试使用aliyun提供的镜像,创建 vim ~/.pip/pip.conf ,然后填入:

[global]
index-url = http://mirrors.aliyun.com/pypi/simple/

[install]
trusted-host=mirrors.aliyun.com

参考


2017-04-22 Python , pyenv , 经验总结

Redis 常用命令

Redis 常用的数据结构有 String, Hash, List, Set, Sorted Set. 前三种类型不用多讲,后两种 set 是单纯的集合, Sorted Set 是有序集合,在集合内可以根据 score 进行排序。 Redis 的命令不区分大小写,但通常情况下使用大写以示区分。

几个常用网址:

对Redis键的命名格式并没有强制性的要求,不过一般约定为,”对象类型:对象ID:对象属性“,比如使用 user:1:friends 表示 id 为 1 的用户的好友列表。为了方便后期维护,键的命名一定要有意义。

redis-cli 是 Redis 自带的命令行工具(类似于MySQL的mysql命令), 直接在命令行终端与 redis server 执行所有命令和返回响应。下面所有命令都可以在 cli 交互式命令行中执行。

交互式命令参数

redis-cli 命令行自带一些参数,可以使用 redis-cli --help 查看。

通常 -p 参数指定端口, -a 参数指定密码, -h 指定 hostname。

--stat 参数打印状态

如果本地没有安装 Redis,可以通过在线模拟尝试 Try Redis

基础命令

获取符合规则的键名列表

KEYS pattern

pattern 支持 glob 风格的通配符格式。

可以使用 EXISTS 命令来判断一个键是否存在

EXISTS key

使用 TYPE 键的数据类型

TYPE key

字符串类型操作命令

字符串类型,最大容量 512MB,字符串类型可以包含任何数据,图片的二进制,或者序列化的对象。

命令 行为
GET 获取存储在键中的值
SET 给 KEY 设置值
DEL 删除存储在KEY中的值,该命令可以用于所有类型

赋值与取值

将 value 关联到 key,如果 key 有值, SET 命令覆盖。对于某个原本带有生存时间(TTL)的键来说, 当 SET 命令成功在这个键上执行时, 这个键原有的 TTL 将被清除。

SET key value
GET key

使用 SETNX (set not exists)可以实现如果 key 存在时不做任何操作

SETNX key value  # 如果 key 存在,则返回 0,如果设置成功返回1

可以使用 SETEX 来针对 key 设置过期时间,以秒为单位

SETEX key seconds value

递增递减数字

让当前键值递增,操作键不存在时默认为0,当键不是整数时,报错. 对不存在的 key ,则设置 key 为 1

INCR key

通过 increment 参数来在 key 的基础上加上一个增量。

INCRBY key increment

递减数值,对于不存在的 key,设置为 -1

DECR key

递减一个量, DECRBY 为了增加可读性,完全可以使用 INCRBY 一个负值来实现同样的效果

DECRBY key decrement

增加指定浮点数

INCRBYFLOAT key increment

向尾部追加值

如果key 已经存在,并且 value 是一个字符串,那么 APPEND 将 value 追加到末尾

APPEND key value

获取字符串长度

返回 key 所存储的字符串长度

STRLEN key

多key操作

获取多个值

MGET key [key ...]

设置多个 key value, 一次性设置多个值,返回0 表示没有值被覆盖

MSET key value [key value ...]

其他复杂命令

SETRANGE key offset value  # 用 value 值覆盖给定 key 从 offset 开始所存储的字符串
MSETNX key value key value # 一次性设置多个值, SETNX 的multi 版本
GETSET key 				# 设置 key 的值,并返回 key 的旧值
GETRANGE key start end  # 截取 start 和 end 偏移量的字符串

散列类型操作命令

通过 HSET 建立的键是散列类型,用过 SET 命令建立的是字符串类型. 散列或者哈希非常适合存储对象,添加和删除操作复杂度平均O(1).

命令 行为
HSET 给散列起给定的键值名
HGET 获取给定散列值
HGETALL 获取散列包含的所有键值对
HDEL 如果给定键存在,则移除该键

赋值取值

将哈希表 key 中的域 field 的值设为 value 。

如果 key 不存在,一个新的哈希表被创建并进行 HSET 操作。

如果域 field 已经存在于哈希表中,旧值将被覆盖。

HSET key field value
HGET key field

HMSET key field value [field value ...]
HMGET key field [field ...]

当字段不存在时赋值

HSETNX key field value

获取所有域和值

HGETALL key

查看哈希表 key 中,给定域 field 是否存在。

HEXISTS key field

增加数字,返回增值后的字段值

HINCRBY key field increment

删除一个或者多个字段,返回被删除的字段个数

HDEL key field [field ...]

获取指定 hash 的 field 数量

HKEYS key

获取指定 hash 的 values

HVALS key

列表类型

有序的字符串列表,向列表两端添加元素,或者获取列表的某一个片段。列表类型内部使用双向链表,向列表两端添加元素时间复杂度O(1)

命令 行为
RPUSH 给定值加入列表右端
LRANGE 获取列表给定范围的所有值
LINDEX 获取列表在给定位置上的单个元素
LPOP 从列表左端弹出一个值,并返回被弹出的值

LPUSH 用来向列表左边增加元素,返回值表示增加元素后列表的长度,RPUSH 同理

LPUSH key value [value ...]
RPUSH key value [value... ]

从左边右边弹出元素

LPOP key
RPOP key

获取列表中元素的个数

LLEN key

获取列表中某一个片段

LRANGE key start stop

删除列表中指定的值

LREM key count value

获取设置指定索引的元素值

LINDEX key index

删除指定索引范围之外的所有元素。

LTRIM key start end

向列表中插入元素,将值 value 插入到列表 key 当中,位于值 pivot 之前或之后。

LINSERT key BEFORE | AFTER pivot value

先执行 RPOP 命令再执行 LPUSH 命令

RPOPLPUSH source destination

集合类型

set 是集合,和数学中的集合概念相似。 Redis 的 set 是 String 类型的无序集合,set 元素最大可包含 2 的 32 次方个元素。

向集合中增加一个或者多个元素,如果不存在则创建,如果存在则忽略。SREM 用来从集合中删除一个或者多个元素,并返回删除成功的个数

命令详解,将给定元素添加到集合

SADD key member [member ...]

如果给定的元素存在于集合中,移除该元素

SREM key member [member ...]

随机返回并删除一个元素

SPOP key

随机返回一个元素,但是不删除, Redis 2.6 版本之后接受可选 count 参数。

SRANDMEMBER key [count]

返回集合中的所有元素

SMEMBERS key

判断元素是否在集合中

SISMEMBER key member

集合间运算

集合差集 A-B

SDIFF key [key ...]

集合交集运算 A交B

SINTER key [key ..]

集合并集 A并B

SUNION key [key...]

获得集合中的元素个数

SCARD key

将结果保存到 destination 键中

SDIFFSTORE destination key [key ...]
SINTERSTORE destination key [key ...]
SUNIONSTORE destination key [key...]

有序集合

在集合的基础上加上了排序,有序集合的键被称为成员 member,每个成员都是不同的,有序集合的值称为分值 score,分值必须为浮点数。

有序集合中加入一个元素和该元素的分数,如果元素存在则用新的分数替换

ZADD key score member [score member ...]

获得元素分数

ZSCORE key member

获取排名在某个范围的元素列表,按照元素分数从小到大顺序返回索引从 start 到 stop 之间的所有元素,包括两端。可选参数可返回元素分数。但 stop 为 -1 时返回全部。

ZRANGE key start stop [WITHSCORES]

按 score 从大到小

ZREVRANGE key start stop

元素分数从小到大顺序返回元素分数在 min 和 max 之间的元素

ZRANGEBYSCORE key min max

增加某个元素分数,返回值为更改过后的分数

ZINCRBY key increment member

获取集合中元素的数量,返回 integer 数量

ZCARD key

获得指定分数范围内的元素个数,返回个数

ZCOUNT key min max

删除一个或者多个元素,返回成功删除的元素数量

ZREM key member [member ...]

按照排名范围删除元素,元素分数从小到大顺序(索引0表示最小值),删除指定排名范围内的所有元素,并返回删除的数量

ZREMRANGEBYRANK key start stop

按照分数范围删除元素,删除指定分数范围内的所有元素,返回删除元素的数量

ZREMRANGEBYSCORE key min max

获得元素的排名,从小到大顺序,分数最小排名为0。

ZRANK key member

获取元素的排名,从大到小

ZREVRANK key member

计算多个有序集合的交集,并将结果存储在 destination 键中,同样以有序集合存储,返回 destination 键中的元素个数

ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM | MIN | MAX]

AGGREGATE 是 SUM 时(默认值), destination 键中元素的分数是每个参与计算的集合中该元素分数的和。

其他情况同理,MIN 为最小值,MAX 为最大值

事务

Redis 中事务 transaction 是一组命令的集合。事务同命令一样都是 Redis 的最小执行单位。

MULTI
SADD ”user:1:following" 2
SADD "user:2:followers" 1
EXEC

事务中 WATCH 命令,监控一个或者多个键,一旦其中一个键被修改(或删除),之后的事务就不会执行,监控持续到 EXEC 命令

使用 DISCARD 命令来取消事务,DISCARD 命令来清空事务命令队列并退出事务上下文,回滚。

过期时间

关系型数据库一般需要额外设置一个字段“到期时间”,然后定期删除,而在 Redis 中可使用 EXPIRE 命令设置一个键的过期时间,到时间后 Redis 会自动删除它。

EXPIRE key seconds

返回1表示成功,0为键不存在或者设置失效。 EXPIRE 命令参数必须为整数,最小单位为1秒,如果想要更加精确的控制过期时间可以使用 PEXPIRE 命令,单位为毫秒,也可以使用 PTTL 来以毫秒为单位返回剩余时间。

TTL key

TTL 命令查看键多久时间被删除,当键不存在时返回 -2,当键不过期时返回-1

PERSIST key

取消键的过期时间,成功清除返回1,否则返回0

SORT 命令对列表类型,集合类型和有序集合类型键进行排序,可以完成关系型数据库中连接查询类似的任务。

SORT 命令时

  • 尽可能减少待排序键中的元素数量
  • 使用LIMIT参数只获取需要的数据
  • 排序的数据比较大,尽可能使用 STORE 参数将结果缓存

Redis Client

Redis 支持的客户端

https://redis.io/clients

2017-04-21 Redis , Database , 学习笔记

Redis 介绍

Redis (Remote Dictionary Server) 是由 Salvatore Sanfilippo(antirez) 开发的开源数据库,基于内存的 Key-Value 类型的 NoSQL 。在 DB Engines Ranking K-V 数据库中排行第一1

Redis 是 REmote DIctionary Server 远程字典服务 的缩写,他以字典结构存储数据,并允许其他应用通过 TCP 协议来读写字典中的内容。

Redis支持很多的特性:

  • 所有数据都必须放在内存中
  • 支持数据持久化:AOF和RDB两种类型
  • 支持异步数据复制

Redis Cluster 常用5种数据结构(String, Lists, Sets, Sorted Set, Hash) 以单进程方式处理请求,数据持久化和网络Socket IO等工作是异步进程

安装

源中安装

在Debian/Ubuntu/Linux Mint 下直接安装即可,但是 redis 对内核有要求,如果安装失败的时候, -uname -a 看一下自己的内核,如果版本太低就升级一下。

sudo apt-get install redis-server

安装成功之后就可以使用

sudo service redis-server status # 查看当前状态
sudo service redis-server stop/start # 等等来控制 redis-server 的状态

最方便最快捷的安装方式,如果使用 docker 也可以使用 docker 中官方的源。

手动安装

官网下载 https://redis.io/download

下载最新的稳定版 Redis,可以从 http://download.redis.io/redis-stable.tar.gz 获取最新稳定版

curl -O http://download.redis.io/redis-stable.tar.gz

解压 tag.gz

tar xzvf redis-stable.tar.gz

进入解压后目录

cd redis-stable

编译和安装,运行 make 命令

make

当二进制文件编译完成之后,运行 test 确保一切都正确

make test

当所有测试跑通过之后安装到系统

sudo make install

运行 test 的时候报了一个错误:

*** [err]: Test replication partial resync: ok psync (diskless: yes, reconnect: 1) in tests/integration/replication-psync.tcl

参考该 issue 使用单核运行 test

taskset -c 1 sudo make test

配置 Redis

在 etc 目录下新建 redis 配置文件目录

sudo mkdir /etc/redis

将默认配置文件拷贝到配置目录

sudo cp redis.conf /etc/redis

编辑配置文件

sudo vim /etc/redis/redis.conf

修改 supervised 为 systemd

# If you run Redis from upstart or systemd, Redis can interact with your
# supervision tree. Options:
#   supervised no      - no supervision interaction
#   supervised upstart - signal upstart by putting Redis into SIGSTOP mode
#   supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET
#   supervised auto    - detect upstart or systemd method based on
#                        UPSTART_JOB or NOTIFY_SOCKET environment variables
# Note: these supervision methods only signal "process is ready."
#       They do not enable continuous liveness pings back to your supervisor.
supervised systemd

接下来,寻找 dir 配置, 该参数制定 Redis 存储数据的目录,需要一个 Redis 有写权限的位置,使用 /var/lib/redis.

# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
dir /var/lib/redis

修改完毕,保存关闭。

创建 systemd unit

创建 redis.service 文件

sudo vim /etc/systemd/system/redis.service

[Unit] 单元中提供描述,和启动需要在网络可用之后。[Service] 中定义服务的具体动作,自定义用户 redis,以及 redis-server 的地址。

[Unit]
Description=Redis In-Memory Data Store
After=network.target

[Service]
User=redis
Group=redis
ExecStart=/usr/local/bin/redis-server /etc/redis/redis.conf
ExecStop=/usr/local/bin/redis-cli shutdown
Restart=always

[Install]
WantedBy=multi-user.target

创建 redis 用户,组

创建用户,组

sudo adduser --system --group --no-create-home redis

创建文件夹

sudo mkdir /var/lib/redis

给予权限

sudo chown redis:redis /var/lib/redis

修改权限,普通用户无法访问

sudo chmod 770 /var/lib/redis

运行 Redis

启动

sudo systemctl start redis

查看状态

sudo systemctl status redis

显示

sudo service redis status
● redis.service - Redis In-Memory Data Store
   Loaded: loaded (/etc/systemd/system/redis.service; disabled; vendor preset: enabled)
   Active: active (running) since Sat 2017-04-22 18:59:56 CST; 2s ago
 Main PID: 28750 (redis-server)
   CGroup: /system.slice/redis.service
           └─28750 /usr/local/bin/redis-server 127.0.0.1:6379       

Apr 22 18:59:56 ev redis-server[28750]:   `-._    `-._`-.__.-'_.-'    _.-'
Apr 22 18:59:56 ev redis-server[28750]:       `-._    `-.__.-'    _.-'
Apr 22 18:59:56 ev redis-server[28750]:           `-._        _.-'
Apr 22 18:59:56 ev redis-server[28750]:               `-.__.-'
Apr 22 18:59:56 ev redis-server[28750]: 28750:M 22 Apr 18:59:56.445 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower valu
Apr 22 18:59:56 ev redis-server[28750]: 28750:M 22 Apr 18:59:56.445 # Server started, Redis version 3.2.8
Apr 22 18:59:56 ev redis-server[28750]: 28750:M 22 Apr 18:59:56.445 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.ov
Apr 22 18:59:56 ev redis-server[28750]: 28750:M 22 Apr 18:59:56.445 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage 
Apr 22 18:59:56 ev redis-server[28750]: 28750:M 22 Apr 18:59:56.445 * DB loaded from disk: 0.000 seconds
Apr 22 18:59:56 ev redis-server[28750]: 28750:M 22 Apr 18:59:56.445 * The server is now ready to accept connections on port 6379

使用 redis-cli 客户端测试。

redis-cli

然后运行 ping ,会得到 PONG。

127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set test "It's working"
OK
127.0.0.1:6379> get test
"It's working"
127.0.0.1:6379> exit

然后重启 redis

sudo systemctl restart redis.service

然后进入 redis-cli:

127.0.0.1:6379> get test
"It's working"

如果能够获得,就证明配置好了。

开机启动

sudo systemctl enable redis

在启动了 redis 之后就可以再熟悉一下他的命令了。

多数据库支持

Redis 实例提供了多个用来存储数据库的字典,客户端可以用来指定将数据存储在哪个数据库中,类似关系型数据库可以新建很多个数据库,可以将 Redis 的每一个字典都理解成为一个数据库。

每个数据库对外都是以一个从0开始的递增数字命名, Redis 默认支持 16 个数据库。 客户端与 Redis 建立连接之后会自动选择 0 号数据库,不过随时可以使用 SELECT 命令来更换数据库,比如选择 1 号数据库 SELECT 1.

注意:Redis 不支持自定义数据库名,每个数据库都以编号命名;Redis 也不支持为每一个数据库设置不同的访问密码;多个数据库之间并不是完全隔离, FLUSHALL 命令可以清空 Redis 实例中所有数据库数据。

reference

参考: https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-redis-on-ubuntu-16-04


2017-04-20 Redis , Database , NoSQL , 学习笔记

每天学习一个命令:df 查看磁盘剩余空间

之前也介绍过 di disk information,不过系统默认不带,需要自己安装,如果遇到没有权限安装时,就可以使用 df 来查看当前机器剩余磁盘空间。

df 全称 disk filesystem,用于显示 Linux 系统磁盘利用率。

使用

直接使用 df ,显示设备名称、总块数、总磁盘空间、已用磁盘空间、可用磁盘空间和文件系统上的挂载点。

Filesystem     1K-blocks      Used Available Use% Mounted on
udev             8126360         0   8126360   0% /dev
tmpfs            1629376     75432   1553944   5% /run
/dev/sdb1      240230912 185617700  42387064  82% /
tmpfs            8146864    546884   7599980   7% /dev/shm
tmpfs               5120         4      5116   1% /run/lock
tmpfs            8146864         0   8146864   0% /sys/fs/cgroup
/dev/loop1         83712     83712         0 100% /snap/core/4206
/dev/loop2           128       128         0 100% /snap/anbox-installer/17
/dev/loop4        259584    259584         0 100% /snap/electronic-wechat/7
cgmfs                100         0       100   0% /run/cgmanager/fs
tmpfs            1629376        72   1629304   1% /run/user/1000
/dev/sda3      723180576     70584 686351464   1% /media/user/add8bd89-da2a-4573-ac6e-7ec44f8c5414
/dev/loop5         84096     84096         0 100% /snap/core/4327
/dev/loop3         95872     95872         0 100% /snap/slack/6
/dev/loop6         88704     88704         0 100% /snap/core/4407

df -h 可以显示比较友好的输出

Filesystem      Size  Used Avail Use% Mounted on
udev            7.8G     0  7.8G   0% /dev
tmpfs           1.6G   74M  1.5G   5% /run
/dev/sdb1       230G  178G   41G  82% /
tmpfs           7.8G  534M  7.3G   7% /dev/shm
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           7.8G     0  7.8G   0% /sys/fs/cgroup
/dev/loop1       82M   82M     0 100% /snap/core/4206
/dev/loop2      128K  128K     0 100% /snap/anbox-installer/17
/dev/loop4      254M  254M     0 100% /snap/electronic-wechat/7
cgmfs           100K     0  100K   0% /run/cgmanager/fs
tmpfs           1.6G   72K  1.6G   1% /run/user/1000
/dev/sda3       690G   69M  655G   1% /media/mi/add8bd89-da2a-4573-ac6e-7ec44f8c5414
/dev/loop5       83M   83M     0 100% /snap/core/4327
/dev/loop3       94M   94M     0 100% /snap/slack/6
/dev/loop6       87M   87M     0 100% /snap/core/4407

df -hT 其中 -T 参数显示文件类型 ext4 等等

Filesystem     Type      Size  Used Avail Use% Mounted on
udev           devtmpfs  7.8G     0  7.8G   0% /dev
tmpfs          tmpfs     1.6G   74M  1.5G   5% /run
/dev/sdb1      ext4      230G  178G   41G  82% /

df -ih 显示 inodes

Filesystem     Inodes IUsed IFree IUse% Mounted on
udev             2.0M   520  2.0M    1% /dev
tmpfs            2.0M   888  2.0M    1% /run
/dev/sdb1         15M  1.8M   13M   12% /

相关

查看磁盘占用 du


2017-04-12 linux , df , disk , 磁盘空间

Celery 使用介绍

Celery 简单来说就是一个分布式消息队列。简单、灵活且可靠,能够处理大量消息,它是一个专注于实时处理的任务队列,同时也支持异步任务调度。Celery 不仅可以单机运行,也能够同时在多台机器上运行,甚至可以跨数据中心。

Celery 中比较关键的概念:

  • worker: worker 是一个独立的进程,任务执行单元,它持续监视队列中是否有需要处理的任务;
  • broker: broker 消息传输中间件,任务调度队列,接收生产者发出的消息,将任务存入队列,broker 负责协调客户端和 worker 的沟通。客户端向队列添加消息,broker 负责把消息派发给 worker。
  • 任务模块:包含异步任务和定时任务,异步任务通常在业务逻辑中被触发并发往任务队列,定时任务由 celery beat 进程周期性发送
  • 任务结果 backend:backend 存储任务执行结果,同消息中间件一样,需要由其他存储系统提供支持

一个典型的 Celery 使用场景就是,当用户在网站注册时,请求可以立即返回而不用等待发送注册激活邮件之后返回,网站可以将发送邮件这样的耗时不影响主要流程的操作放到消息队列中,Celery 就提供了这样的便捷。

安装 Celery

直接使用 python 工具 pip 或者 easy_install 来安装:

$ pip install celery

安装 Broker

Celery 支持多种 broker, 但主要以 RabbitMQ 和 Redis 为主,其他都是试验性的,虽然也可以使用, 但是没有专门的维护者。如何在 RabbitMQ 和 Redis之间选择呢?

RabbitMQ is feature-complete, stable, durable and easy to install. It’s an excellent choice for a production environment.

Redis is also feature-complete, but is more susceptible to data loss in the event of abrupt termination or power failures.

Celery 官方明确表示推荐在生产环境下使用 RabbitMQ,Redis 存在丢数据的问题。所以如果你的业务可以容忍 worker crash 或者电源故障导致的任务丢失,采用 redis 是个不错的选择,本篇就以 redis 为例来介绍。

Celery 对于 redis 的支持需要安装相关的依赖,以下命令可以同时安装 celery 和 redis 相关的依赖,但是 redis server 还是必须单独安装的。

$ pip install -U celery[redis]  # -U 的意思是把所有指定的包都升级到最新的版本

Celery 的配置和使用

Celery 本身的配置项是很多的,但是如果要让它跑起来,你只需要加一行配置:

BROKER_URL = 'redis://localhost:6379/0'

这一行就是告诉 celery broker 的地址和选择的 redis db,默认是 0。接下来用个很简单的例子来介绍 celery 是如何使用的:

# task.py
from celery import Celery

broker = 'redis://127.0.0.1:6379/0'

app = Celery('tasks', broker=broker)

@app.task()
def add(x, y):
   return x + y

上述代码创建了一个 celery 的实例 app,可以通过它来创建任务和管理 workers。在上面的例子中,我们创建了一个简单的任务 task,它返回了两个数相加后的结果。然后启动celery 服务,通过它来监听是否有任务要处理。

$ celery worker -A task -l info
  • -A 选项指定 celery 实例 app 的位置,本例中 task.py 中自动寻找,当然可以直接指定 celery worker -A task.app -l info
  • -l 选项指定日志级别, -l--loglevel 的缩略形式

其他更多选项通过 celery worker –-help 查看

调用任务或者发送消息

然后我们再打开一个 shell 窗口,进入 python 控制台去调用 add 任务:

>>> from task import add
>>> add.delay(1, 2)
<AsyncResult: 42ade14e-c7ed-4b8d-894c-1ca1ec7ca192>

delay()apply_async 的简写,用于一个任务消息(task message)。我们发现 add 任务并没有返回结果 3,而是一个对象 AsyncResult,它的作用是被用来检查任务状态,等待任务执行完毕或获取任务结果,如果任务失败,它会返回异常信息或者调用栈。

我们先尝试获取任务的执行结果:

>>> result = add.delay(1, 2)
>>> result.get()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/celery/result.py", line 169, in get
    no_ack=no_ack,
  File "/usr/local/lib/python2.7/dist-packages/celery/backends/base.py", line 604, in _is_disabled
    'No result backend configured.  '
NotImplementedError: No result backend configured.  Please see the documentation for more information.

报错了: No result backend configured. 错误信息告诉我们没有配置 result backend。因为 celery 会将任务的 状态或结果保存在 result backend,result backend 的选择也有很多,本例中依然选用 redis 作为 result backend。

我们修改 task.py 的代码,添加上 result backend 的设置,保存后重启 celery worker。

# task.py
...
app = Celery('tasks', backend='redis://localhost', broker='redis://localhost//')
...

然后重新调用 add task:

>>> from task import add
>>> result = add.delay(1,2)
>>> result.get()
3

Celery Flower

flower 是一个 celery 的监控工具,它提供了一个图形用户界面,可以极大的方便我们监控任务的执行过程, 执行细节及历史记录,还提供了统计功能。

flower 安装

pip install flower

or:

easy_install flower

flower 使用简介,首先启动通过命令行启动 flower 进程:

flower -A proj --port=5555

然后打开浏览器 http://localhost:5555/

celery flower

Celery 任务类型

apply_async

调用一个异步任务,这也是最常用的任务类型之一,delay 与它的作用相同,只是 delay 不支持 apply_async 中额外的参数。该方法有几个比较重要的参数,在实际应用中会经常用到:

countdown: 任务延迟执行的秒数,默认立即执行; eta:任务被执行的绝对时间

crontab 计划任务

Celery 同样也支持定时任务:

from datetime import timedelta
from celery.schedules import crontab
 
app.conf.beat_schedule = {
    # Executes every Monday morning at 7:30 A.M
    'add-every-monday-morning': {
        'task': 'tasks.add',
        'schedule': crontab(hour=7, minute=30, day_of_week=1),
        'args': (20, 20),
    },
    # execute every ten minutes
   'every_ten_minutes': {
        'task': 'worker.cntv.cntv_test',
        'schedule': timedelta(minutes=10),
        'args': ('args1',),
        'options': {
            'queue': 'queue_name'
        }
    }, 
}

要启动定时任务,需要启动一个心跳进程,假设

celery beat -A celery_app.celery_config -s /path/to/celerybeat-schedule -l info

其中 -s 参数指定 celerybeat 文件保存的位置。beat 主要的功能就是将 task 下发到 broker 中,让 worker 去消费。

reference


2017-04-10 celery , python , queue , task , distribution

电影网站评分机制

年前的时候喉舌媒体批评豆瓣,猫眼等评分太低影响了票房,而导致16年的年度票房目标没有达到,广电很生气,后果很严重。可是豆瓣存在了那么多年,那么多的电影,在院线上映的,还是不上映的,从来也没有听说过 IMDB 或者 烂番茄的评分会影响到总体的票房。虽然得分的多少或多或少的会对票房有所影响,可这难道是豆瓣,或者 IMDB 或 烂番茄这样的影评网站应该承担的责任吗? 制片公司,发行商,甚至细化到导演,演员,剧本,在国内甚至可以拉上审查来负责,动不动删掉个14分钟,谁还愿意花了冤枉钱去大荧幕看一个不完整的片子呢?真正的影迷 大概会愿意花个机票钱去看一个完整版吧。

当然也不想过多的吐槽,或许被“认证”也才能证明豆瓣的评分也算良心吧。这里就看看国内玩几家影评站对网站打分的计分规则。其实早在很早就将计算的公式记录在了记事本里面,一直没有整理。而现在想要来整理一下,也是感觉豆瓣评分在一定程度上没有想象的真实,看过一部被恶意差评的国产片,看后感觉并不是5分多的水平,后来看评论才知道其中的某一位演员的黑粉恶意差评才导致这样的结果,而看一些长评论确实客观很多。或许是差评的人,没那么多的时间来写长评吧。所以就像那篇评论中说的那样,“中国电影市场的正常发展,不仅需要好的导演,好的编剧,好的演员,还需要好的观众”。

BGM,找资料时偶得,为某一期奥斯卡缅怀逝去的人时的背景音乐

豆瓣

先来说一说我使用最多的豆瓣,豆瓣也是评分规则中最简单的,豆瓣不人工干预评分,而一部电影的最终得分就是由每个用户的打分的加权平均,举个例子,一个用户打5星,一个用户打3星,一个用户打1星,那么这部片子就是(5+3+1)/3 也就是3星,6分。

豆瓣最后得分的具体公式1

其中, $x_1$ 表示打1颗星的人数,$x_2$ 表示打2颗星的人数,以此类推。由该公式能够看出,豆瓣的评分是很简单的计算,而至少一颗星(2分)的最低评分,也无形中提高了影片的评分,因为豆瓣根本不存在0分的电影,哦,不,还是有的。其实,豆瓣一直是一个满分8分的机制,那些超过8分的电影,是一定不会差的。所以曾经有段时间,找不到片子看的时候就直接找8分以上的片子看。

screenshot-area-2017-04-08-154156

豆瓣的评分机制简单粗暴,在降低用户打分思考的时候,也会造成用户对一部影片的看法截然不同,尤其是在恶意刷分时,会导致最后的评分波动较大。曾经有人开过玩笑说过豆瓣的评分图案,r 型(5星占大多数)的为口碑爆棚的好片,P 型为普通好片,b 型为普通烂片,而 C 型是水军刷出来的烂片,还有 L 型是多少水军都刷不出来的超级烂片。现在想来还是依然非常好玩。

时光网

时光网的存在感近两年被慢慢的抹去,但还依然半死不活的存在,时光网和豆瓣的评分机制一样,都是加权平均,只是时光网采用的是10分制,也就是用户有10个选择,用户需要话时间在评分的分数上,更多的选择,使得绝大部分用户选择中间段进行评分,因而导致最终的评分呈现中庸状态,同样无法真正体现出一部电影的真正得分。

而这样的十分制同样会导致在遭受大规模恶意打分(无论是好评还是差评)之后直接在最终结果中明显体现。

IMDB

IMDB 是国外最大的电影资料站,大家经常提到的 IMDB TOP 250,也就是在该站上评分最高的 250 名。他采用贝叶斯算法,具体的公式2

其中:

  • WR,加权得分 weighted rating
  • R,本影片的平均得分 rating
  • v,评分人数 votes
  • m,基准票数,进入 IMDB Top 250 的最小票数
  • C,站点所有电影的平均分

这个公式的目的是为了让得分更加偏向于平均分,如果投票越多,评分就越接近真实的平均分,否则就越接近所有电影的平均分。而这个公式的唯一人为设定的参数就是基准票数。而这个参数的设定也正是为了解决如何让冷门和热门影片在得分上具有可比性。冷门片不会因为爱好者而导致评分异常高,这个问题也是豆瓣经常遇到的问题,一些冷门韩综,日剧,韩剧在评分上都有一定的偏高。

而关于 IMDB 这个公式是怎么防止恶意刷分,有兴趣可以了解一下当年《蝙蝠侠》和《教父》的往事:

烂番茄

烂番茄主要是专业影评人士评价汇总,和 IMDB 和 豆瓣这样单纯由网名进行投票的评分制度有些不同。而烂番茄通过新鲜度来对电影进行评价,而这里的新鲜度并不是实际意义上的评分,而是由影评人对该影片正面打分的比例来决定的,若正面的评价超过60%以上,该部作品将会被认为是“新鲜”(fresh)。如果正面评价超过 75 % ,那么该作品会得到“Certified Fresh” 的评价,而如果一部作品的正面评价低于60%,那么该作品会被标示为“腐烂”(rotten)。影评人只有两个选项,正面和反面。

烂番茄和其他影评网站的最大区别是,他突出的是人群对一部电影持有的主流观点,而不是一个让每个人都感同身受的数值。

Metacritic

Metacritic 是一个综合性评定网站,影评只是该网站其中的一个小模块,该网站上影评人多以纸媒为主。 Metacritic 的评分主要从主流媒体和专业影评机构聚合而来,这些影评人和其供职的机构大多在影评方面具有公信力,比如《卫报》、《纽约时报》、《时代周刊》等等。但是并不是每一个机构和影评人都给出一个确切的分数。 Metacritic 具体做法是,如果来源有具体评分则使用来源评分,来源有星级打分则换算成百分制,如果来源影评只提供文字,然后他们自己去找人阅读影评,根据读完的感受给分。3

比较

各家网站都有各家的好坏,豆瓣的评分机制是最简单高效的,这也是绝大多数的系统惯常的做法。但正是这样的机制使得刷分异常容易,大批量的差评或者好评能在短时间内影响影片总体的分数。另外一个比较严重的问题就是,无法在冷门片和热门片之间比较,这也是豆瓣官方博客在文章中提及的,热门影片能在短时间内获得几十万的评分,但是一些冷门片,或者一些上映时间比较久远的电影可能难以达到这么多的评分,这样就会导致热门片和冷门片在评分上无法比较。口碑比较好的热门片可能因为观众口味不一而导致评分稍中庸,而冷门片可能因为资深影迷而导致评分过高。因此在豆瓣看评分时,一般还需要看一下评分人数。而最近我也会看一下长评论,毕竟愿意花时间来评价一部影片,远比花1秒打个评分要来的认真。

而 IMDB 的评分方式一定程度上解决了冷门影片和热门影片评分上的差异,但是选择基准票数却也需要经过不断的调整,IMDB 历史上也经历过变化,根据该数据,阈值从 3000 票提升到了 25000 票,这次变换也相应的造成了最后得分的变化,尤其是影响了得到25000票以下,并且得分较高的影片。可以说只有当影片的评分人数足够多时,基准票数的影响才会减至最小,而对于票数比较少的影片,就相当于一次洗牌。

而对于烂番茄和国内的猫眼专家评分,其实一定意义上说代表着专业领域的人士意见,这些评论都值得一读,但是更多的需要自己的看法,只有最后形成自己的世界观那部分东西才真正属于自己。所以豆瓣和IMDB 对于我来说,一方面提供给我足够的信息,包括导演,演员,编剧等等,另一方面也是让我远离烂片,毕竟看一部烂片浪费的是自己的时间。

最后,引用数位时代中的一句话,“在美国,佳片会收到它应得的票房和好评作为奖赏,烂片就算进了电影院也不可能躲得开差评—-无论在报纸、电台还是在网络上。在美国,对电影的批评,也是言论自由保护的一部分”。

若差评不自由,则高分无意义。


2017-04-08 Movie , Douban , IMDB

每天学习一个命令:lsof 列出打开的文件

lsof 用于列出当前系统打开的文件 (list open files),在 Linux 中,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件。所以如传输控制协议 (TCP) 和用户数据报协议 (UDP) 套接字等,系统在后台都为该应用程序分配了一个文件描述符,无论这个文件的本质如何,该文件描述符为应用程序与基础操作系统之间的交互提供了通用接口。因为 lsof 命令需要访问核心内存和各种文件,所以需要 root 用户执行。

简单使用

比如可以使用 lsof 来查看当前系统中 80 端口是否被占用

sudo lsof -i tcp:80
COMMAND     PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
docker-pr 14863 root    4u  IPv6 38693061      0t0  TCP *:http (LISTEN)

然后获取到 PID 之后可以用 lsof 来查看进程

sudo lsof -p 14863
COMMAND     PID USER   FD      TYPE   DEVICE SIZE/OFF       NODE NAME
docker-pr 14863 root  cwd       DIR      8,0     4096          2 /
docker-pr 14863 root  rtd       DIR      8,0     4096          2 /
docker-pr 14863 root  txt       REG      8,0  3329080      17531 /usr/bin/docker-proxy
docker-pr 14863 root  mem       REG      8,0  1868984      20743 /lib/x86_64-linux-gnu/libc-2.23.so
docker-pr 14863 root  mem       REG      8,0   138696      11625 /lib/x86_64-linux-gnu/libpthread-2.23.so
docker-pr 14863 root  mem       REG      8,0   162632      10738 /lib/x86_64-linux-gnu/ld-2.23.so
docker-pr 14863 root    0r      CHR      1,3      0t0       8085 /dev/null
docker-pr 14863 root    1w      CHR      1,3      0t0       8085 /dev/null
docker-pr 14863 root    2w      CHR      1,3      0t0       8085 /dev/null
docker-pr 14863 root    4u     IPv6 38693061      0t0        TCP *:http (LISTEN)
docker-pr 14863 root    5u  a_inode     0,12        0       8082 [eventpoll]
docker-pr 14863 root   12r      REG      0,3        0 4026531993 net

由以上的信息就能看出来我的机器中的 80 端口是 docker 占用的,docker 的位置在系统中的 /usr/bin/docker-proxy

lsof 输出各列信息的意义如下:

  • COMMAND:进程的名称
  • PID:进程标识符
  • PPID:父进程标识符(需要指定 -R 参数)
  • USER:进程所有者
  • PGID:进程所属组
  • FD:文件描述符,应用程序通过文件描述符识别该文件。如 cwd、txt 等

FD 的取值

  • cwd:表示 current work dirctory,即:应用程序的当前工作目录,这是该应用程序启动的目录,除非它本身对这个目录进行更改
  • txt :该类型的文件是程序代码,如应用程序二进制文件本身或共享库,如上列表中显示的 /sbin/init 程序
  • lnn:library references (AIX);
  • er:FD information error (see NAME column);
  • jld:jail directory (FreeBSD);
  • ltx:shared library text (code and data);
  • mxx :hex memory-mapped type number xx.
  • m86:DOS Merge mapped file;
  • mem:memory-mapped file;
  • mmap:memory-mapped device;
  • pd:parent directory;
  • rtd:root directory;
  • tr:kernel trace file (OpenBSD);
  • v86 VP/ix mapped file;
  • 0:表示标准输出
  • 1:表示标准输入
  • 2:表示标准错误

一般在标准输出、标准错误、标准输入后还跟着文件状态模式:r、w、u 等

  • u:表示该文件被打开并处于读取 / 写入模式
  • r:表示该文件被打开并处于只读模式
  • w:表示该文件被打开并处于
  • 空格:表示该文件的状态模式为 unknow,且没有锁定
  • -:表示该文件的状态模式为 unknow,且被锁定

介绍

在有了基本的概念之后来看 lsof 的参数

lsof  [ -?abChKlnNOPRtUvVX ] [ -A A ] [ -c c ] [ +c c ] [ +|-d d ] [ +|-D D ] [ +|-e s ] [ +|-E ] [ +|-f [cfgGn] ] [ -F [f] ] [ -g [s] ] [ -i [i] ] [ -k k ] [ +|-L [l] ] [ +|-m m
   ] [ +|-M ] [ -o [o] ] [ -p s ] [ +|-r [t[m<fmt>]] ] [ -s [p:s] ] [ -S [t] ] [ -T [t] ] [ -u s ] [ +|-w ] [ -x [fl] ] [ -z [z] ] [ -Z [Z] ] [ -- ] [names]

能看到非常多的选项,因此也能看到 lsof 的强大。

-i 选项查看链接

默认 : 没有选项,lsof 列出活跃进程的所有打开文件
-i : 列出网络连接

使用 lsof -i 来显示所有的链接,可以用来代替 netstat

sudo lsof -i
COMMAND     PID     USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
sshd       2972     root    3u  IPv4 18883553      0t0  TCP *:22 (LISTEN)
sshd       2972     root    4u  IPv6 18883562      0t0  TCP *:22 (LISTEN)
docker-pr 14852     root    4u  IPv6 38693034      0t0  TCP *:https (LISTEN)
docker-pr 14863     root    4u  IPv6 38693061      0t0  TCP *:http (LISTEN)

输出结果有缩略,也能看出来 22 的 SSH 端口,然后 http 默认的 80 端口,和 https 使用的 443 端口。如果要查看 IPv6 的流量可以使用 lsof -i 6

同样如果要查看 TCP UDP 连接信息,lsof -iTCPlsof -iUDP。再比如查看和本地 22 端口的连接 lsof -i:22

显示来自特定主机的连接,lsof -i@1.2.3.4 ,指定主机和端口 lsof -i@1.2.3.4:22

-p 选项查看指定进程

使用 -p 查看指定进程 ID 已打开的内容

lsof -p 10075

列出用户打开的文件

lsof -u einverne

查看 java 项目依赖的 jar

比如说如果系统中依赖的一个 jar 被发现有漏洞,比如说可以查看 fastjson 在系统中有没有使用。

lsof -X | grep fastjson

2017-04-02 lsof , linux , file , command

电子书

Google+

最近文章

  • 使用 alembic 迁移数据库结构 Alembic 是一个处理数据库更改的工具,它利用 SQLAlchemy 来实现形成迁移。 因为 SQLAlchemy 只会在我们使用时根据 metadata create_all 方法来创建缺少的表 ,它不会根据我们对代码的修改而更新数据库表中的列。它也不会自动帮助我们删除表。 Alembic 提供了一种更新 / 删除表,更改列名和添加新约束的方法。因为 Alembic 使用 SQLAlchemy 执行迁移,它们可用于各种后端数据库。
  • 每天学习一个命令:iotop 查看 Linux 下每个进程 IO 占用 iotop 是一个用来监控磁盘 I/O 的类似 top 的工具,Linux 下 IO 统计工具,比如 iostat, nmon 等只能统计到每个设备的读写情况,如果想要知道哪一个进程占用比较高的 IO 就要使用 iotop。 iotop 使用 Python 语言编写,要求 Python >= 2.5,Linux Kernel >= 2.6.20.
  • 修正关于 HTTP Header 的错误认识 HTTP 请求的 Header 是不区分大小写的!,一直以为 HTTP 请求的请求头是有区分大小的,知道今天调试发现 Spring 将 header 全部处理成小写,然后有人提了 Bug 58464 然后看到 Stackoverflow 上面有人回答。
  • 解决 failed to create bus connection no such file or directory 错误 今天在修改 hostname 使用 sudo hostnamectl set-hostname ds 命令时遇到问题:
  • Vim 的颜色主题 Retro groove color scheme for Vim