2011-01-13 52 views
26

Tôi có chuỗi HTTP thô và tôi muốn đại diện cho các trường trong đối tượng. Có cách nào để phân tích cú pháp tiêu đề riêng lẻ từ chuỗi HTTP không?Phân tích cú pháp Tiêu đề HTTP thô

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

Trả lời

7

Điều này dường như làm việc tốt nếu bạn tách các dòng 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 

Một cách để phân tích ví dụ của bạn và thêm thông tin từ dòng đầu tiên cho các đối tượng sẽ là:

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

Có cách nào để làm điều này trong python3? – Broseph

+0

mimetools không còn được dùng kể từ 2.3 –

+0

@Broseph Xem câu trả lời của Gowtham. – JeromeJ

72

Có các công cụ tuyệt vời trong Thư viện chuẩn để phân tích cú pháp tiêu đề RFC 821 và cũng để phân tích toàn bộ các yêu cầu HTTP. Dưới đây là một chuỗi ví dụ yêu cầu (lưu ý rằng Python đối xử với nó như một chuỗi lớn, mặc dù chúng ta đang phá vỡ nó trên một số dòng cho dễ đọc) mà chúng ta có thể ăn các ví dụ của tôi:

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

Như @TryPyPy chỉ ra, bạn có thể sử dụng mimetools.Message để phân tích các tiêu đề - mặc dù chúng ta nên thêm rằng kết quả Message hành vi đối tượng như một cuốn từ điển các tiêu đề khi bạn đang thực hiện tạo ra nó:

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

Nhưng điều này, tất nhiên, bỏ qua những dòng yêu cầu, hoặc làm cho bạn phân tích nó cho mình. Nó chỉ ra rằng có một giải pháp tốt hơn nhiều.

Thư viện chuẩn sẽ phân tích cú pháp HTTP cho bạn nếu bạn sử dụng BaseHTTPRequestHandler. Mặc dù tài liệu của nó hơi mơ hồ - một vấn đề với toàn bộ bộ công cụ HTTP và URL trong Thư viện Chuẩn - tất cả những gì bạn phải làm để làm cho nó phân tích chuỗi là (a) quấn chuỗi của bạn trong một StringIO(), (b) đọc raw_requestline để nó sẵn sàng để được phân tích cú pháp, và (c) nắm bắt bất kỳ mã lỗi nào xảy ra trong quá trình phân tích cú pháp thay vì để nó cố gắng ghi chúng trở lại máy khách (vì chúng tôi không có)!

Vì vậy, đây là chuyên môn của chúng ta về lớp Thư viện Tiêu chuẩn:

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 

Một lần nữa, tôi muốn các folks thư viện chuẩn đã nhận ra rằng phân tích cú pháp HTTP nên được chia ra theo một cách mà không đòi hỏi chúng ta phải viết chín dòng mã để gọi đúng, nhưng bạn có thể làm gì? Đây là cách bạn sẽ sử dụng lớp đơn giản này:

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

Nếu có một lỗi trong quá trình phân tích cú pháp, các error_code sẽ không 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')" 

Tôi thích sử dụng bộ thư viện chuẩn như thế này bởi vì tôi nghi ngờ rằng họ đã gặp phải và giải quyết bất kỳ trường hợp cạnh nào có thể gây khó chịu cho tôi nếu tôi cố gắng triển khai lại một đặc tả Internet với các biểu thức chính quy.

+0

Điều này thật tuyệt vời, cảm ơn! Tuy nhiên, tôi cần phải duy trì thứ tự của thông tin được đệ trình tiêu đề HTTP (từ điển không duy trì thứ tự). Có anyway để làm điều này? – jeffrey

+1

Tôi không chắc chắn! Sâu bên trong 'Message' và các lớp yêu cầu mà Python sử dụng để thực hiện phân tích cú pháp này, tôi giả sử, một dòng mã tạo từ điển của các tiêu đề. Nếu nó có thể được nói thay vì sử dụng một 'OrderedDict' thay vì một' dict' đơn giản thì bạn sẽ biết thứ tự - nhưng, chỉ cần lưu lại mã một thời gian ngắn, tôi không thể biết từ điển đầu trang đã được tạo ra. –

7

mimetools không được dùng nữa kể từ Python 2.3 và được xóa hoàn toàn khỏi Python 3 (link).

Đây là cách bạn nên làm bằng 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) 
+0

'email.message_from_file (io.StringIO (headers_alone))' có thể được thay thế bằng 'email.message_from_string (headers_alone)'. – eigenein

Các vấn đề liên quan