分类 Python 下的文章

[数据挖掘入门01] weibo验证码绕过

0x00 介绍

由于本人兴趣所致,一直对数据分析有极大的兴趣,所以也会进行相关方面的东西的一些尝试。

既然是数据分析第一步自然是抓取数据,既然做了自然是希望这个东西有一定的价值,所以选择了twitter和微博来练手。所以也就有了爬微博数据的需求。

后续也会将自己做的一些东西和一些思考按照进度放在博客上,做个记录。

0x01 问题

微博的站点一共有三个,分别是:

    1. weibo.cn
    1. m.weibo.cn
    1. weibo.com

页面的复杂度也是从低到高,所以为了爬取数据更为的简单,肯定是首选 weibo.cn。但是weibo有个很蛋疼的问题,就是当你要抓取一些页面的时候需要登陆,而这个登陆有个滑动验证码如图所示,本文便是解决该验证码问题的。

0x02 思路

    1. 该图形为四宫格,所以可能的组合只有 432*1 种。
    1. 可以找出这24种验证码的图片。
    1. 图片特别干净没有噪点。
    1. 将图片灰度化后与收集的24种不同的图片进行类比即可知道路线。
    1. pyppeteer 模拟鼠标滑动进行登陆。

0x03 代码

    1. 获取验证码图片
async def MainLogin():
    browser = await launch(executablePath=ChromePath, headless=False, args=['--proxy-server=socks5://127.0.0.1:1080'])
    page = await browser.newPage()
    await page.goto(WeiboLoginUri, {"waitUntil": "documentloaded"})

    # 进入页面停滞两秒,先发个呆
    time.sleep(2)
    # 填充表格
    await page.type(selector="#loginName", text="yourLoginname", options={"delay": 50})
    await page.type(selector="input#loginPassword", text="yourPassword", options={"delay": 50})

    # 选择按钮
    Button = await page.querySelector("#loginAction")
    await Button.click()
    # 等待图形验证码加载
    await page.waitForSelector("#patternCaptchaHolder > div.patt-holder-body")
    # 等待动画放映完成
    time.sleep(2.5)

    # 获取图形验证码位置坐标
    CapturePic = await page.querySelector("#patternCaptchaHolder > div.patt-holder-body > div.patt-shadow")
    CapturePicPoint = await CapturePic.boundingBox()

    # 截取图形验证码
    await page.screenshot({"path": "4.png", "clip": CapturePicPoint})
    1. 通过循环获取图片,然后提取出24种不同的图片

    1. 提取这24张图片每个像素点的灰度值。
def getIMSpy():
    ims = {}
    Path = "CapturePicAll"

    for fingerPicFile in os.listdir("CapturePicAll"):

        keyName = fingerPicFile.split('.')[0]
        PicABSPath = Path + os.sep + fingerPicFile
        openPic = Image.open(PicABSPath).convert('L')
        width, heigth = openPic.size

        ims[keyName] = []

        for i in range(width):
            rowResut = []
            for j in range(heigth):
                rowResut.append(openPic.load()[i,j])

            ims[keyName].append(rowResut)

    f = open("ims.py",'a')
    f.write("ims = ")
    f.write(str(ims))
    f.close()

这样就可以得到一个ims.py文件,里面包含每个路径图片的特征。

    1. 获取登陆验证码图片与ims.py中路径中的每个特征进行对比,可得到路径。
def AalysisPic(FilePath):
    result = ""
    SourcePic = Image.open(FilePath).convert('L')
    width, height = SourcePic.size

    for MoveWay in ims.keys():
        isGoingOn = True
        for i in range(width):
            for j in range(height):
                # 以245为临界值,大约245为空白,小于245为线条;两个像素之间的差大约10,是为了去除245边界上的误差
                if (
                        (SourcePic.load()[i, j] >= 245 and ims[MoveWay][i][j] < 245) or
                        (SourcePic.load()[i, j] < 245 and ims[MoveWay][i][j] >= 245)
                ) and \
                        abs(ims[MoveWay][i][j] - SourcePic.load()[i, j]) > 10:
                    isGoingOn = False
                    break
            if isGoingOn is False:
                result = ''
                break
            else:
                result = MoveWay
        else:
            break

    return result


async def MainLogin():
    browser = await launch(executablePath=ChromePath, headless=False, args=['--proxy-server=socks5://127.0.0.1:1080'])
    page = await browser.newPage()
    await page.goto(WeiboLoginUri, {"waitUntil": "documentloaded"})

    # 进入页面停滞两秒,先发个呆
    time.sleep(2)
    # 填充表格
    await page.type(selector="#loginName", text="yourLoginname", options={"delay": 50})
    await page.type(selector="input#loginPassword", text="yourPassword", options={"delay": 50})

    # 选择按钮
    Button = await page.querySelector("#loginAction")
    await Button.click()
    # 等待图形验证码加载
    await page.waitForSelector("#patternCaptchaHolder > div.patt-holder-body")
    # 等待动画放映完成
    time.sleep(2.5)

    # 获取图形验证码位置坐标
    CapturePic = await page.querySelector("#patternCaptchaHolder > div.patt-holder-body > div.patt-shadow")
    CapturePicPoint = await CapturePic.boundingBox()

    # 截取图形验证码
    await page.screenshot({"path": "4.png", "clip": CapturePicPoint})
    MoveWay = AalysisPic("4.png")
    print(MoveWay)
    1. 页面定位到验证码的位置,模拟滑动,定位后需要加个随机值,防止位置一直相同。需要将连线的step调高,看起来更像人手动滑动的。
import time
import random
import asyncio
from pyppeteer import launch

from PIL import Image

from ims import ims

WeiboLoginUri = "https://passport.weibo.cn/signin/login?entry=mweibo&r=https://weibo.cn"
ChromePath = "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"


def AalysisPic(FilePath):
    result = ""
    SourcePic = Image.open(FilePath).convert('L')
    width, height = SourcePic.size

    for MoveWay in ims.keys():
        isGoingOn = True
        for i in range(width):
            for j in range(height):
                # 以245为临界值,大约245为空白,小于245为线条;两个像素之间的差大约10,是为了去除245边界上的误差
                if (
                        (SourcePic.load()[i, j] >= 245 and ims[MoveWay][i][j] < 245) or
                        (SourcePic.load()[i, j] < 245 and ims[MoveWay][i][j] >= 245)
                ) and \
                        abs(ims[MoveWay][i][j] - SourcePic.load()[i, j]) > 10:
                    isGoingOn = False
                    break
            if isGoingOn is False:
                result = ''
                break
            else:
                result = MoveWay
        else:
            break

    return result


async def MainLogin():
    browser = await launch(executablePath=ChromePath, headless=False, args=['--proxy-server=socks5://127.0.0.1:1080'])
    page = await browser.newPage()
    await page.goto(WeiboLoginUri, {"waitUntil": "documentloaded"})

    # 进入页面停滞两秒,先发个呆
    time.sleep(2)
    # 填充表格
    await page.type(selector="#loginName", text="yourLoginname", options={"delay": 50})
    await page.type(selector="input#loginPassword", text="yourPassword", options={"delay": 50})

    # 选择按钮
    Button = await page.querySelector("#loginAction")
    await Button.click()
    # 等待图形验证码加载
    await page.waitForSelector("#patternCaptchaHolder > div.patt-holder-body")
    # 等待动画放映完成
    time.sleep(2.5)

    # 获取图形验证码位置坐标
    CapturePic = await page.querySelector("#patternCaptchaHolder > div.patt-holder-body > div.patt-shadow")
    CapturePicPoint = await CapturePic.boundingBox()

    # 截取图形验证码
    await page.screenshot({"path": "4.png", "clip": CapturePicPoint})
    MoveWay = AalysisPic("4.png")

    # 获取四个点的位置
    Point_1 = {"x": CapturePicPoint["x"] + 32 * 1 + random.randint(0, 40) - 20,
               "y": CapturePicPoint["y"] + 32 * 1 + random.randint(0, 40) - 20}
    Point_2 = {"x": CapturePicPoint["x"] + 32 * 4 + random.randint(0, 40) - 20,
               "y": CapturePicPoint["y"] + 32 * 1 + random.randint(0, 40) - 20}
    Point_3 = {"x": CapturePicPoint["x"] + 32 * 1 + random.randint(0, 40) - 20,
               "y": CapturePicPoint["y"] + 32 * 4 + random.randint(0, 40) - 20}
    Point_4 = {"x": CapturePicPoint["x"] + 32 * 4 + random.randint(0, 40) - 20,
               "y": CapturePicPoint["y"] + 32 * 4 + random.randint(0, 40) - 20}

    MoveWay = list(MoveWay)

    time.sleep(1)
    for PointNum in range(len(MoveWay)):
        if PointNum == 0:
            if MoveWay[PointNum] == "1":
                await page.mouse.move(Point_1['x'], Point_1['y'], {"steps": 70})
            elif MoveWay[PointNum] == "2":
                await page.mouse.move(Point_2['x'], Point_2['y'], {"steps": 70})
            elif MoveWay[PointNum] == "3":
                await page.mouse.move(Point_3['x'], Point_3['y'], {"steps": 60})
            elif MoveWay[PointNum] == "4":
                await page.mouse.move(Point_4['x'], Point_4['y'], {"steps": 70})

            await page.mouse.down()

        elif PointNum == 3:
            if MoveWay[PointNum] == "1":
                await page.mouse.move(Point_1['x'], Point_1['y'], {"steps": 70})
            elif MoveWay[PointNum] == "2":
                await page.mouse.move(Point_2['x'], Point_2['y'], {"steps": 80})
            elif MoveWay[PointNum] == "3":
                await page.mouse.move(Point_3['x'], Point_3['y'], {"steps": 70})
            elif MoveWay[PointNum] == "4":
                await page.mouse.move(Point_4['x'], Point_4['y'], {"steps": 70})

            await page.mouse.up()

        else:
            if MoveWay[PointNum] == "1":
                await page.mouse.move(Point_1['x'], Point_1['y'], {"steps": 60})
            elif MoveWay[PointNum] == "2":
                await page.mouse.move(Point_2['x'], Point_2['y'], {"steps": 60})
            elif MoveWay[PointNum] == "3":
                await page.mouse.move(Point_3['x'], Point_3['y'], {"steps": 60})
            elif MoveWay[PointNum] == "4":
                await page.mouse.move(Point_4['x'], Point_4['y'], {"steps": 60})

    time.sleep(20)

    await browser.close()


if __name__ == "__main__":
    asyncio.get_event_loop().run_until_complete(MainLogin())

0x06

下一步准备做的是设计下weibo爬虫的分布式,想每天的数据量能有2千万。当然更想拿到twitter的数据。
可能还会学习下nlp相关的知识(虽然已经在看了),数据挖掘(有一定尝试),以支撑我想完成的东西。

PPC_HCTF

HCTF的一道PPC,计算的规则很好懂。

把每个运算符都考虑成单独的,分别做先后运算,如下:

((15+3)*8)-7
(15+(3*8))-7
15+((3*8)-7)
15+(3*(8-7))
((15+3)*(8-7)) //+ - *
((15+3)*(8-7)) //- + *

最后将这些所有式子求出的值相加得到最终结果。

输入形式如下:

6[2,5,10,9,3,34]+*++-

代码需感谢小学弟和大哥的支持。

代码如下:

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
#define INF 0x3fffffff
#define maxn 1000

typedef long long LL;
const LL MOD = 1e9+7;

LL A[maxn], C[maxn][maxn];
char op[maxn];
LL dp[maxn][maxn];

int main(int nnnnn,char* args[])
{
    int n;char ch;
    A[0] = 1;
    for(int i=1; i<=maxn-10; i++)
        A[i] = (A[i-1] * i)%MOD;
        //A内存放阶乘地址
    C[0][0] = 1;//规定排列组合C00=1
    for(int i=1; i<=maxn-10; i++)
    {
        C[i][0] = 1;
        for(int j=1; j<=i; j++)
            C[i][j] = (C[i-1][j-1] + C[i-1][j])%MOD;
    }//计算出排列组合的数,供下方调用。
    stringstream ss(args[1]);
    ss >> n >> ch;
    //n为数据的个数
    {
        memset(dp, 0, sizeof(dp));
        for(int i=1; i<=n; i++)
            ss >> dp[i][i] >> ch;
            //dp[i][i]此时为其中数据,如4[3,8,11,4]*++   d[1][1]=3  d[2][2]=8  以此类推
        ss >> op+1;
        //op内储存符号地址
        for(int L=2; L <= n; L++)
        {
            for(int i=1; i+L-1 <= n; i++)
            {
                int j = i + L - 1;
                dp[i][j] = 0;
                for(int k=i; k<j; k++)
                {
                    LL t;
                    if(op[k] == '*'){
                    t = (dp[i][k]*dp[k+1][j])%MOD;
                    }
                    if(op[k] == '+'){
                    t = (dp[i][k]*A[j-k-1]+dp[k+1][j]*A[k-i])%MOD;
                    }
                    if(op[k] == '-'){
                    t = (dp[i][k]*A[j-k-1]-dp[k+1][j]*A[k-i])%MOD;
                    }
                    dp[i][j] = (dp[i][j]+t*C[j-i-1][k-i])%MOD;
                }
            }
        }
        printf("%lld\n", (dp[1][n]+MOD)%MOD );
    }
    return 0;
}

这段代码很有意思,所以总结学习一下。

原本按照计算规则老老实实的写代码的话需要计算的次数为n(n为数据的个数)的阶乘次。那么当计算数据较多的时候将会需要很多的时间。不能够符合题目的要求。

但是这段代码巧妙的减少了时间复杂度。

下面是总结:

-------------------
|        |        |
-------------------
| n个符号| m个符号 |    
0        k        p

如上图所示,我们假设有一个式子,在其中随机选取一个符号,作为K。K的左边有n个符号,k的右边有m个符号。

现在假设:

n=2
m=3

那么:

n将有两种情况,这两种情况求出的值我们分别记为a,b。
m将有六种情况,这六种情况我们分别记为A,B,C,D,E,F.

当k为+或者-时,最后的结果为:

     |-A  =a1                       |-A =b1
     |-B  =a2                       |-B =b2
     |-C  =a3                       |-C =b3
    a(+-)|-D  =a4                  b(+-)|-D =b4 
     |-E  =a5                       |-E =b5
     |-F  =a6                       |-F =b6

最终的结果为:

sum = a1+a2+a3+a4+a5+a6+b1+b2+b3+b4+b5+b6
等同于
sum = (a+A)+(a+B)+...+(a+F)+(b+A)+(b+B)+...(b+F)

那么里面一共有个a和b在最后处理的时候相加了

也就是

sum = a*A[3](+-)(A+B+C+D+E+F)+b*A[3](+-)(A+B+C+D+E+F)    

这也就意味着,k的左边也就是n这一边最后处理的数据为:

(a+b)*A[m]   

也就是左边按照规则处理完以后的数据乘以右边数据数量的阶乘,以此类推右边也是这个样子。

那么最后的结果就是:

dp[n]*A[m](+-)dp[m]*A[n]
dp[n]为左边n个符号处理以后的结果,dp[m]为右边m个符号处理以后的结果.A为阶乘

当k为*时

sum = a1*a2*a3*a4*a5*a6*b1*b2*b3*b4*b5*b6
等同于:
sum = a*(A+B+C+D+E+F)+b*(A+B+C+D+E+F)

也就没有阶乘了。

好,思想讲完了,现在来分析代码。

输入啥的就不说了,注释里面已经有了,直接分析。

L表示的是当前循环计算的数据的个数,例如当L=2时,如下:

(15*8)+11+4 -> 15*(8+11)+4 -> 15*8+(11+4)

就只计算括号中的数据,并将计算结果储存在d[i][j]中

dp[i][j]表示的是从第i个数据计算到第j个数据的结果,如

dp[1][2]=15*8    dp[2][3]=8+11

dp[1][2]中1指的是第一个数据15,2指的是第二个数据8

当L=3时,如下:

|15*8+11|+4 -> 15*|8+11+4|

在计算 **158+11* 的时候,他不会再重新计算 15 x 88+11 会直接调用上次循环的结果dp[1][2]和dp[2][3]进行计算,最后得出来整个式子的结果。这个式子是第一个数据到第三个数据,那么就用dp[1][3]储存。以此类推,当处理四个数据时,调用前三个数据的结果。依次递归,所以空间需要很大,但是会大大的节约运算的时间。

最后考虑排列组合的问题。

dp[i][j] = (dp[i][j] + t * C[j-i-1][k-i])%MOD; //t是处理完当前K的值以后的结果

那么为什么要乘以C[j-i-1][k-i])呢。

还是那个例子:

15*8+11+4   

现在计算整个式子的值,当K在第二个+的地方。

处理完(15*8)+(11+4)以后的值为t,但是这个t只是一种情况。什么意思呢?

(15*8)+(11+4)

这个t的值是这个式子的值,没有错,但是它并没有考虑是先算乘号还是先算加号。

所以就需要考虑先算(15*8)还是先算(11+4)。现在我们只考虑符号。

这个式子一共有三个符号* + +,去除第二个加号已经是最低优先级运算符,已经确定是最后运算,所以不考虑。所以只有两个符号需要考虑。

这两个运算符需要考虑先后顺序,而且顺序又不能重叠,所以类似于排列组合。

如:

|A |B |
-------

现在有A,B两个位置可以填运算符。

K为已经确定为最低优先运算级的第二个加号,K的左边有一个运算符*,K的右边有一个运算符+,那么可以在A中填 * 号,那么B中只能够填+号了。当在A中填+号,那么B中也只能填 * 号。

所以填进去的方式有种。

好,现在假设有五个符号

 * + - + *

把K设置在- 号的位置。K的左边和K的右边计算结果已经计算出为t,即t为:

(data1 * data2 + data3 ) - (data4 + dadta5 *data6)

上述式子中在左边和右边分开考虑时候的一种情况。

那么当左右一起考虑的时候一共有几种情况呢?

去掉中间的K指定的符号还有四个符号,即还可以填四个符号,如下:

|A |B |C |D |
-------------

现把这四个符号用1,2,3,4表示:

*  +  +  *
1  2  3  4

那么因为K的左边和右边分开考虑计算(这一点已经在上面说啦~),那么1,2和3,4的先后顺序互不影响。也就是把1放进A把2放进D是一样的,因为他们中间的-号是最低优先级。

举个例子:

现在确定优先级为 **1 3 2 4 **,这个时候的式子可以看做:

((data1 * data2) + data3)  - ((data4 + dadta5) *data6)

在确定优先级为 ** 1 4 2 3 **,这个时候式子可以看做:

((data1 * data2) + data3)  - (data4 + (dadta5 *data6))

假如这四个式子按照这个顺序排,则一共有种组合,而且每种组合都在K的两边进行和K没有关系。

但是已经说了:

t = k的左边所有情况求出的最后的值 - k的右边所有情况求出的值

那么k的左边的所有情况,也就是 data1 x data2 + data3 的所有情况,包括:

((data1 * data2) + data3)
(data1 * (data2 + data3))

也就是种情况的和。

当时在四个符号排列的时候,计算K的左边和右边的值并不是左边或者右边计算出来的最后的值。只是左边或者右边所有情况中的一种情况。

所以最后的四个符号再加上K所指定的符号 - 一共五个符号排列计算最后的值的所有情况应该为种.

情况数再乘以t就是最后的值。

代入上述代码中,即一共就j-i-1个符号需要排列。k左边的符号有k-i个。

也就是

最后利用递归依次求出解并放置在dp数组中调用,直到L为n,循环结束输出最后一组值就是结果。

以上,欢迎指正教育 :)

urllib and urllib2

尽管现在Requests库十分好用,但是我还是觉的urllib和urllib2两个库肯定有其可取之处,所以专门学习记录一下。

为了学习python的两个模块可以搭建本地环境。我构建的如下:

python_test.php
<?php
    echo "You have send a request! <br>"
    if(isset($_POST['aa']))
    {
        $name = $_POST['aa'];
        echo $name;
    }
    if(isset($_GET['cc']))
    {
        $name = $_GET['cc'];
        echo $name;
    }
?>

发送请求后可以直接response.read(),判断是否发送成功,很方便。

urllib中的方法

urlopen(url[,data[,proxies]])
打开一个url的方法,返回一个文件对象,然后可以进行类似文件对象的操作。

url为发送请求的url。

data为POST的数据。

proxies为代理。

import urllib

req = urllib.urlopen("http://localhost/python_test.php")
data = req.read()
print data

执行可以看到

dikis@dikis-boom ~/code> python url.py

You have send a request!</br>

其他功能:

import urllib

######发送get请求#####

pragma = {'bb':'just for test!'}
pragma = urllib.urlencode(pragma)
req = urllib.urlopen("http://localhost/python_test.php?%s") %pragma
data = req.read()
print data

######发送POST请求####

pragma = {'aa':'just for test!'}
pragma = urllib.urlencode(pragma)
req = urllib.urlopen("http://localhost/python_test.php",pragma)
data = req.read()
print data

urlopen返回对象提供方法:

1.read() , readline() ,readlines() , fileno() , close() :这些方法的使用方式与文件对象完全一样.

2.info():返回一个httplib.HTTPMessage对象,表示远程服务器返回的头信息.

3.geturl():返回请求的url.

urllib.urlretrieve(url[, filename[, reporthook[, data]]]):

urlretrieve方法直接将远程数据下载到本地。

参数filename指定了保存到本地的路径(如果未指定该参数,urllib会生成一个临时文件来保存数据)。

数reporthook是一个回调函数,当连接上服务器、以及相应的数据块传输完毕的时候会触发该回调。

参数data指post到服务器的数据。

该方法返回一个包含两个元素的元组(filename, headers),filename表示保存到本地的路径,header表示服务器的响应头。

测试代码:

import urllib

def cbk(a, b, c):
‘’‘ 
回调函数
a: 已经下载的数据块
b: 数据块的大小
c: 远程文件的大小
'''
per = 100.0 * a * b / c
if per > 100:
    per = 100
print '%.2f %%' % per

url = 'http://localhost/python_test.php'
local = '/tmp/test.html'
testfile = urllib.urlretrieve(url, local, cbk)
print testfile[0]
print testfile[1]

执行结果:

dikis@dikis-boom ~/code> python url.py 
0.00 %
100.00 %
/tmp/test.html
Date: Wed, 18 Nov 2015 00:25:47 GMT
Server: Apache/2.4.7 (Ubuntu)
X-Powered-By: PHP/5.5.9-1ubuntu4.14
Content-Length: 31
Connection: close
Content-Type: text/html

该功能在写爬虫爬取一些网页(如:乌云知识库)到本地的时候很有用。

urllib.urlcleanup()

清除由于urllib.urlretrieve()所产生的缓存。

urllib.quote(url)和urllib.quote_plus(url)

将url数据获取之后,并将其编码,从而适用与URL字符串中,使其能被打印和被web服务器接受。

>>> urllib.quote('http://www.baidu.com')
'http%3A//www.baidu.com'
>>> urllib.quote_plus('http://www.baidu.com')
'http%3A%2F%2Fwww.baidu.com'

urllib.urlencode(query[, doseq])

将dict或者包含两个元素的元组列表转换成url参数。例如 字典{‘name’: ‘dark-bull’, ‘age’: 200}将被转换为”name=dark-bull&age=200。

urllib2_mode

urllib2.request and urllib2.addheader

urllib2模块和urllib模块相比增加了在请求中增加请求头的功能,示例代码如下:

import urllib
import urllib2
url = "http://baidu.com"
headers ={"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:42.0) Gecko/20100101 Firefox/42.0"}
data = {"username":"aaa","password":"bbb"}
data = urllib.urlencode(data)
req = urllib2.Request(url,data,headers)
response = urllib2.urlopen(req)

不难看出其中调用urllib模块的目的是为了将传递的参数进行urlcode。urllib2也可以发送post包和get包,使用方式已经在上述示例中写出。

还有一些没有总结.... 剩下的等我期末考完放假在更新吧...

Python 文本处理学习

因为最近开始写的比较多,但是前面的文本处理没有仔细看,导致了很多问题,所以这次专门整理了一下。

list方法可以将字符串中的字符依次进行处理

list(‘abdc') # ['a','b','c','d']

join方法可以将单个字符串按照需求串联起来

li=['a','b','c','d']

''.join(li) # abcd

'_'.join(li) #a_b_c_d

map方法 可以将列表中的数据每个都按照前面的方法执行

map(add,li)

ASCII 和 字符的转换

ord('a') #97

chr(97) # a

unicode码

unichr(8224)

u'\u2020'

repr() >>> repr('a') # "'a'"

ljust,rjust,center 靠齐左边,右边,居中

lstrip(),rstrip(),strip() 消除空格

附re模块详细教程一篇:

http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html