parsing原始HTTP头

我有一个原始的HTTPstring,我想代表对象中的字段。 有什么办法来parsingHTTPstring中的单个头?

'GET /search?sourceid=chrome&ie=UTF-8&q=ergterst HTTP/1.1\r\nHost: www.google.com\r\nConnection: keep-alive\r\nAccept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\nUser-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.45 Safari/534.13\r\nAccept-Encoding: gzip,deflate,sdch\r\nAvail-Dictionary: GeNLY2f-\r\nAccept-Language: en-US,en;q=0.8\r\n [...]' 

标准库中有很好的工具可以parsingRFC 821头文件,也可以parsing整个HTTP请求。 下面是一个示例请求string(请注意,Python将其视为一个大string,即使我们正在将其分成几行以供阅读),我们可以将其提供给我的示例:

 request_text = ( 'GET /who/ken/trust.html HTTP/1.1\r\n' 'Host: cm.bell-labs.com\r\n' 'Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n' 'Accept: text/html;q=0.9,text/plain\r\n' '\r\n' ) 

正如@TryPyPy所指出的那样,你可以使用mimetools.Message来parsing头文件 – 尽pipe我们应该补充说,一旦你完成了这个头文件的生成, Message对象的行为就像是一个头字典:

 # Ignore the request line and parse only the headers from mimetools import Message from StringIO import StringIO request_line, headers_alone = request_text.split('\r\n', 1) headers = Message(StringIO(headers_alone)) print len(headers) # -> "3" print headers.keys() # -> ['accept-charset', 'host', 'accept'] print headers['Host'] # -> "cm.bell-labs.com" 

但是,这当然会忽略请求行,或者让你自己parsing它。 事实certificate,有一个更好的解决scheme。

如果您使用BaseHTTPRequestHandler则标准库将为您parsingHTTP。 尽pipe它的文档有些模糊 – 这是标准库中整个HTTP和URL工具的一个问题 – 你只需要做一个stringparsing就可以:(a)将你的string包装在一个StringIO() ,(b )读取raw_requestline以便它可以被parsing,并且(c)捕获parsing过程中发生的任何错误代码,而不是让它把它们写回客户端(因为我们没有!)。

所以这里是我们的标准库类的专业化:

 from BaseHTTPServer import BaseHTTPRequestHandler from StringIO import StringIO class HTTPRequest(BaseHTTPRequestHandler): def __init__(self, request_text): self.rfile = StringIO(request_text) self.raw_requestline = self.rfile.readline() self.error_code = self.error_message = None self.parse_request() def send_error(self, code, message): self.error_code = code self.error_message = message 

再次,我希望标准库人员已经意识到,HTTPparsing应该以一种不需要我们写九行代码来正确调用它的方式来分解,但是你能做什么? 下面是你将如何使用这个简单的类:

 # Using this new class is really easy! request = HTTPRequest(request_text) print request.error_code # None (check this first) print request.command # "GET" print request.path # "/who/ken/trust.html" print request.request_version # "HTTP/1.1" print len(request.headers) # 3 print request.headers.keys() # ['accept-charset', 'host', 'accept'] print request.headers['host'] # "cm.bell-labs.com" 

如果parsing过程中出现错误, error_code将不会为None

 # Parsing can result in an error code and message request = HTTPRequest('GET\r\nHeader: Value\r\n\r\n') print request.error_code # 400 print request.error_message # "Bad request syntax ('GET')" 

我更喜欢使用这样的标准库,因为我怀疑他们已经遇到并解决了任何可能咬我的边界情况,如果我尝试用正则expression式自己重新实现Internet规范。

这似乎工作正常,如果你去掉GET线:

 import mimetools from StringIO import StringIO he = "Host: www.google.com\r\nConnection: keep-alive\r\nAccept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\nUser-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.45 Safari/534.13\r\nAccept-Encoding: gzip,deflate,sdch\r\nAvail-Dictionary: GeNLY2f-\r\nAccept-Language: en-US,en;q=0.8\r\n" m = mimetools.Message(StringIO(he)) print m.headers 

parsing您的示例并从第一行添加信息到对象的方法是:

 import mimetools from StringIO import StringIO he = 'GET /search?sourceid=chrome&ie=UTF-8&q=ergterst HTTP/1.1\r\nHost: www.google.com\r\nConnection: keep-alive\r\n' # Pop the first line for further processing request, he = he.split('\r\n', 1) # Get the headers m = mimetools.Message(StringIO(he)) # Add request information m.dict['method'], m.dict['path'], m.dict['http-version'] = request.split() print m['method'], m['path'], m['http-version'] print m['Connection'] print m.headers print m.dict 

从Python 2.3开始, mimetools已经被弃用,并且完全从Python 3( 链接 )中删除。

这里是你应该怎么做在Python 3中:

 import email import io import pprint # […] request_line, headers_alone = request_text.split('\r\n', 1) message = email.message_from_file(io.StringIO(headers_alone)) headers = dict(message.items()) pprint.pprint(headers, width=160)