В стандартной библиотеке есть отличные инструменты как для анализа заголовков RFC 821, так и для анализа целых HTTP-запросов.Вот пример строки запроса (обратите внимание, что Python рассматривает ее как одну большую строку, хотя мы разбиваем ее на несколько строк для удобства чтения), которую мы можем передать моим примерам:
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
для разбора заголовков - хотя мы должны добавить, что результирующий объект 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"
Но это, изКонечно, игнорирует строку запроса или заставляет вас разобрать ее самостоятельно.Оказывается, что есть гораздо лучшее решение.
Стандартная библиотека проанализирует HTTP для вас, если вы используете его BaseHTTPRequestHandler
.Хотя его документация немного неясна - проблема со всем набором инструментов HTTP и URL в Стандартной библиотеке - все, что вам нужно сделать, чтобы разобрать строку, - это (a) обернуть вашу строку в StringIO()
, (b) прочитайте raw_requestline
, чтобы он был готов к анализу, и (c) перехватите любые коды ошибок, возникающие во время синтаксического анализа, вместо того, чтобы позволить ему попытаться записать их обратно клиенту (поскольку у нас его нет!).
Итак, вот наша специализация класса Стандартной библиотеки:
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
Опять же, я бы хотел, чтобы люди из Стандартной библиотеки поняли, что разбор HTTP должен быть разорван таким образом, который не требует от наснаписать девять строк кода, чтобы правильно его назвать, но что вы можете сделать?Вот как бы вы использовали этот простой класс:
# 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"
Если при синтаксическом анализе возникнет ошибка, 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')"
Я предпочитаю использоватьСтандартная библиотека, как это, потому что я подозреваю, что они уже сталкивались и решали любые крайние случаи, которые могут укусить меня, если я попытаюсь повторно реализовать спецификацию Интернета с помощью регулярных выражений.