3 @copyright: (c) 2005 by Szoftver Messias Bt.
6 Objects of the MozillaEmulator class can emulate a browser that is capable of:
10 - configurable user agent string
12 - multipart POST (send files)
13 - receive content into file
16 I have seen many requests on the python mailing list about how to emulate a browser. I'm using this class for years now, without any problems. This is how you can use it:
19 2. Install and open the livehttpheaders plugin
20 3. Use the website manually with firefox
21 4. Check the GET and POST requests in the livehttpheaders capture window
22 5. Create an instance of the above class and send the same GET and POST requests to the server.
26 - For testing, use a MozillaCacher instance - this will cache all pages and make testing quicker
27 - You can change user agent string in the build_opened method
28 - The "encode_multipart_formdata" function can be used alone to create POST data from a list of field values and files
32 - should have a method to save/load cookies
42 class MozillaEmulator(object):
44 def __init__(self,cacher={},trycount=0):
45 """Create a new MozillaEmulator object.
47 @param cacher: A dictionary like object, that can cache search results on a storage device.
48 You can use a simple dictionary here, but it is not recommended.
49 You can also put None here to disable caching completely.
50 @param trycount: The download() method will retry the operation if it fails. You can specify -1 for infinite retrying.
51 A value of 0 means no retrying. A value of 1 means one retry. etc."""
53 self.cookies = cookielib.LWPCookieJar()
55 self.trycount = trycount
57 def build_opener(self,url,postdata=None,extraheaders={},forbid_redirect=False):
59 'Accept':'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png',
60 'Accept-Language':'en,en-us;q=0.5',
61 'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
63 for key,value in extraheaders.iteritems():
64 txheaders[key] = value
65 req = urllib2.Request(url, postdata, txheaders)
66 self.cookies.add_cookie_header(req)
68 redirector = HTTPNoRedirector()
70 redirector = urllib2.HTTPRedirectHandler()
72 http_handler = urllib2.HTTPHandler(debuglevel=self.debug)
73 https_handler = urllib2.HTTPSHandler(debuglevel=self.debug)
75 u = urllib2.build_opener(http_handler,https_handler,urllib2.HTTPCookieProcessor(self.cookies),redirector)
76 u.addheaders = [('User-Agent','Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.8) Gecko/20050511 Firefox/1.0.4')]
77 if not postdata is None:
78 req.add_data(postdata)
81 def download(self,url,postdata=None,extraheaders={},forbid_redirect=False,
82 trycount=None,fd=None,onprogress=None,only_head=False):
83 """Download an URL with GET or POST methods.
85 @param postdata: It can be a string that will be POST-ed to the URL.
86 When None is given, the method will be GET instead.
87 @param extraheaders: You can add/modify HTTP headers with a dict here.
88 @param forbid_redirect: Set this flag if you do not want to handle
89 HTTP 301 and 302 redirects.
90 @param trycount: Specify the maximum number of retries here.
91 0 means no retry on error. Using -1 means infinite retring.
92 None means the default value (that is self.trycount).
93 @param fd: You can pass a file descriptor here. In this case,
94 the data will be written into the file. Please note that
95 when you save the raw data into a file then it won't be cached.
96 @param onprogress: A function that has two parameters:
97 the size of the resource and the downloaded size. This will be
98 called for each 1KB chunk. (If the HTTP header does not contain
99 the content-length field, then the size parameter will be zero!)
100 @param only_head: Create the openerdirector and return it. In other
101 words, this will not retrieve any content except HTTP headers.
103 @return: The raw HTML page data, unless fd was specified. When fd
104 was given, the return value is undefined.
106 warnings.warn("Performing download of %s" % url, UserWarning, 2)
108 trycount = self.trycount
112 req,u = self.build_opener(url,postdata,extraheaders,forbid_redirect)
113 openerdirector = u.open(req)
115 print req.get_method(),url
116 print openerdirector.code,openerdirector.msg
117 print openerdirector.headers
118 self.cookies.extract_cookies(openerdirector,req)
120 return openerdirector
121 return openerdirector.read()
122 except urllib2.URLError:
124 if (trycount > -1) and (trycount < cnt):
128 print "MozillaEmulator: urllib2.URLError, retryting ",cnt
131 class HTTPNoRedirector(urllib2.HTTPRedirectHandler):
132 """This is a custom http redirect handler that FORBIDS redirection."""
134 def http_error_302(self, req, fp, code, msg, headers):
135 e = urllib2.HTTPError(req.get_full_url(), code, msg, headers, fp)
136 if e.code in (301,302):
137 if 'location' in headers:
138 newurl = headers.getheaders('location')[0]
139 elif 'uri' in headers:
140 newurl = headers.getheaders('uri')[0]