python模拟网页上传文件

在开发过程中经常有需要上传文件,而python上传文件官方并不直接支持,网上的大部分方案都是用python poster库来支持文件上传,用这种方式上传文件确实也特别的简单。

poster用法请参考这里:http://atlee.ca/software/poster/

今天我要介绍的是模拟网页来上传文件,这样不需要依赖第三方库,使用和传播都更加的方便。

既然是模拟网页提交,我们先来看看网页上传文件到底上传了什么内容?

这是请求的header,我们主要关注的是Content-Length、Content-Type、和提交的数据,如果上传文件涉及到身份的验证,可能还需要关注Cookie

这是提交的内容,总共提交了四个字段

  • app   字符串  值为dlife
  • platform 字符串  值为android
  • type  字符串   值为package
  • file 文件 文件流

可以看到一些规律,字符型数据都是如下的内容

------WebKitFormBoundarywxxHf5sLp9I0dQCs Content-Disposition: form-data; name="app" dlife

总共分为4行

第一行 ——WebKitFormBoundarywxxHf5sLp9I0dQCs  boundary值,和Content-Type里面boundary一致,boundary是内容之间的分隔符

第二行 Content-Disposition: form-data; name=”app”   数据类型和字段名称

第三行 为空(\r\n)

第四行 dlife 是字段app的值

如果内容是文件的话还多了一行Content-Type: application/octet-stream ,就是文件类型,比如android apk文件类型是application/vnd.android.package-archive,这个可以用python mimetypes.guess_type(file)方法获取文件类型

代码如下:

组合请求参数

def get_request_param(params, boundary):  
    """
    获取请求的参数
    args:
      params: 请求的参数
      boundary: 上传的boundary
    return:
      string: 请求参数
    """
    body = ''
    if len(params) > 0:
        CRLF = '\r\n'
        for key in params.keys():
            value = params[key]
            body += '--' + boundary + CRLF
            if os.path.exists(value):
                file_type = mimetypes.guess_type(value)[0]
                file_name = os.path.basename(value)
                if file_type is None:
                    file_type = "text/plain; charset=utf-8"
                body += 'Content-Disposition: form-data; name="' \
                        + key + '"; filename="' + file_name + '"' + CRLF
                body += "Content-Type: " + file_type + CRLF
                body += CRLF
                body += open(value, 'rb').read() + CRLF
            else:
                body += 'Content-Disposition: form-data; name="' + key + '"' + CRLF
                body += CRLF
                body += value + CRLF
        body += "--" + boundary + "--" + CRLF
    return body

上传文件

import urllib  
import urllib2  
import cookielib  
import json  
import sys  
import os  
import uuid  
import mimetypes  
upload_url = host + 'admin/app-package'  
boundary = '--------ksc' + uuid.uuid4().hex  
param = {  
    'file': apk_url,
    'app': app,
    'platform': platform,
    'type': 'package'
}
request_data = get_request_param(param, boundary)  
req = urllib2.Request(upload_url)  
req.add_header("User-Agent", 'ksc')  
req.add_header("Content-Type", "multipart/form-data, boundary=" + boundary)  
res = urllib2.urlopen(req, request_data)  
content = res.read().decode('utf-8')  

参考:http://blog.geekli.cn/archives/129