使用Scrapy抓取需要登录的网站
2016-04-01 16:51
633 查看
经常在爬有些网站的时候需要登录,大多数网站需要你提供一个用户名和密码,在这种情况下,需要先向网站发送一个POST请求。可以使用Scrapy的
然后把
在
Scrapy帮助我们处理了Cookies,只要登录之后,它就会在以后的请求中传递给服务器,就像浏览器做的一样。运行一下
从以上的输出中可以观察到一点:从
如果输入错了用户名或者密码,会被重定向到一个错误页面,不会有items,且爬取的过程也会终止,比如:
上面只是一个简单的登录的例子,有些网站的登录做得更加复杂,但是大多数Scrapy也能很容易地处理。比如有些网站需要你从表单页POST数据到登录页的时候加上一些其他的一些表单变量,这样可以确认cookies是否已经启用,而且可以让暴力破解更加困难。
假设你访问了http://localhost:9312/dynamic/nonce这个URL,用谷歌浏览器的开发者工具可以看到这个表单有一个隐藏的域是nonce。当提交这份表单(到http://localhost:9312/dynamic/nonce-login)时,必须给出正确的用户名密码以及访问登录页面时服务器给出的nonce值。这个值猜是没法猜的,只能分成两个请求来解决登录问题。先要访问表单面然后访问登录页并传递数据。Scrapy有内建的函数帮助我们完成。
创建一个爬虫,在
注:
花些时间熟悉一下
运行结果如下:
可以看到,第一个请求是到/dynamic/nonce的GET请求,然后是到/dynamic/nonce-login的POST请求,再然后是登录之后到/dynamic/gated的重定向.
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的重定向.