Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MRG+1] Implement FormRequest.from_response CSS support #1382

Merged
merged 1 commit into from
Jan 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion docs/topics/request-response.rst
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ fields with form data from :class:`Response` objects.
The :class:`FormRequest` objects support the following class method in
addition to the standard :class:`Request` methods:

.. classmethod:: FormRequest.from_response(response, [formname=None, formnumber=0, formdata=None, formxpath=None, clickdata=None, dont_click=False, ...])
.. classmethod:: FormRequest.from_response(response, [formname=None, formnumber=0, formdata=None, formxpath=None, formcss=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 ``<form>`` element contained
Expand Down Expand Up @@ -310,6 +310,9 @@ fields with form data from :class:`Response` objects.
:param formxpath: if given, the first form that matches the xpath will be used.
:type formxpath: string

:param formcss: if given, the first form that matches the css selector will be used.
:type formcss: 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
Expand Down Expand Up @@ -339,6 +342,9 @@ fields with form data from :class:`Response` objects.
.. versionadded:: 0.17
The ``formxpath`` parameter.

.. versionadded:: 1.1.5
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have this version yet :) I think this change will be included in 1.1.0.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops, sorry, I missed that (I shouldn't merge PRs after midnight!).
lemme fix that

The ``formcss`` parameter.

Request usage examples
----------------------

Expand Down
10 changes: 8 additions & 2 deletions scrapy/http/request/form.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,14 @@ def __init__(self, *args, **kwargs):

@classmethod
def from_response(cls, response, formname=None, formid=None, formnumber=0, formdata=None,
clickdata=None, dont_click=False, formxpath=None, **kwargs):
clickdata=None, dont_click=False, formxpath=None, formcss=None, **kwargs):

kwargs.setdefault('encoding', response.encoding)

if formcss is not None:
from parsel.csstranslator import HTMLTranslator
formxpath = HTMLTranslator().css_to_xpath(formcss)

form = _get_form(response, formname, formid, formnumber, formxpath)
formdata = _get_inputs(form, formdata, dont_click, clickdata, response)
url = _get_form_url(form, kwargs.pop('url', None))
Expand Down Expand Up @@ -73,7 +79,7 @@ def _get_form(response, formname, formid, formnumber, formxpath):
f = root.xpath('//form[@id="%s"]' % formid)
if f:
return f[0]

# Get form element from xpath, if not found, go up
if formxpath is not None:
nodes = root.xpath(formxpath)
Expand Down
20 changes: 20 additions & 0 deletions tests/test_http_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,26 @@ def test_html_base_form_action(self):
req = self.request_class.from_response(response)
self.assertEqual(req.url, 'http://b.com/test_form')

def test_from_response_css(self):
response = _buildresponse(
"""<form action="post.php" method="POST">
<input type="hidden" name="one" value="1">
<input type="hidden" name="two" value="2">
</form>
<form action="post2.php" method="POST">
<input type="hidden" name="three" value="3">
<input type="hidden" name="four" value="4">
</form>""")
r1 = self.request_class.from_response(response, formcss="form[action='post.php']")
fs = _qs(r1)
self.assertEqual(fs[b'one'], [b'1'])

r1 = self.request_class.from_response(response, formcss="input[name='four']")
fs = _qs(r1)
self.assertEqual(fs[b'three'], [b'3'])

self.assertRaises(ValueError, self.request_class.from_response,
response, formcss="input[name='abc']")

def _buildresponse(body, **kwargs):
kwargs.setdefault('body', body)
Expand Down