diff --git a/docs/topics/request-response.rst b/docs/topics/request-response.rst index bf9acf653..d1caab550 100644 --- a/docs/topics/request-response.rst +++ b/docs/topics/request-response.rst @@ -254,7 +254,7 @@ objects. The :class:`FormRequest` objects support the following class method in addition to the standard :class:`Request` methods: - .. classmethod:: FormRequest.from_response(response, [formnumber=0, formdata=None, clickdata=None, dont_click=False, ...]) + .. classmethod:: FormRequest.from_response(response, [formname=None, formnumber=0, formdata=None, clickdata=None, dont_click=False, ...]) Returns a new :class:`FormRequest` object with its form field values pre-populated with those found in the HTML ``
`` element contained @@ -277,6 +277,11 @@ objects. to pre-populate the form fields :type response: :class:`Response` object + :param formname: if given, the form with name attribute set to this value + will be used. Otherwise, ``formnumber`` will be used for selecting + the form. + :type formname: string + :param formnumber: the number of form to use, when the response contains multiple forms. The first one (and also the default) is ``0``. :type formnumber: integer @@ -298,6 +303,9 @@ objects. The other parameters of this class method are passed directly to the :class:`FormRequest` constructor. + .. versionadded:: 0.10.3 + The ``formname`` parameter. + Request usage examples ---------------------- diff --git a/scrapy/http/request/form.py b/scrapy/http/request/form.py index 1356cd27f..b683c6415 100644 --- a/scrapy/http/request/form.py +++ b/scrapy/http/request/form.py @@ -37,17 +37,27 @@ class FormRequest(Request): self.headers['Content-Type'] = 'application/x-www-form-urlencoded' @classmethod - def from_response(cls, response, formnumber=0, formdata=None, + def from_response(cls, response, formname=None, formnumber=0, formdata=None, clickdata=None, dont_click=False, **kwargs): encoding = getattr(response, 'encoding', 'utf-8') forms = ParseFile(StringIO(response.body), response.url, encoding=encoding, backwards_compat=False) if not forms: raise ValueError("No element found in %s" % response) - try: - form = forms[formnumber] - except IndexError: - raise IndexError("Form number %d not found in %s" % (formnumber, response)) + + form = None + + if formname: + for f in forms: + if f.name == formname: + form = f + break + + if not form: + try: + form = forms[formnumber] + except IndexError: + raise IndexError("Form number %d not found in %s" % (formnumber, response)) if formdata: # remove all existing fields with the same name before, so that # formdata fields properly can properly override existing ones, diff --git a/scrapy/tests/test_http_request.py b/scrapy/tests/test_http_request.py index cc8c0b412..e9da2d3b3 100644 --- a/scrapy/tests/test_http_request.py +++ b/scrapy/tests/test_http_request.py @@ -330,6 +330,67 @@ class FormRequestTest(RequestTest): response = Response("http://www.example.com/lala.html", body=respbody) self.assertRaises(IndexError, self.request_class.from_response, response, formnumber=1) + def test_from_response_noformname(self): + respbody = """ + + + +
+ """ + response = Response("http://www.example.com/formname.html", body=respbody) + r1 = self.request_class.from_response(response, formdata={'two':'3'}, callback=lambda x: x) + self.assertEqual(r1.method, 'POST') + self.assertEqual(r1.headers['Content-type'], 'application/x-www-form-urlencoded') + fs = cgi.FieldStorage(StringIO(r1.body), r1.headers, environ={"REQUEST_METHOD": "POST"}) + self.assertEqual(fs['one'].value, '1') + self.assertEqual(fs['two'].value, '3') + + + def test_from_response_formname_exists(self): + respbody = """ +
+ + +
+
+ + +
+ """ + response = Response("http://www.example.com/formname.html", body=respbody) + r1 = self.request_class.from_response(response, formname="form2", callback=lambda x: x) + self.assertEqual(r1.method, 'POST') + fs = cgi.FieldStorage(StringIO(r1.body), r1.headers, environ={"REQUEST_METHOD": "POST"}) + self.assertEqual(fs['three'].value, "3") + self.assertEqual(fs['four'].value, "4") + + def test_from_response_formname_notexist(self): + respbody = """ +
+ +
+
+ +
+ """ + response = Response("http://www.example.com/formname.html", body=respbody) + r1 = self.request_class.from_response(response, formname="form3", callback=lambda x: x) + self.assertEqual(r1.method, 'POST') + fs = cgi.FieldStorage(StringIO(r1.body), r1.headers, environ={"REQUEST_METHOD": "POST"}) + self.assertEqual(fs['one'].value, "1") + + def test_from_response_formname_errors_formnumber(self): + respbody = """ +
+ +
+
+ +
+ """ + response = Response("http://www.example.com/formname.html", body=respbody) + self.assertRaises(IndexError, self.request_class.from_response, response, formname="form3", formnumber=2) + class XmlRpcRequestTest(RequestTest):