经常在爬有些网站的时候需要登录,大多数网站需要你提供一个用户名和密码,在这种情况下,需要先向网站发送一个POST请求。可以使用Scrapy的FormRequest
类,这个类和Request
类很相似,只是多了一个extra参数,用这个参数可以传递表单数据。要使用这个类,先导入:
from scrapy.http import FormRequest
然后把start_urls
替换成start_requests()
方法,因为在这种情况下需要的不仅仅是一些URL(start_requests()
方法的默认行为是从start_urls
取出URL发出请求)。
在start_requests()
方法中创建并返回一个FormRequest
:
# Start with a login request
def start_requests(self):
return [
FormRequest(
"http://web:9312/dynamic/login",
formdata={"user": "user", "pass": "pass"}
)]
Scrapy帮助我们处理了Cookies,只要登录之后,它就会在以后的请求中传递给服务器,就像浏览器做的一样。运行一下scrapy crawl
:
$ scrapy crawl login
INFO: Scrapy 1.0.3 started (bot: properties)
...
DEBUG: Redirecting (302) to <GET .../gated> from <POST
.../login >
DEBUG: Crawled (200) <GET .../data.php>
DEBUG: Crawled (200) <GET .../property_000001.html> (referer:
.../data.php)
DEBUG: Scraped from <200 .../property_000001.html>
{'address': [u'Plaistow, London'],
'date': [datetime.datetime(2015, 11, 25, 12, 7, 27, 120119)],
'description': [u'features'],
'image_urls': [u'http://web:9312/images/i02.jpg'],
...
INFO: Closing spider (finished)
INFO: Dumping Scrapy stats:
{...
'downloader/request_method_count/GET': 4,
'downloader/request_method_count/POST': 1,
...
'item_scraped_count': 3,
从以上的输出中可以观察到一点:从dynamic/login
(登录页面)重定向到dynamic/gated
(登录后自动跳转到的页面)。也就是说,Scrapy从登录后跳转的作为种子URL,而这个跳转行为是与浏览器一致的。
如果输入错了用户名或者密码,会被重定向到一个错误页面,不会有items,且爬取的过程也会终止,比如:
$ scrapy crawl login
INFO: Scrapy 1.0.3 started (bot: properties)
...
DEBUG: Redirecting (302) to <GET .../dynamic/error > from <POST
.../dynamic/login>
DEBUG: Crawled (200) <GET .../dynamic/error>
...
INFO: Spider closed (closespider_itemcount)
上面只是一个简单的登录的例子,有些网站的登录做得更加复杂,但是大多数Scrapy也能很容易地处理。比如有些网站需要你从表单页POST数据到登录页的时候加上一些其他的一些表单变量,这样可以确认cookies是否已经启用,而且可以让暴力破解更加困难。
假设你访问了http://localhost:9312/dynamic/nonce这个URL,用谷歌浏览器的开发者工具可以看到这个表单有一个隐藏的域是nonce。当提交这份表单(到http://localhost:9312/dynamic/nonce-login)时,必须给出正确的用户名密码以及访问登录页面时服务器给出的nonce值。这个值猜是没法猜的,只能分成两个请求来解决登录问题。先要访问表单面然后访问登录页并传递数据。Scrapy有内建的函数帮助我们完成。
创建一个爬虫,在start_request()
里面先返回一个指向表单页的请求,并把它的callback
属性设置成parse_welcome()
。在parse_welcome()
方法中,使用FormRequest
对象的from_response()
方法,创建一个包含了原来表单所有域和值的FormRequest
对象。FormRequest.from_response()
方法大概模仿了页面上的第一个表单的单击提交行为,不过表单的每个域都是空的。
注:
花些时间熟悉一下from_response()
方法的文档是很值得的。它有很多特性比如fromname
和formnumber
可以在页面上不止一个表单时帮助你选择你想要的那个表单。
from_response()
方法包含了表单所有的包括隐藏的域,我们需要做的仅仅是用formdata
参数填充user
和pass
域并返回FormRequest
对象即可。相关代码如下:
# Start on the welcome page
def start_requests(self):
return [
Request(
"http://web:9312/dynamic/nonce", callback=self.parse_welcome)
]
# Post welcome page's first form with the given user/pass
def parse_welcome(self, response):
return FormRequest.from_response(
response,
formdata={"user": "user", "pass": "pass"}
)
运行结果如下:
$ scrapy crawl noncelogin
INFO: Scrapy 1.0.3 started (bot: properties)
...
DEBUG: Crawled (200) <GET .../dynamic/nonce>
DEBUG: Redirecting (302) to <GET .../dynamic/gated > from <POST
.../dynamic/nonce-login>
DEBUG: Crawled (200) <GET .../dynamic/gated>
...
INFO: Dumping Scrapy stats:
{...
'downloader/request_method_count/GET': 5,
'downloader/request_method_count/POST': 1,
...
'item_scraped_count': 3,
可以看到,第一个请求是到/dynamic/nonce的GET请求,然后是到/dynamic/nonce-login的POST请求,再然后是登录之后到/dynamic/gated的重定向.