このサイトcsv
にファイルをアップロードしようとしています。しかし、私はいくつかの問題に遭遇しました。それは間違った(多分) から生じていると思います。mimetype
を介して手動でファイルを投稿しようとしているurllib2
ので、コードは次のようになります。
import urllib
import urllib2
import mimetools, mimetypes
import os, stat
from cStringIO import StringIO
#============================
# Note: I found this recipe online. I can't remember where exactly though..
#=============================
class Callable:
def __init__(self, anycallable):
self.__call__ = anycallable
# Controls how sequences are uncoded. If true, elements may be given multiple values by
# assigning a sequence.
doseq = 1
class MultipartPostHandler(urllib2.BaseHandler):
handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first
def http_request(self, request):
data = request.get_data()
if data is not None and type(data) != str:
v_files = []
v_vars = []
try:
for(key, value) in data.items():
if type(value) == file:
v_files.append((key, value))
else:
v_vars.append((key, value))
except TypeError:
systype, value, traceback = sys.exc_info()
raise TypeError, "not a valid non-string sequence or mapping object", traceback
if len(v_files) == 0:
data = urllib.urlencode(v_vars, doseq)
else:
boundary, data = self.multipart_encode(v_vars, v_files)
contenttype = 'multipart/form-data; boundary=%s' % boundary
if(request.has_header('Content-Type')
and request.get_header('Content-Type').find('multipart/form-data') != 0):
print "Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data')
request.add_unredirected_header('Content-Type', contenttype)
request.add_data(data)
return request
def multipart_encode(vars, files, boundary = None, buf = None):
if boundary is None:
boundary = mimetools.choose_boundary()
if buf is None:
buf = StringIO()
for(key, value) in vars:
buf.write('--%s\r\n' % boundary)
buf.write('Content-Disposition: form-data; name="%s"' % key)
buf.write('\r\n\r\n' + value + '\r\n')
for(key, fd) in files:
file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
filename = fd.name.split('/')[-1]
contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
buf.write('--%s\r\n' % boundary)
buf.write('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename))
buf.write('Content-Type: %s\r\n' % contenttype)
# buffer += 'Content-Length: %s\r\n' % file_size
fd.seek(0)
buf.write('\r\n' + fd.read() + '\r\n')
buf.write('--' + boundary + '--\r\n\r\n')
buf = buf.getvalue()
return boundary, buf
multipart_encode = Callable(multipart_encode)
https_request = http_request
import cookielib
cookies = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies),
MultipartPostHandler)
opener.addheaders = [(
'User-agent',
'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6'
)]
params = {"FILENAME" : open("weather_scrape.csv", 'rb'),
'CGIREF' : '/calludt.cgi/DDFILE1',
'USE':'MODEL',
'MODEL':'CM',
'CROP':'APPLES',
'METHOD': 'SS',
'UNITS' : 'E',
'LOWTHRESHOLD': '50',
'UPTHRESHOLD': '88',
'CUTOFF':'H',
'COUNTY':'AL',
'ACTIVE':'Y',
'FROMMONTH':'3',
'FROMDAY':'15',
'FROMYEAR': '2013',
'THRUMONTH':'5',
'THRUDAY':'13',
'THRUYEAR':'2013',
'DATASOURCE' : 'FILE'
}
response = opener.open("http://www.ipm.ucdavis.edu/WEATHER/textupload.cgi", params)
これを投稿すると、最初POST
に返された次の Web ページで送信ボタンをクリックするまで、すべて問題ないようです。次に、次のエラー メッセージが表示されます。
ERROR (bad data) in file 'weather.csv' at line 135.
Data record = [--192.168.117.2.1.4404.1368589639.796.1--]
Too few values found. Check delimiter specification.
ここで、ブラウザでアクションを実行するときに行われる投稿リクエストを調査すると、次のようにcontent-type
非常に具体的であることがわかりました。
------WebKitFormBoundaryfBp6Jfhv7LlPZLKd
Content-Disposition: form-data; name="FILENAME"; filename="weather.csv"
Content-Type: application/vnd.ms-excel
コンテンツタイプがエラーの原因であるかどうかは完全にはわかりませんが、..それは現在除外しているものです(実際に何がうまくいかないのかわからないため)。 urllib2を介してコンテンツタイプを設定するので、グーグルで調べた後、偶然見つけましたurllib3.
Urllib3
ファイル投稿機能が組み込まれていますが、その使用方法が完全にはわかりません。
Filepost
urllib3.filepost.encode_multipart_formdata(fields, boundary=None)
Encode a dictionary of fields using the multipart/form-data MIME format.
Parameters:
fields –
Dictionary of fields or list of (key, value) or (key, value, MIME type) field tuples. The key is treated as the field name, and the value as the body of the form-data bytes. If the value is a tuple of two elements, then the first element is treated as the filename of the form-data section and a suitable MIME type is guessed based on the filename. If the value is a tuple of three elements, then the third element is treated as an explicit MIME type of the form-data section.
Field names and filenames must be unicode.
boundary – If not specified, then a random boundary will be generated using mimetools.choose_boundary().
urllib3.filepost.iter_fields(fields)
Iterate over fields.
Supports list of (k, v) tuples and dicts.
このライブラリを使用して、ドキュメントの記述として値をエンコードしようとしましたが、エラーが発生しています。
私は最初に、物事をテストするために、dict
.
params = {"FILENAME" : open("weather.csv", 'rb'),
'CGIREF' : '/calludt.cgi/DDFILE1',
'USE':'MODEL',
'MODEL':'CM',
'CROP':'APPLES',
'METHOD': 'SS',
'UNITS' : 'E',
'LOWTHRESHOLD': '50',
'UPTHRESHOLD': '88',
'CUTOFF':'H',
'COUNTY':'AL',
'ACTIVE':'Y',
'FROMMONTH':'3',
'FROMDAY':'15',
'FROMYEAR': '2013',
'THRUMONTH':'5',
'THRUDAY':'13',
'THRUYEAR':'2013',
'DATASOURCE' : 'FILE'
}
values = urllib3.filepost.encode_multipart_formdata(params)
ただし、これにより次のエラーが発生します。
values = urllib3.filepost.encode_multipart_formdata(params)
File "c:\python27\lib\site-packages\urllib3-dev-py2.7.egg\urllib3\filepost.py", line 90, in encode_multipart_formdata
body.write(data)
TypeError: 'file' does not have the buffer interface
何が原因なのかわからないので、タプル (キー、値、MIME タイプ) のリストを渡そうとしましたが、それもエラーをスローします:
params = [
("FILENAME" , open("weather_scrape.csv"), 'application/vnd.ms-excel'),
('CGIREF' , '/calludt.cgi/DDFILE1'),
('USE','MODEL'),
('MODEL','CM'),
('CROP','APPLES'),
('METHOD', 'SS'),
('UNITS' , 'E'),
('LOWTHRESHOLD', '50'),
('UPTHRESHOLD', '88'),
('CUTOFF','H'),
('COUNTY','AL'),
('ACTIVE','Y'),
('FROMMONTH','3'),
('FROMDAY','15'),
('FROMYEAR', '2013'),
('THRUMONTH','5'),
('THRUDAY','13'),
('THRUYEAR','2013'),
('DATASOURCE' , 'FILE)')
]
values = urllib3.filepost.encode_multipart_formdata(params)
>>ValueError: too many values to unpack