Cindy

Cindy 查看完整档案

深圳编辑  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

Cindy 发布了文章 · 2020-06-19

apache+mod_wsgi+flask 详细安装步骤

记录 apache+mod_wsgi+flask 基于 python3 详细安装步骤,同时配置站点访问的 html 和站点 restful api 请求

本机环境介绍

  1. 已安装 apache/2.4.6
  2. 已安装 python3.6
  3. 系统是 centos7

安装 mod_wsgi

因为我是按照 apache 加载扩展的方式引入 mod_wsgi ,所以我需要拿到编译后的 mod_wsgi.so 文件。可以自己去 github下载文件进行编译安装,下面是我使用的 pip3 安装

  1. 使用 pip3 安装 mod_wsgi

    $ yum install python36-devel httpd-devel
    $ /usr/bin/pip3 install -v mod_wsgi
  2. 安装成功后找到 mod_wsgi 文件夹下 server 文件夹 path/to/python3.6/site-package/smod_wsgi/server,在该文件夹下有个 mod_wsgi 的 .so 文件 mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so,在 apache 中引入该扩展:

    # 进入 mod_wsgi 下 server 文件夹
    $ cd /usr/local/lib64/python3.6/site-packages/mod_wsgi/server
    # 将文件夹下 mod_wsgi.so 文件复制到 apache 的 modules 模块下
    $ cp mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so /usr/local/apache/modules/mod_wsgi.so

在虚拟环境中安装Flask

  1. pip 安装virtualenv

    $ pip3 install virtualenv
  2. 创建开发项目 project ,并进入该项目

    $ mkdir project
    $ cd project
  3. 创建一个独立的Python运行环境,命名为 venv ,并用 source 进入该环境

    $ virtualenv --no-site-packages venv
    $ source venv/bin/activate
  4. 在虚拟环境中安装 flask

    $ pip3 install flask

配置站点

/data/www/project 
├── api
│    └── api.py
├── index.html

站点目录结构如图所示,首页是根目录下的 index.html 请求http://localhost/index.html ,api请求为 http://localhost/api/list

  1. 在 api 目录下新建文件 app.wsgi

    import sys
    sys.path.insert(0, "/data/www/project/api/")
    from api import app as application
    
    if __name__ == "__main__":
       application.run()
  2. 编辑 api.py 文件

    from flask import Flask,jsonify
    app = Flask(__name__)
    
    @app.route('/list')
    def getList():
        return jsonify('success')
    
    if __name__ == '__main__':
        app.run()

配置 Apache

  1. 加载 mod_wsgi.so 模块,修改 apache 的 httpd.conf ,加入 LoadModule wsgi_module modules/mod_wsgi.so

    $ vim /usr/local/apache/conf/httpd.conf
    LoadModule wsgi_module modules/mod_wsgi.so
  2. 修改站点配置文件

    $ vim /usr/local/apache/conf/vhost/default.conf
    <VirtualHost *:80>
      ServerName localhost
      
      # 指定根目录
      DocumentRoot /data/www/project/
      
      # WSGIDaemonProcess用于指定应创建不同的守护进程,设置守护进程组的名称为 wsgi_api
      # threads 指定线程数为5
      # python-home 指定守护进程使用的 Python 虚拟环境的位置为/data/www/project/venv
      # python-path 引用 Python 虚拟环境的 site-packages 目录
      WSGIDaemonProcess wsgi_api threads=5 python-home=/data/www/project/venv python-path=/data/www/project/venv/lib/python3.6/site-packages
      
      # WSGIScriptAlias 与 Alias 指令相同,将特定文件路径 /api 标记为脚本 /data/www/project/api/app.wsgi,此脚本应由mod_wsgi 的 wsgi-script 处理程序处理
      WSGIScriptAlias /api /data/www/project/api/app.wsgi
      
      <Directory /data/www/project/api>
           WSGIProcessGroup wsgi_api
           WSGIApplicationGroup %{GLOBAL}
           # WSGIScriptReloading 设置对WSGI脚本文件的更改都触发重新加载机制
           WSGIScriptReloading On
           AllowOverride None
           Require all granted
        </Directory>
    </VirtualHost>
  3. 重启 apache

    $ /etc/init.d/httpd restart

测试

  1. 直接访问首页 http://localhost/index.html
  2. 请求接口 curl http://localhost/api/list

遇到并解决的问题

问题1:

/usr/bin/pip3 install -v mod_wsgi 报错 src/server/wsgi_python.h:24:20: fatal error: Python.h: No such file or directory

答案:
$ yum install python36-devel httpd-devel
问题2:

apache 可以不加配置选项 WSGIDaemonProcess

答案:

修改 app.wsgi 文件为

# 进入虚拟环境
activate_this = '/data/www/project/venv/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))

import sys
sys.path.insert(0, "/data/www/project/api/")
from api import app as application

if __name__ == "__main__":
   application.run()

参考资料

  1. mod_wsgi 文档
  2. mod-wsgi 4.7.1 安装文档
查看原文

赞 1 收藏 1 评论 0

Cindy 赞了回答 · 2020-05-20

解决MongoDB数据不存在时插入,已存在时更新

MongoDB中引入了一种叫做upsert的特殊更新,如果没有文档符合更新条件,就会以这个条件和更新文档为基础创建一个新的文档;如果找到了匹配文档,则正常更新。

详见MongoDB的文档

关注 2 回答 4

Cindy 回答了问题 · 2020-03-30

解决为什么0等于任何东西

  1. == 是类型转换之后相等即为 TRUE
  2. 如果你想比较 0 与 '0' 的类型的话,应该使用 ===

关注 5 回答 5

Cindy 回答了问题 · 2020-03-30

composer 出现 Fatal error: Class 'Phar' not found in 报错信息?

  1. 确认 Phar 扩展是否被安装启用

    $ php -i | grep 'phar'
  2. 如果已安装但未被启用

    $ php -d extension=phar.so composer.phar <your_script>

关注 4 回答 3

Cindy 赞了回答 · 2020-03-26

csrf token 生成与验证是如何做的呢?

  1. CSRF=攻击者构建一个请求,引诱有某网站权限的人发出,达成目的
  2. 所以,要防止 CSRF,只要让用户身份验证在这种情况下失效即可
  3. 所以,只要满足这些条件就可以

    1. 服务器端会校验 csrf token
    2. csrf token 不能被轻易破解
    3. 二选一:

      1. csrf token 以某种 无法构建 的方式发送给服务器
      2. 攻击者难以取得 csrf token

回到题目,我觉得你描述的做法是加大攻击者取得和重复利用 csrf token 的难度,并非一定要这么做。

关注 3 回答 2

Cindy 发布了文章 · 2019-04-23

Selenium + chromeDriver + Python3 完成 Flash 播放

在使用 selenium + chromeDriver + python3 截图时,遇上 Flash 无法加载,导致了截图 Falsh 是空白区。

环境要求:selenium chromeDriver Python3

问题

chrome 无头浏览器无法自动加载 Flash

解决办法

参考了 allow-flash-content-in-chrome-69-running-via-chromedriver 的回答,直接修改 Chrome 的设置 chrome://settings/content/siteDetails?site= 里面的 Flash 设置,修改为 Allow

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select

class chromeDriver():

    def __init__(self, driver = ''):
        # 设置窗口大小
        self.window_width  = 1680
        self.window_height = 948
        # 设置 chromedriver 位置
        self.executable_path = '/usr/local/bin/chromedriver'
        # 设置 Flash 的路径
        self.flash_path     = '/Users/cindy/Library/Application Support/Google/Chrome/PepperFlash/32.0.0.171/PepperFlashPlayer.plugin'
        # 获取 driver
        if driver: self.driver = driver
        else:
            self.driver = self.get_chrome_driver()
        

    def get_chrome_driver(self):
        # 头部
        user_agent         = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
        # 创建参数对象
        options         = webdriver.ChromeOptions()

        prefs = {
            # 开启图片
            "profile.managed_default_content_settings.images":1,
            # 关闭 Notification
            "profile.default_content_setting_values.notifications": 2,
        }

        # 设置 Flash 的路径
        options.add_argument('--ppapi-flash-version=32.0.0.171')
        options.add_argument('--ppapi-flash-path=' + self.flash_path)
        options.add_argument('binary_location=/Applications/Google Chrome.app/Contents/MacOS/Google Chrome')
        # 指定屏幕分辨率
        options.add_argument('window-size=' + str(self.window_width) + 'x' + str(self.window_height) + '\'')
        # 最大化窗口
        options.add_argument('--start-maximized')
        # 规避bug
        options.add_argument('--disable-gpu')
        # 禁用弹出拦截
        options.add_argument('--disable-popup-blocking')
        # 隐藏自动软件
        options.add_argument('disable-infobars')
        # 设置中文
        options.add_argument('lang=zh_CN.UTF-8')
        #忽略 Chrome 浏览器证书错误报警提示
        options.add_argument('--ignore-certificate-errors')
        # 更换头部
        options.add_argument('user-agent=' + user_agent)
        options.add_argument('no-default-browser-check')
        # 关闭特征变量
        options.add_experimental_option('excludeSwitches', ['enable-automation'])
        options.add_experimental_option('prefs', prefs)
        # 创建 Chrome 对象
        driver     = webdriver.Chrome(options = options, executable_path = self.executable_path)

        return driver

    def get(self, web_url):
        if not web_url: return False
        return     self.driver.get(web_url)

    def add_flash_site(self, web_url):
        if not web_url: return False
        self.get("chrome://settings/content/siteDetails?site=" + web_url)
        root1 = self.driver.find_element(By.TAG_NAME, "settings-ui")
        shadow_root1 = self.expand_root_element(root1)
        root2 = shadow_root1.find_element(By.ID, "container")
        root3 = root2.find_element(By.ID, "main")
        shadow_root3 = self.expand_root_element(root3)
        shadow_root3 = self.expand_root_element(root3)
        root4 = shadow_root3.find_element(By.CLASS_NAME, "showing-subpage")
        shadow_root4 = self.expand_root_element(root4)
        root5 = shadow_root4.find_element(By.ID, "advancedPage")
        root6 = root5.find_element(By.TAG_NAME, "settings-privacy-page")
        shadow_root6 = self.expand_root_element(root6)
        root7 = shadow_root6.find_element(By.ID, "pages")
        root8 = root7.find_element(By.TAG_NAME, "settings-subpage")
        root9 = root8.find_element(By.TAG_NAME, "site-details")
        shadow_root9 = self.expand_root_element(root9)
        root10 = shadow_root9.find_element(By.ID, "plugins")
        shadow_root10 = self.expand_root_element(root10)
        root11 = shadow_root10.find_element(By.ID, "permission")
        Select(root11).select_by_value("allow")

    def expand_root_element(self, element):
        return self.driver.execute_script("return arguments[0].shadowRoot", element)


    def get_flash_url(self, web_url):
        if not web_url: return False
        self.add_flash_site(web_url)
        self.get(web_url)

    def quit_driver(self):
        self.driver.quit()



driver = chromeDriver()
url = 'http://your.website/'
driver.get_flash_url(url)

最后

不能使用无界面模式,不能设置 handless 参数 options.add_argument('--headless')。否则无法直接修改 Chrome 的设置。

查看原文

赞 0 收藏 0 评论 0

Cindy 发布了文章 · 2019-04-12

详解相似图片匹配算法:差异值哈希算法 + 颜色直方图

由于最近涉及到匹配相似图片的问题,所以在此记录下解决办法:差异值哈希算法 + 颜色直方图

环境要求:Python cv2库 math库

差异值哈希算法

检索相似图片,第一个想到的就是差异值哈希算法。这个算法的步骤是:

  1. 缩小尺寸
    一般将图片缩放为 8 * 8 的尺寸大小,共64个像素的图片。但是由于64个像素对于我来说,损失的细节太多所以我选择了缩放到 33 * 32 的尺寸大小
  2. 彩色图像灰度化
    由于我们现有的图片是由 RGB 三原色构成,每个像素点是一个由这三个颜色组成的一个 list 。而 RGB 三个颜色中每个颜色值都是用 8 个比特来表示,大小范围是 0 ~ 255(2^8 - 1),就一共有 256 * 256 * 256 种颜色。并且作为一个像素类似于这样的数值:[253 255 255] 是不利于简单比较的,肉眼看着类似的颜色,但是它的三个颜色分布可能相差很多。所以将它灰度化,用 256 个不同的灰色表示现有的图片。由于现在用一种灰色表示三种颜色,原来每个像素是一个 list 现在就降维成一个数值,数值的大小还是比较容易比较的。
  3. 比较像素的灰度值
    比较图片灰度化的每行相邻像素之间的大小,每行后面像素值大于前面一个像素值那么记为1,如果不大于则记为0
  4. 计算哈希值
    根据上一步得到了由0和1构成的数组合在一起就构成了1024位的整数
  5. 对比不同图片的汉明距离
    对比两个图片生成的整数有多少位不一样。一般汉明距离小于 5 ,两张图片的相似度就很高了。

差异值哈希算法的 Python 代码

import cv2

# 差异值哈希算法
def dhash(image):
    resize_height, resized_width = 32, 33
    # 缩放到(resized_width, resize_height)尺寸的大小
    resized_img = cv2.resize(image, (resized_width, resize_height))
    # 图片灰度化
    grey_resized_img = cv2.cvtColor(resized_img, cv2.COLOR_RGB2GRAY)
    # 差异值计算
    hash_list = []
    for row in range(resize_height):
        for col in range(resized_width - 1):
            # 每行前一个颜色强度大于后一个,值为1,否则值为0
            if grey_resized_img[row, col] > grey_resized_img[row, col + 1]:
                hash_list.append('1')
            else:
                hash_list.append('0')

    return '' . join(hash_list)

# 比较汉明距离
def hamming_distance(dhash1, dhash2):
    return bin(int(dhash1, base = 2) ^ int(dhash2, base = 2)).count('1')

# 读取图片内容
img1 = cv2.imread(img1_path)
# 读取图片内容
img2 = cv2.imread(img2_path)
if hamming_distance(dhash(img1), dhash(img2)) <= 5:
    print('相似图片')

颜色直方图

由于差异值哈希失去了太多的细节,适合比较原图或者缩略图。所以我再加上颜色直方图的比较计算图片间的接近程度,用以排除部分像素的微小差异。

  1. 缩小尺寸
    一般将图片缩放为 8 * 8 的尺寸大小,共64个像素的图片。但是由于64个像素对于我来说,损失的细节太多所以我选择了缩放到 32 * 32 的尺寸大小
  2. 降低位深
    原来 RGB 每个颜色都有 256 种变化,现在做一个映射,将原来的 256 分为 8(3个比特表示) 个颜色区间。类似旧的 0 - 31 对应新的颜色 0,以达到降低计算的效果
  3. 计算像素值
    由于降低了位深,图片颜色值变小。每个颜色值不大于8(0 - 7),然后我们给三元素不同的权重,分别为 8 * 881 作为数组的 key,用以统计每个颜色的像素出现次数,并且不会出现不同颜色统计到了同一个 key 值下的目的。
  4. 计算相似度
    计算出像素值后得到,我们得到了以不同颜色的数值为 key,出现次数为 value 的数组。这时候我们可以使用用余弦相似度去计算相同颜色出现次数的相似度,越是相似的像素最后值越接近于1。截图来自于WiKi

    图片描述

颜色直方图的 Python 代码

import cv2
from math import sqrt

# 颜色映射
def bgr_mapping(img_val):
    # 将bgr颜色分成8个区间做映射
    if img_val >= 0 and img_val <= 31: return 0
    if img_val >= 32 and img_val <= 63: return 1
    if img_val >= 64 and img_val <= 95: return 2
    if img_val >= 96 and img_val <= 127: return 3
    if img_val >= 128 and img_val <= 159: return 4
    if img_val >= 160 and img_val <= 191: return 5
    if img_val >= 192 and img_val <= 223: return 6
    if img_val >= 224: return 7

# 颜色直方图的数值计算
def calc_bgr_hist(image):
    if not image.size: return False
    hist = {}
    # 缩放尺寸减小计算量
    image = cv2.resize(image, (32, 32))
    for bgr_list in image:
        for bgr in bgr_list:
            # 颜色按照顺序映射
            maped_b = bgr_mapping(bgr[0])
            maped_g = bgr_mapping(bgr[1])
            maped_r = bgr_mapping(bgr[2])
            # 计算像素值
            index   = maped_b * 8 * 8 + maped_g * 8 + maped_r
            hist[index] = hist.get(index, 0) + 1
    
    return hist

# 计算两张图片的相似度
def compare_similar_hist(h1, h2):
    if not h1 or not h2: return False
    sum1, sum2, sum_mixd = 0, 0, 0
    # 像素值key的最大数不超过512,直接循环到512,遍历取出每个像素值
    for i in range(512):
        # 计算出现相同像素值次数的平方和
        sum1 = sum1 + (h1.get(i, 0) * h1.get(i, 0))
        sum2 = sum2 + (h2.get(i, 0) * h2.get(i, 0))
        # 计算两个图片次数乘积的和
        sum_mixd = sum_mixd + (h1.get(i, 0) * h2.get(i, 0))
    # 按照余弦相似性定理计算相似度
    return sum_mixd / (sqrt(sum1) * sqrt(sum2))

# 读取图片内容
img1 = cv2.imread(img1_path)
# 读取图片内容
img2 = cv2.imread(img2_path)
if compare_similar_hist(calc_bgr_hist(img1), calc_bgr_hist(img2)) < 0.9999:
    print('相似图片')

总结

总的来说:差异值哈希算法 + 颜色直方图 解决了我的相似图片匹配问题。

参考资料

相似图片搜索的原理一
相似图片搜索的原理二

查看原文

赞 0 收藏 0 评论 0

Cindy 回答了问题 · 2019-03-22

如何将SFTP用户限制在自己的主目录下?

修改用户的根目录

关注 3 回答 2

Cindy 回答了问题 · 2018-10-31

关于金额转换

    <?php
    function part_res($float) {
        if(!is_float($float)) {
            return false;
        }
        $part_res = explode('.', number_format($float, 2));
        if(!isset($part_res[1])) {
            return $float;
        }
        return $part_res[0] . '.' . strrev($part_res[1]);
    }
    
    var_dump(part_res(1.30));

关注 3 回答 3

Cindy 赞了回答 · 2018-10-31

php 获取url上的参数

#是锚标记的开始,这个是无法发送到后端的,只能通过js来获取,然后传给PHP来处理。

关注 6 回答 5

认证与成就

  • 获得 122 次点赞
  • 获得 12 枚徽章 获得 1 枚金徽章, 获得 1 枚银徽章, 获得 10 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-11-29
个人主页被 1.6k 人浏览