2011-01-13 17 views
26

Mam ciąg nieprzetworzonego protokołu HTTP i chciałbym reprezentować pola w obiekcie. Czy istnieje sposób analizowania poszczególnych nagłówków z ciągu HTTP?Parsowanie nieprzetworzonych nagłówków HTTP

'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 
[...]' 

Odpowiedz

7

To wydaje się działać prawidłowo, jeśli pozbawić GET wiersz:

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 

Droga do analizowania swój przykład i dodawać informacje z pierwszej linii do obiektu będzie:

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 
+0

Czy istnieje sposób, aby zrobić to w python3? – Broseph

+0

mimetools jest przestarzałe od 2.3 –

+0

@Broseph Patrz odpowiedź Gowthama. – JeromeJ

72

Istnieją doskonałe narzędzia w Bibliotece standardowej zarówno do analizowania nagłówków RFC 821, jak i do analizowania całych żądań HTTP. Oto ciąg przykład żądanie (zauważ, że Python traktuje go jako jeden wielki ciąg, choć jesteśmy zerwania go w kilku liniach, dla czytelności), które możemy nakarmić moich przykładów:

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' 
    ) 

Jak @TryPyPy zwraca uwagę, można użyć mimetools.Message do analizowania nagłówków - choć trzeba dodać, że powstałe Message obiekt działa jak słownika nagłówkami po zakończeniu jej tworzenia:

# 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" 

Ale to, oczywiście, ignoruje linię żądanie lub sprawia, że ​​sam je analizujesz. Okazuje się, że istnieje o wiele lepsze rozwiązanie.

Biblioteka standardowa przeanalizuje dla Ciebie HTTP, jeśli używasz jego BaseHTTPRequestHandler. Chociaż jego dokumentacja jest nieco niejasna - problem z całym pakietem narzędzi HTTP i adresów URL w Bibliotece standardowej - wszystko, co musisz zrobić, aby sparsować ciąg, to (a) zawinięcie łańcucha znaków w StringIO(), (b) raw_requestline, tak aby był gotowy do analizy i (c) przechwytywać wszelkie kody błędów, które występują podczas analizowania, zamiast pozwalać na zapisanie ich z powrotem do klienta (ponieważ nie mamy takiego!).

Więc tutaj jest nasza specjalizacja klasy Biblioteka standardowa:

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 

Ponownie życzę ludzie Biblioteka standardowa sobie sprawę, że podczas analizowania HTTP powinny być podzielone w sposób, który nie wymaga od nas, aby napisać dziewięć linie kodu, aby poprawnie go nazwać, ale co możesz zrobić? Oto jak można wykorzystać tę prostą klasę:

# 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" 

Jeśli wystąpił błąd podczas parsowania The error_code nie będzie 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')" 

wolę korzystania z biblioteki Standardowy tak dlatego podejrzewam że już napotkali i rozwiązali jakieś skrajne przypadki, które mogą mnie ugryźć, jeśli sam spróbuję ponownie wdrożyć specyfikację internetową za pomocą wyrażeń regularnych.

+0

To jest niesamowite, dzięki! Muszę jednak zachować kolejność informacji w nagłówkach HTTP (słowniki nie utrzymują porządku). Czy jest to mimo wszystko? – jeffrey

+1

Nie jestem pewien! Głębokim wnętrzem klas "Message" i żądań, których Python używa do wykonania tego parsowania, powinien być, jak sądzę, wiersz kodu, który tworzy słownik nagłówków. Gdyby można było zamiast tego użyć 'OrderedDict' zamiast zwykłego' dict', to znałbyś kolejność, ale po krótkiej krótkiej wizycie nie wiedziałem, gdzie powstał słownik nagłówka. –

7

mimetools został przestarzały od czasu wersji Python 2.3 i całkowicie usunięty z Python 3 (link).

Oto jak należy zrobić w Pythonie 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) 
+0

'e-mail.message_from_file (io.StringIO (headers_alone))' można zastąpić 'mail.message_from_string (headers_alone)'. – eigenein

Powiązane problemy