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
36 class MozillaEmulator(object):
38 def __init__(self, cacher=None, trycount=0):
39 """Create a new MozillaEmulator object.
41 @param cacher: A dictionary like object, that can cache search results on a storage device.
42 You can use a simple dictionary here, but it is not recommended.
43 You can also put None here to disable caching completely.
44 @param trycount: The download() method will retry the operation if it fails. You can specify -1 for infinite retrying.
45 A value of 0 means no retrying. A value of 1 means one retry. etc."""
49 self.cookies = cookielib.LWPCookieJar()
51 self.trycount = trycount
53 def build_opener(self, url, postdata=None, extraheaders=None, forbid_redirect=False):
54 if extraheaders is None:
58 'Accept': 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png',
59 'Accept-Language': 'en,en-us;q=0.5',
60 'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
62 for key, value in extraheaders.iteritems():
63 txheaders[key] = value
64 req = urllib2.Request(url, postdata, txheaders)
65 self.cookies.add_cookie_header(req)
67 redirector = HTTPNoRedirector()
69 redirector = urllib2.HTTPRedirectHandler()
71 http_handler = urllib2.HTTPHandler(debuglevel=self.debug)
72 https_handler = urllib2.HTTPSHandler(debuglevel=self.debug)
74 u = urllib2.build_opener(
77 urllib2.HTTPCookieProcessor(self.cookies),
82 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.8) Gecko/20050511 Firefox/1.0.4'
84 if not postdata is None:
85 req.add_data(postdata)
88 def download(self, url, postdata=None, extraheaders=None, forbid_redirect=False,
89 trycount=None, fd=None, onprogress=None, only_head=False):
90 """Download an URL with GET or POST methods.
92 @param postdata: It can be a string that will be POST-ed to the URL.
93 When None is given, the method will be GET instead.
94 @param extraheaders: You can add/modify HTTP headers with a dict here.
95 @param forbid_redirect: Set this flag if you do not want to handle
96 HTTP 301 and 302 redirects.
97 @param trycount: Specify the maximum number of retries here.
98 0 means no retry on error. Using -1 means infinite retring.
99 None means the default value (that is self.trycount).
100 @param fd: You can pass a file descriptor here. In this case,
101 the data will be written into the file. Please note that
102 when you save the raw data into a file then it won't be cached.
103 @param onprogress: A function that has two parameters:
104 the size of the resource and the downloaded size. This will be
105 called for each 1KB chunk. (If the HTTP header does not contain
106 the content-length field, then the size parameter will be zero!)
107 @param only_head: Create the openerdirector and return it. In other
108 words, this will not retrieve any content except HTTP headers.
110 @return: The raw HTML page data, unless fd was specified. When fd
111 was given, the return value is undefined.
113 warnings.warn("Performing download of %s" % url, UserWarning, 2)
115 if extraheaders is None:
118 trycount = self.trycount
122 req, u = self.build_opener(url, postdata, extraheaders, forbid_redirect)
123 openerdirector = u.open(req)
125 print req.get_method(), url
126 print openerdirector.code, openerdirector.msg
127 print openerdirector.headers
128 self.cookies.extract_cookies(openerdirector, req)
130 return openerdirector
131 return openerdirector.read()
132 except urllib2.URLError:
134 if (trycount > -1) and (trycount < cnt):
138 print "MozillaEmulator: urllib2.URLError, retryting ", cnt
141 class HTTPNoRedirector(urllib2.HTTPRedirectHandler):
142 """This is a custom http redirect handler that FORBIDS redirection."""
144 def http_error_302(self, req, fp, code, msg, headers):
145 e = urllib2.HTTPError(req.get_full_url(), code, msg, headers, fp)
146 if e.code in (301, 302):
147 if 'location' in headers:
148 newurl = headers.getheaders('location')[0]
149 elif 'uri' in headers:
150 newurl = headers.getheaders('uri')[0]