python flask before_request、after_request修饰器截获所有请求

在学习着用flask开发安卓后天接口时,遇到一个需求,就是想截获所有请求,即在所有请求进入app.route装饰的函数前先被处理一次。

经过在网上查找资料后,知道了@before_request、@after_request这两个方法,示例:

@app.before_request
def before_request():
    ip = request.remote_addr
    url = request.url
    print ip,
    print url

before_request()函数被app.before_request修饰以后,每一次请求到来后,都会先进入函数before_request()中,如上代码,获取请求的ip以及url,并打印出来,执行完毕后请求才会正常进入到app.route修饰的函数中响应,如果有多个函数被app.before_request修饰了,那么这些函数会被依次执行。

app.before_request修饰器在开发中用处非常大,比如判断某个ip是否有恶意访问行为,从而进行拦截等操作。

此外同理,app.after_request修饰器是在用户请求得到函数响应后被执行,不过需要注意的是这个执行是在函数返回数据前被调用,即请求已经被app.route修饰的函数响应过了,已经形成了response,但还未返回给用户的时候,调用的。

如何使用Python和BeautifulSoup抓取网页

网络上的信息是任何人穷极一生也无法全部了解的。你需要的或许不是简单的获得信息,而是一个可以收集,整理,分析信息,并且具有拓展性的方法。

你需要网页抓取(Web scraping)技术

网页抓取可以自动提取网站上的数据信息,并把这些信息用一种容易理解的格式呈现出来。网页抓取应用广泛, 在本教程中我们将重点讲解它在金融市场领域的运用。

如果你是个投资达人,每天查找收盘价一定是个烦心事,更不用提数据来源于多个网站的时候。我们可以用代码写一个网络爬虫 (web scraper) 来帮助我们自动从网站获取股指信息,从而大大简化数据提取过程。
范例+代码:一文带你上手Python网页抓取神器BeautifulSoup库-数据分析网

未分类

我们开始吧。

我们采用Python进行网页数据抓取,并采用简单强大的BeautifulSoup 库完成分析。

  • 对于Mac用户来说, OS X操作系统已经预先安装好Python。您需要打开终端并输入python –version。您应该可以看到python的版本为2.7.x。
  • 对于Windows用户而言,请由官方网站安装Python。

下一步,我们需要利用pip命令获取BeautifulSoup 库。Pip 是Python中管理库和包的工具。

在终端中输入:

easy_install pip  
pip install BeautifulSoup4

注意:如果您不能运行上面的命令,在每行前面加上sudo 再试试。

基础知识

在学习代码之前,让我们先来了解HTML的基础知识和网页抓取的基本规则。

标签

如果你已经充分理解HTML标签,请跳过这一部分

<!DOCTYPE html>  
<html>  
    <head>
    </head>
    <body>
        <h1> First Scraping </h1>
        <p> Hello World </p>
    <body>
</html>

这就是HTML网页的基本语法。

每一对标签内包含网页的一段代码:

  • <!DOCTYPE html> : HTML 文件必须以文件类型声明开头

  • HTML文件包含在 <html> 和 <html/> 标签之间

  • 元(meta)和脚本(script)声明包含在 <head> 和 </head> 标签之间

  • 网站上可见的部分包含在 <body> 和 </body> 标签之间

  • <h1> 和 <h6> 标签之间的部分为网站标题

  • <p> 标签用于定义段落

其他有用的标签还有: <a> 是超链接的标签, <table> 是表格的标签, <tr> 是表格行的标签, <td> 是表格列的标签。

并且,HTML标签常常带有标识码(id) 或类(class)属性,标识码用来唯一的识别某个HTML标签,并且标识码的值在整个HTML文件中是唯一的。类属性可以定义同类HTML标签相同的样式。我们可以利用标识码和类来帮助我们定位想要的数据。

如果您想了解关于HTML标签,标识码和类的更多内容,请参考W3Schools 出品的教程。

网络抓取规则

  • 在您进行网络抓取时,你应该查看网站的条款和条件。请仔细阅读数据合法使用声明。通常情况下,您抓取的数据不能商用。

  • 您的程序不要过于频繁的向网站发出数据请求(也被称为垃圾请求),这种做法可能会使网站崩溃。请确保您的程序行为合理(也就是说和人类的行为相似)。对一个网站每秒进行一次数据请求是比较好的做法。

  • 网站的布局随时间不断变化,所以请您确保时常重新访问网站,如果需要的话,修改抓取代码。

查看页面

让我们以Bloomberg Quote网站的其中一页为例。

作为一个关注股票市场的投资人,我们想要从这一页得到股指名称(标准普尔500指数)和价格。首先,右键点击打开浏览器的检查器(inspector),查看网页。

未分类

请尝试把光标放在股指价格上,你应该可以看到价格周围的蓝色方块,如果你点击这个方块,就可以选中浏览器控制台相应的HTML代码。

未分类

从结果可以看出,价格信息包含在好几层HTML标签中:<div> → <div> → <div>.

同样的,如果你把光标放在名称“标准普尔指数“上,并点击,可以看到控制台里这个信息包含在标签 < iv> 及 < h1> 之内。

未分类

现在我们知道如何依靠类标签找到我们需要的数据了。

学习代码

现在我们知道所需数据的位置,我们可以开始写代码构建我们的网络爬虫了。现在请打开您的文字编辑工具!

首先我们要导入我们要用的各种库。

# 导入各种库

import urllib2

from bs4 import BeautifulSoup

然后,我们定义一个变量(quote_page)并赋值为网站的网址链接。

# 赋值网站链接

quote_page = ‘http://www.bloomberg.com/quote/SPX:IND'

接着,利用Python的urllib2库获取方才定义的网址quote_page的 HTML网页信息。

# 检索网站并获取html代码,存入变量”page”中

page = urllib2.urlopen(quote_page)

最后,我们把网页解析为 BeautifulSoup格式,以便我们用BeautifulSoup 库来分析网页。

# 用 beautifulSoup 解析HTML代码并存入变量“soup”中`

soup = BeautifulSoup(page, ‘html.parser’)

现在我们有了包含整个网页的HTML代码的变量soup。我们就从soup开始着手提取信息。

别忘了我们的数据存储在特有的层次中。BeautifulSoup库中的find()函数可以帮助我们进入不同的层次提取内容。我们需要的HTML类“名称”在整个网页中是独一无二的,因此我们可以简单的查找 < div>

# 获取“名称”类的<div>代码段落并提取相应值

name_box = soup.find(‘h1’, attrs={‘class’: ‘name’})

在我们得到标签之后,我们可以用name_box的text属性获取相应值

name = name_box.text.strip() # strip() 函数用于去除前后空格

print name

采用相似的方法,我们可以得到股指价格数据。

# 获取股指价格数据

price_box = soup.find(‘div’, attrs={‘class’:’price’})

price = price_box.text

print price

未分类

当你运行程序时,应该可以看到程序输出当前的标普500指数的价格。

导出Excel CSV格式数据

我们已经学会如何获取数据,现在来学习如何存储数据了。Excel逗号隔开的数据格式(CSV)不失为一个好选择。这样我们就可以在Excel中打开数据文件进行查看和进一步处理。

在此之前,我们需要导入Python的csv模块和datetime模块。Datetime模块用于获取数据记录时间。请将下面几行代码插入您的导入代码部分。

import csv

from datetime import datetime

在您代码的最下方,加上把数据写入CSV文件的代码。

# 以“添加”模式打开一个csv文件, 以保证文件原有信息不被覆盖

with open(‘index.csv’, ‘a’) as csv_file:

writer = csv.writer(csv_file)

writer.writerow([name, price, datetime.now()])

未分类

现在如果运行程序,您应该可以导出一个index.csv文件。您可以在Excel中打开文件,看到里面有如图所示的一行数据。

所以如果您每天都运行这个程序,您就可以很简单的获取标准普尔指数价格,不需要像之前一样在网站上翻找。

更进一步(高级用法)

多个股指

抓取一个股指信息对您来说不够,对吗?我们可以试试同时提取多个股指信息。首先,我们需要修改quote_page,把它定义为网址的数组。

quote_page = [‘http://www.bloomberg.com/quote/SPX:IND', ‘http://www.bloomberg.com/quote/CCMP:IND']

然后我们把数据提取部分代码改成一个for循环。这个循环可以一一处理网址,并以元组(tuple)类型把所有数据存入变量data.

# for 循环

data = []

for pg in quote_page:

# 检索网站并返回HTML代码,存入变量‘page’ 

page = urllib2.urlopen(pg)

# 用 beautifulSoup 解析HTML代码并存入变量 `soup`

soup = BeautifulSoup(page, ‘html.parser’)

# 获取“名称”类的<div>代码段落并提取相应值

name_box = soup.find(‘h1’, attrs={‘class’: ‘name’})

name = name_box.text.strip() # strip() is used to remove starting and trailing

# 获取股指价格数据

price_box = soup.find(‘div’, attrs={‘class’:’price’})

price = price_box.text

# 用元组类型存储数据

data.append((name, price))

并且,修改保存部分以便一行行保存数据

# 以“添加”模式打开一个csv文件, 以保证文件原有信息不被覆盖 

with open(‘index.csv’, ‘a’) as csv_file:

writer = csv.writer(csv_file)

# for 循环

for name, price in data:

writer.writerow([name, price, datetime.now()])

重新运行程序,您应该可以同时提取两个股指价格信息了!

高级抓取技术

BeautifulSoup 库使用简单,能很好的完成小量的网站抓取。但是如果您对大量的抓取信息感兴趣,您可以考虑其他方法:

  • 强大的Python数据抓取框架Scrapy。

  • 您可以试试把一些公共应用程序接口(Application programming interface, API) 整合入您的代码。这个获取数据的方法远比网页抓取高效。举个例子来说,您可以试试Facebook Graph API,这个应用程序接口可以帮助您获取脸书网站上不显示的隐藏信息。

  • 如果数据量过大,您可以考虑使用类似MySQL的数据库后端来存储数据。
    采取“别重复”方法

未分类

DRY是“别重复你做过的事”的英文简写。您可以尝试像链接中的这个人一样把日常的工作自动化。同时您还可以考虑其他有趣的项目,比如说掌握您的脸书好友的上线时间(当然在征得他们同意的情况下),或者获取某个论坛的讲座主题列表来尝试自然语言处理(这是目前人工智能的热门话题)!

原文链接:https://medium.freecodecamp.org/how-to-scrape-websites-with-python-and-beautifulsoup-5946935d93fe

使用Python简单模拟Linux系统的tree工具

Linux系统中有个tree工具可以用比较好看的形式来显示指定文件夹的目录结构。例如下图(来自于网络):

未分类

本文代码使用Python对Linux系统的tree命令简单进行了模拟,不过还不是特别像,大家可以在此基础上进行改写或者扩展。

import os
import os.path


def tree(path, depth=0):
    if depth == 0:
        print(path)
    items = os.listdir(path)
    for item in items:
        # 输出文件夹中的文件和子文件夹名
        print('|    '*depth, end='')
        print('|----', item)
        item = os.path.join(path, item)
        if os.path.isdir(item):
            # 递归遍历子目录
            tree(item, depth+1)


tree(r'g:test')

运行结果:

未分类

Python–Virtualenv简明教程

  • virtualenv是一种工具来创建独立的Python环境。

  • virtualenv通过创建独立Python开发环境的工具, 来解决依赖、版本以及间接权限
    问题. 比如一个项目依赖Django1.3 而当前全局开发环境为Django1.7, 版本跨度过大, 导致不兼容使项目无法正在运行, 使用virtualenv可以解决这些问题.

  • virtualenv创建一个拥有自己安装目录的环境, 这个环境不与其他虚拟环境共享库, 能够方便的管理python版本和管理python库

1. 安装Virtualenv

使用pip安装Virtualenv, 使用过python的都应该知道pip包管理神器吧, 即使不知道, 网站也有大把的教程, 不过推荐查看官方安装指南

$ pip install virtualenv
//或者由于权限问题使用sudo临时提升权限
$ sudo pip install virtualenv

2. virtualenv基本使用

现在开始使用virtualenv管理python环境

➜  Test git:(master) ✗ virtualenv ENV  #创建一个名为ENV的目录, 并且安装了ENV/bin/python, 创建了lib,include,bin目录,安装了pip
New python executable in 
Installing setuptools, pip...done.
➜  Test git:(master) ✗ cd ENV
➜  ENV git:(master) ✗ ll
drwxr-xr-x  14 andrew_liu  staff  476 12  8 08:49 bin
drwxr-xr-x   3 andrew_liu  staff  102 12  8 08:49 include
drwxr-xr-x   3 andrew_liu  staff  102 12  8 08:49 lib
  • lib,所有安装的python库都会放在这个目录中的lib/pythonx.x/site-packages/下
  • bin,bin/python是在当前环境是使用的python解释器

如果在命令行中运行virtualenv –system-site-packages ENV, 会继承/usr/lib/python2.7/site-packages下的所有库, 最新版本virtualenv把把访问全局site-packages作为默认行为
default behavior.

2.1 激活virtualenv

#ENV目录下使用如下命令
➜  ENV git:(master) ✗ source ./bin/activate  #激活当前virtualenv
(ENV)➜  ENV git:(master) ✗ #注意终端发生了变化
#使用pip查看当前库
(ENV)➜  ENV git:(master) ✗ pip list
pip (1.5.6)
setuptools (3.6)
wsgiref (0.1.2) #发现在只有这三个
pip freeze  #显示所有依赖
pip freeze > requirement.txt  #生成requirement.txt文件
pip install -r requirement.txt  #根据requirement.txt生成相同的环境

2.2 关闭virtualenv

使用下面命令

$ deactivate

2.3 指定python版本

可以使用-p PYTHON_EXE选项在创建虚拟环境的时候指定python版本

#创建python2.7虚拟环境
➜  Test git:(master) ✗ virtualenv -p /usr/bin/python2.7 ENV2.7
Running virtualenv with interpreter /usr/bin/python2.7
New python executable in ENV2.7/bin/python
Installing setuptools, pip...done.
#创建python3.4虚拟环境
➜  Test git:(master) ✗ virtualenv -p /usr/local/bin/python3.4 ENV3.4
Running virtualenv with interpreter /usr/local/bin/python3.4
Using base prefix '/Library/Frameworks/Python.framework/Versions/3.4'
New python executable in ENV3.4/bin/python3.4
Also creating executable in ENV3.4/bin/python
Installing setuptools, pip...done.

到此已经可以解决python版本冲突问题和python库不同版本的问题

3. 其他

3.1 生成可打包环境

某些特殊需求下,可能没有网络, 我们期望直接打包一个ENV, 可以解压后直接使用, 这时候可以使用virtualenv -relocatable指令将ENV修改为可更改位置的ENV

#对当前已经创建的虚拟环境更改为可迁移
➜  ENV3.4 git:(master) ✗ virtualenv --relocatable ./
Making script ./bin/easy_install relative
Making script ./bin/easy_install-3.4 relative
Making script ./bin/pip relative
Making script ./bin/pip3 relative
Making script ./bin/pip3.4 relative

3.2 获得帮助

$ virtualenv -h

当前的ENV都被修改为相对路径, 可以打包当前目录, 上传到其他位置使用。

这并不能使虚拟环境跨平台使用。

python3.6使用sqlalchemy读取mysql中的数据并进行多进程并发处理

1. 介绍 SQLALChemy

SQLALChemy 是一个 python 的 ORM(Object Relational Mapper) 框架,开发人员可以快速开发操作数据库的程序,
它提供完整的数据库访问层,提供高性能的数据库访问能力。
它支持 SQLite、MySQL、Postgres、Oracle 等常用的数据库访问

2. 安装 SQLAlChemy

pip install sqlalchemy

2.1 创建测试数据库

# 建立数据库
CREATE DATABASE `test` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;

2.2 用 SQLALChemy 创建数据库表

2.2.1 程序关键点

  • 创建操作数据库的 engine,使用 pymysql 库访问 mysql 数据库
  • 创建操作数据库的 session,绑定到 engine 上
  • 从 Base 继承定义 User,Article 类,对应 mapping 到数据库的 member,article 表
  • 使用 session.create_all 创建数据库表结构
  • session.add_all 新增数据到数据库
  • session.commit 提交所有变更到数据库,此时可以再数据库中查询插入的数据
  • 查询数据使用 session.query 方法,也可以在后面连接使用 filter 进行条件过滤
#!/usr/bin/env python
# coding: utf-8


from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker


# 创建数据库 engine 使用 utf8 编码
eng = create_engine('mysql+pymysql://root:1@localhost:3306/test?charset=utf8')
Base = declarative_base()

# 创建 session 类,绑定engine
Session = sessionmaker(bind=eng)
session = Session()


class User(Base):
    '''
    用户类,对应数据库的 member 表
    '''
    __tablename__ = 'member'

    # 定义表字段
    mid = Column(Integer, primary_key=True)
    nickname = Column(String(50))
    email = Column(String(128))

    def __repl__(self):
        return '<User(name={}, email={}, nickname{}>'.format(mid,
                                                             email,
                                                             nickname)


class Article(Base):
    '''
    文章类,对应数据库中的 article 表
    '''
    __tablename__ = 'article'

    # 定义表字段
    arid = Column(Integer, primary_key=True)
    tags = Column(String(128))
    description = Column(String(256))
    title = Column(String(256))


def create_table():
    '''
    创建数据库表结构,导入初始数据
    '''
    # 创建表
    Base.metadata.create_all(eng)

    # 插入数据
    session.add_all([
        User(mid=1, nickname='测试数据 test hello', email='[email protected]'),
        User(mid=2, nickname='测试数据 china hello', email='[email protected]'),
        User(mid=3, nickname='测试数据 上海 hello', email='[email protected]'),
        User(mid=4, nickname='测试数据 北京 hello', email='[email protected]'),
        User(mid=5, nickname='测试数据 上海 hello', email='[email protected]'),
        User(mid=6, nickname='测试数据 山东 hello', email='[email protected]'),
        User(mid=7, nickname='测试数据 武夷山 hello', email='[email protected]'),
        User(mid=8, nickname='测试数据 黄山 hello', email='[email protected]'),

        Article(arid=1, tags='测试数据 test hello', title='销售额度', description='测试 test ok'),
        Article(arid=2, tags='测试数据 china hello', title='成功转型', description='测试 test ok'),
        Article(arid=3, tags='测试数据 上海 hello', title='蓝蓝的天上白云飘', description='测试 test ok'),
        Article(arid=4, tags='测试数据 背景 hello', title='在水一方', description='测试 test ok'),
        Article(arid=5, tags='测试数据 上海 hello', title='晴天,阴天,雨天,大风天', description='测试 test ok'),
        Article(arid=6, tags='测试数据 山东 hello', title='每年365天,每天24小时', description='测试 test ok'),
        Article(arid=7, tags='测试数据 武夷山 hello', title='高效工作的秘密', description='测试 test ok'),
        Article(arid=8, tags='测试数据 黄山 hello', title='战狼2', description='测试 test ok'),
    ]
    )

    # 提交到数据库
    session.commit()


def modify_data():
    '''
    测试修改数据
    '''

    # 查询用户表
    users = session.query(User).all()
    print(users)

    # 查询文章表
    # articles = session.query(Article).all()
    articles = session.query(Article).filter(Article.arid==2)
    print(articles)

    # 修改文章表
    articles[0].description = '程度,修改描述可以成功'
    print(session.dirty)

    # 提交到数据库
    session.commit()


if __name__ == '__main__':
    create_engine()
    # modify_data()

3. 多进程搜索程序

3.1 程序关键点

  • 创建 DbMgr 类,封装数据库访问操作
  • 用 sqlalchemy 从数据库获取 member,article 表中的数据
  • 使用 automap 自动反射出 member,article 表对应的类
  • 创建 Searcher 类,提供进程调用函数,用来查询符合条件的结果,并且提供进程执行完的回调展示方法
  • 创建 10 个进程的进程池
  • 循环获取用户输入,创建 searcher 对象,多进程并发执行过滤
  • 多进程调用使用 python multiprocessing
#!/usr/bin/env python
# coding: utf-8


import os


from sqlalchemy import create_engine, MetaData
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.automap import automap_base
from multiprocessing import Pool


class DbMgr():
    '''
    连接数据库,从数据库获取 用户表,文章表数据
    '''

    def __init__(self):
        eng = create_engine('mysql+pymysql://root:1@localhost/test?charset=utf8')

        # 使用 automap 自动反射出 member,article 表对应的类
        meta = MetaData()
        meta.reflect(eng, only=['member', 'article'])
        Base = automap_base(metadata=meta)
        Base.prepare()

        self._Member = Base.classes.member
        self._Article = Base.classes.article

        # 获取操作数据库的 session
        Session = sessionmaker(eng, autocommit=True)
        self._ses = Session()

    def get_data(self):
        '''
        查询用户表,文章表
        '''
        self._users = self._ses.query('"user"', self._Member.mid,
                                      self._Member.email,
                                      self._Member.nickname).all()

        self._articles = self._ses.query(self._Article).all()
        self._articles = [('ar', i.arid, i.title, i.tags, i.description)
                          for i in self._articles]

        return list(self._users) + list(self._articles)


class Searcher():
    '''
    进城处理函数,查找符合条件的结果,找到后返回结果
    '''
    def __init__(self, keyword):
        self._keyword = keyword

    def run(self, data):
        '''
        查找字符串
        '''
        try:
            if self._keyword in str(data):
                return 'ret: ' + str(os.getpid()) + '->' + str(data)
            else:
                return None
        except Exception as e:
            return e

    def callback(self, data):
        '''
        全部执行完后回调函数,展示结果
        '''
        try:
            for i in data:
                if i:
                    print('match: {}'.format(i))
        except Exception as e:
            print(e)


def main():
    # 从数据库读取数据
    mgr = DbMgr()

    # 创建过滤进程池
    pool = Pool(4)

    # 创建搜索器
    while True:
        keyword = input('n输入搜索词:  ')
        if keyword == 'q':
            break

        searcher = Searcher(keyword)

        # 从数据库获取数据
        data = mgr.get_data()
        res = pool.map_async(searcher.run,
                             data,
                             10,
                             callback=searcher.callback)

        # 等待所有进程执行完成
        res.wait()
        print('all done', res.successful())


if __name__ == '__main__':
    main()

3.2 程序运行

(py36env) servadmin@debian:~/test # python mysql2es.py

输入搜索词:  test
match: ret: 10013->('user', 1, '[email protected]', '测试数据 test hello')
match: ret: 10013->('user', 2, '[email protected]', '测试数据 china hello')
match: ret: 10013->('user', 3, '[email protected]', '测试数据 上海 hello')
match: ret: 10013->('user', 4, '[email protected]', '测试数据 背景 hello')
match: ret: 10013->('user', 5, '[email protected]', '测试数据 上海 hello')
match: ret: 10013->('user', 6, '[email protected]', '测试数据 山东 hello')
match: ret: 10013->('user', 7, '[email protected]', '测试数据 武夷山 hello')
match: ret: 10013->('user', 8, '[email protected]', '测试数据 黄山 hello')
match: ret: 10013->('ar', 1, '销售额度', '测试数据 test hello', '程度,修改描述可以成功')
match: ret: 10013->('ar', 3, '蓝蓝的天上白云飘', '测试数据 上海 hello', '测试 test ok')
match: ret: 10013->('ar', 4, '在水一方', '测试数据 背景 hello', '测 test ok')
match: ret: 10013->('ar', 5, '晴天,阴天,雨天,大风天', '测试数据 上海 hello', '测试 test ok')
match: ret: 10013->('ar', 6, '每年365天,每天24小时', '测试数据 山东 hello', '测试 test ok')
match: ret: 10013->('ar', 7, '高效工作的秘密', '测试数据 武夷山 hello', '测试 test ok')
match: ret: 10013->('ar', 8, '战狼2', '测试数据 黄山 hello', '测试 test ok')
all done True

输入搜索词:  转型
match: ret: 10013->('ar', 2, '成功转型', '测试数据 china hello', '程度,修改描述可以成功')
all done True

输入搜索词:  test
match: ret: 10013->('user', 1, '[email protected]', '测试数据 test hello')
match: ret: 10013->('user', 2, '[email protected]', '测试数据 china hello')
match: ret: 10013->('user', 3, '[email protected]', '测试数据 上海 hello')
match: ret: 10013->('user', 4, '[email protected]', '测试数据 背景 hello')
match: ret: 10013->('user', 5, '[email protected]', '测试数据 上海 hello')
match: ret: 10013->('user', 6, '[email protected]', '测试数据 山东 hello')
match: ret: 10013->('user', 7, '[email protected]', '测试数据 武夷山 hello')
match: ret: 10013->('user', 8, '[email protected]', '测试数据 黄山 hello')
match: ret: 10013->('ar', 1, '销售额度', '测试数据 test hello', '程度,修改描述可以成功')
match: ret: 10013->('ar', 3, '蓝蓝的天上白云飘', '测试数据 上海 hello', '测试 test ok')
match: ret: 10013->('ar', 4, '在水一方', '测试数据 背景 hello', '测试 test ok')
match: ret: 10013->('ar', 5, '晴天,阴天,雨天,大风天', '测试数据 上海 hello', '测试 test ok')
match: ret: 10013->('ar', 6, '每年365天,每天24小时', '测试数据 山东 hello', '测试 test ok')
match: ret: 10013->('ar', 7, '高效工作的秘密', '测试数据 武夷山 hello', '测试 test ok')
match: ret: 10013->('ar', 8, '战狼2', '测试数据 黄山 hello', '测试 test ok')
all done True

输入搜索词:

linux下python守护进程编写和原理理解

编写了一个服务端程序,打开终端能直接运行程序,但是这样终端就被限制住了。如果ctrl+c退出或者关闭终端,那么服务端程序就会退出。于是就想着让这个服务端程序成为守护进程,像httpd、vsftpd、mysqld一样,一直在后端运行,不会受终端的影响。

守护进程英文为daemon,像httpd、mysqld、vsftpd最后个字母d其实就是表示daemon的意思。

守护进程的编写步骤:

  • fork子进程,而后父进程退出,此时子进程会被init进程接管。
  • 修改子进程的工作目录、创建新进程组和新会话、修改umask。
  • 子进程再次fork一个进程,这个进程可以称为孙子进程,而后子进程退出。
  • 重定向孙子进程的标准输入流、标准输出流、标准错误流到/dev/null。

完成上面的4个步骤,那么最终的孙子进程就称为守护进程。先看下代码,后面再分析下每个步骤的原因。

#!/usr/bin/env python
#coding=utf8

import os, sys, time

#产生子进程,而后父进程退出
pid = os.fork()
if pid > 0:
    sys.exit(0)

#修改子进程工作目录
os.chdir("/")
#创建新的会话,子进程成为会话的首进程
os.setsid()
#修改工作目录的umask
os.umask(0)

#创建孙子进程,而后子进程退出
pid = os.fork()
if pid > 0:
    sys.exit(0)

#重定向标准输入流、标准输出流、标准错误
sys.stdout.flush()
sys.stderr.flush()
si = file("/dev/null", 'r')
so = file("/dev/null", 'a+')
se = file("/dev/null", 'a+', 0)
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())

#孙子进程的程序内容
while True:
    time.sleep(10)
    f = open('/home/test.txt', 'a')
    f.write('hello')

上面的程序没有任何错误处理,但是不影响原理分析。如果要应用到项目里,还需完善。下面笔者谈下自己对每个步骤的理解。

1、fork子进程,父进程退出

通常,我们执行服务端程序的时候都会通过终端连接到服务器,成功连接后会加载shell环境,终端和shell都是进程,shell进程是终端进程的子进程,通过ps命令可以很容易的查看到。在这个shell环境下一开始执行的程序都是shell进程的子进程,自然会受到shell进程的影响。在程序里fork子进程后,父进程退出,对了shell进程来说,这个父进程就算执行完了,而产生的子进程会被init进程接管,从而也就脱离了终端的控制。

2、修改子进程的工作目录

子进程在创建的时候会继承父进程的工作目录,如果执行的程序是在u盘里的,就会导致u盘不能卸载。

3、创建新会话

使用setsid后,子进程就会成为新会话的首进程(session leader);子进程会成为新进程组的组长进程;子进程没有控制终端。

4、修改umask

由于umask会屏蔽权限,所以设定为0,这样可以避免读写文件时碰到权限问题。

5、fork孙子进程,子进程退出

经过上面几个步骤后,子进程会成为新的进程组老大,可以重新申请打开终端,为了避免这个问题,fork孙子进程出来。

6、重定向孙子进程的标准输入流、标准输出流、标准错误流到/dev/null

因为是守护进程,本身已经脱离了终端,那么标准输入流、标准输出流、标准错误流就没有什么意义了。所以都转向到/dev/null,就是都丢弃的意思。

Ubuntu下安装MySQL-python模块遇到的问题及解决方法

今天把工作台转移到ubuntu上面,因为之前用win开发的时候用的一直都是 sqlite,这货,个人认为 不太好用,先不说它为啥不好用,先来说说今天遇到的问题吧,Python – Flask + Mysql

Error: No module named MySQLdb

在 安装各种依赖的时候 ,出现了这么一个错误

俺 无意识的就直接 pip install MySQLdb ,但是很不幸,并没有,于是 pip install MySQL-python,

(env)xxx@xxx:~/xxx$ pip install MySQL-python
Collecting MySQL-python

  Downloading MySQL-python-1.2.5.zip (108kB)
    100% |████████████████████████████████| 112kB 15kB/s 
    Complete output from command python setup.py egg_info:
    sh: 1: mysql_config: not found
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-build-ezVWsX/MySQL-python/setup.py", line 17, in <module>
        metadata, options = get_config()
      File "setup_posix.py", line 43, in get_config
        libs = mysql_config("libs_r")
      File "setup_posix.py", line 25, in mysql_config
        raise EnvironmentError("%s not found" % (mysql_config.path,))
    EnvironmentError: mysql_config not found

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-ezVWsX/MySQL-python/

很不幸提示找不到 mysql_config这个配置文件,于是俺 find 了一下 mysql_config,看看有没有这个文件

$ sudo find / -name mysql_config
$ 

没有任何结果数据,于是问题貌似就很明显了,Google了一下,看到有人说是

使用apt-get安装的MySQL是没有mysql_config这个文件的

简单回忆了一下,好像确实是这样,于是俺 准备重新装一下sudo apt-get install libmysqld-dev:

然后再find一下,果然

$ sudo find / -name mysql_config
/usr/bin/mysql_config

然后重新pip 安装一下就ok,

看到也有很多网友用源码安装的时候也是出现这个问题,于是俺也照着做了一遍,

$ wget http://sourceforge.net/projects/mysql-python/files/mysql-python/1.2.3/MySQL-python-1.2.3.tar.gz
$ tar zxvf MySQL-python-1.2.3.tar.gz
$ cd MySQL-python-1.2.3/

再到 MySQL-python-1.2.3下面 把 setup_posix.py 和 site.cfg 两个文件里的 mysql_config 对应值修改为/usr/bin/mysql_config ,保存后,再次运行 python setup.py build 就OK 。

$ python setup.py build build
$ python setup.py install 

这样以来,MySQLdb 的问题 就完美解决了 。

python的dict和json有什么区别?

工作中和其他语言的工程师交流,合作与联调中经常会涉及到数据的传输,这个数据的传输通常为json字符串,这个json格式数据和python自身的dict数据对象非常像,所以很自然的会思考这两者究竟区别在哪里?

首先,两者不一样

区别

  • Python 的字典是一种数据结构,JSON 是一种数据格式。

json 就是一个根据某种约定格式编写的纯字符串,不具备任何数据结构的特征。而 python 的字典的字符串表现形式的规则看上去和 json 类似,但是字典本身是一个完整的数据结构,实现了一切自身该有的算法。

  • Python的字典key可以是任意可hash对象,json只能是字符串。

形式上有些相像,但JSON是纯文本的,无法直接操作。

  1. python dict 字符串用单引号,json强制规定双引号。
  2. python dict 里可以嵌套tuple,json里只有arrayjson.dumps({1:2}) 的结果是 {”1″:2}json.dumps((1,2)) 的结果是[1,2]
  3. json key name 必须是字符串, python 是hashable, {(1,2):1} 在python里是合法的,因为tuple是hashable type;{[1,2]:1} 在python里TypeError: unhashable “list”
  4. json: true false null python:,True False None
  5. python {“me”: “我”} 是合法的 json 必须是 {“me”: “u6211”}

联系

  • dict 存在于内存中,可以被序列化成 json 格式的数据(string),之后这些数据就可以传输或者存储了。

  • JSON 是一种数据传输格式。

  • 也就是说,这些字符串以 JSON 这样的格式来传输,至于你怎么 parse 这些信息,甚至是是否 parse,是否储存,都不是 JSON 的事情。

  • 用 Python 举个例子: 某段程序可以把字符串 “{A:1, B:2}” parse 成 一对 tuple( (“A”, 1), (“B”, 2) )而不是 dictionary {“A”: 1, “B”: 2}。Python 的 dictionary 是对 Hash Table 这一数据结构的一种实现。它使用其内置的哈希函数来规划键对应的内容的储存位置,从而获得 O(1) 的数据读取速度。所以 JSON 是一种数据传输格式,它能被解析成 Python 的 Dictionary 或者其他形式,但解析成什么内容是和 JSON 这种格式无关的。Python 的 Dictionary 则是 Python 对 Hash Table 的实现,一套从存储到提取都封装好了的方案。

python字符串连接示例

python中有很多字符串连接方式

  • # 最原始的字符串连接方式:str1 + str2
  • # python 新字符串连接语法:str1, str2
  • # 奇怪的字符串方式:str1 str2
  • # % 连接字符串:‘name:%s; sex: ‘ % (‘tom’, ‘male’)
  • # 字符串列表连接:str.join(some_list)

1、第一种,想必只要是有编程经验的人,估计都知道,直接用 “+” 来连接两个字符串:

'Jim' + 'Green' = 'JimGreen'

2、第二种比较特殊,如果两个字符串用“逗号”隔开,那么这两个字符串将被连接,但是,字符串之间会多出一个空格:

‘Jim’, ‘Green’ = ‘Jim Green’

3、第三种也是 python 独有的,只要把两个字符串放在一起,中间有空白或者没有空白:两个字符串自动连接为一个字符串:

'Jim''Green' = 'JimGreen'
'Jim' 'Green' = 'JimGreen'

4、第四种功能比较强大,借鉴了C语言中 printf 函数的功能,如果你有C语言基础,看下文档就知道了。这种方式用符号“%”连接一个字符串和一组变量,字符串中的特殊标记会被自动用右边变量组中的变量替换:

'%s, %s' % ('Jim', 'Green') = 'Jim, Green'

5、第五种就属于技巧了,利用字符串的函数 join 。这个函数接受一个列表,然后用字符串依次连接列表中每一个元素:

var_list = ['tom', 'david', 'john']
a = '###'
a.join(var_list) = 'tom###david###john'

6、其实,python 中还有一种字符串连接方式,不过用的不多,就是字符串乘法,如:

a = 'abc'
a * 3 = 'abcabcabc'

centos 7系统编译安装python3.5

到python官网找到下载路径, 用wget下载

wget https://www.python.org/ftp/python/3.5.1/Python-3.5.1.tgz

编译环境准备

准备一下编译环境,防止出现安装错误

[yangzai@oldsyang ~]$ yum groupinstall 'Development Tools'
[yangzai@oldsyang ~]$ yum install zlib-devel bzip2-devel openssl-devel ncurese-devel

解压tgz包和编译

#解压
[root@oldsyang mysoft]# tar -zxvf Python-3.5.1.tgz
...
#配置安装目录
[root@oldsyang mysoft]# cd Python-3.5.1
[root@oldsyang mysoft]# ./configure --prefix=/usr/local/python3
#编译并安装
[root@oldsyang mysoft]# make && make install

Python3.5.1 安装编译安装时会默认安装 pip 如果出现:

Ignoring ensurepip failure: pip 1.5.6 requires SSL/TLS

未安装编译环境,重新安装该编译环境并重新编译
Python3.5.1

[yangzai@oldsyang ~]$ yum install zlib-devel bzip2-devel openssl-devel ncurese-devel

添加全局变量

#打开配置文件
[yangzai@oldsyang ~]$ vim /etc/profile
#随便找个地址添加PATH,并保存退出
PATH=/usr/local/Python3/bin:$PATH
export PATH
#退出当前登录终端,重新进入
[yangzai@oldsyang ~]$ python3
Python 3.5.1 (default, Jun 12 2017, 20:31:31)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-11)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

更换系统默认 Python 版本

#备份旧版本 Python
mv /usr/bin/python /usr/bin/python2.7
#新建指向新版本 Python 以及 pip 的软连接
ln -s /usr/local/python3/bin/python3.5 /usr/bin/python
ln -s /usr/local/python3/bin/pip3 /usr/bin/pip
#检验 Python 及 pip 版本
python -V
pip -V

更新 yum 相关设置

因 yum 的功能依赖 Python2 更改默认 Python版本后会导致 yum无法正常工作

执行以下命令:

vi /usr/bin/yum

打开文件,修改第一行为:

#!/usr/bin/python2.7

若执行 yum 时出现以下错误:

File "/usr/libexec/urlgrabber-ext-down", line 28

执行以下更改,打开该文件并修改首行为:

#!/usr/bin/python2.7

执行 yum 时,若出现以下 Error:

Error: Delta RPMs disabled because /usr/bin/applydeltarpm not installed.

执行以下安装可解决:

yum install deltarpm