Existen excelentes herramientas en la biblioteca estándar para analizar los encabezados RFC 821 y también para analizar todas las solicitudes HTTP. He aquí un ejemplo de cadena pedido (tenga en cuenta que Python lo trata como una cadena grande, a pesar de que estamos rompiendo a través de varias líneas para facilitar la lectura) que podemos alimentar a mis ejemplos:
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'
)
Como @TryPyPy señala, puede utilizar mimetools.Message
analizar los encabezados - aunque hay que añadir que las resultantes Message
objeto actúa como un diccionario de cabeceras vez que haya terminado de crearlo:
# 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"
Pero esto, por supuesto, ignora la línea de petición, o te hace analizarlo tú mismo. Resulta que hay una solución mucho mejor.
La biblioteca estándar analizará HTTP por usted si usa su BaseHTTPRequestHandler
. Aunque su documentación es un poco oscura, un problema con todo el conjunto de herramientas HTTP y URL en la Biblioteca estándar, todo lo que tiene que hacer para analizar una cadena es (a) ajustar la cadena en un StringIO()
, (b) leer el raw_requestline
para que esté listo para ser analizado, y (c) capture cualquier código de error que ocurra durante el análisis en lugar de dejar que intente escribirlos de nuevo en el cliente (¡ya que no tenemos uno!).
Así que aquí es nuestra especialización de la clase Biblioteca Estándar:
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
Una vez más, deseo que la gente de la biblioteca estándar se habían dado cuenta de que el análisis de HTTP debe ser roto a cabo de una manera que no nos requiere para escribir nueve líneas de código para llamarlo correctamente, pero ¿qué puedes hacer? Aquí es cómo se utiliza esta clase simple:
# 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"
Si hay un error durante el análisis, la error_code
no habrá 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')"
Yo prefiero usar la biblioteca estándar como éste porque sospecho que ya han encontrado y resuelto cualquier caso límite que pueda afectarme si trato de volver a implementar una especificación de Internet con expresiones regulares.
¿Hay una manera de hacer esto en python3? – Broseph
mimetools está en desuso desde 2.3 –
@Broseph Vea la respuesta de Gowtham. – JeromeJ