在用 [Tornado][tornado] 来写一个爬虫,遇到几个问题,记录一下。

无法捕获异常###

def http_client():
    _http_client = tornado.httpclient.AsyncHTTPClient()
    req = tornado.httpclient.HTTPRequest("http://httpbin.org/post", method="POST", body=urllib.urlencode({"a": "a"}))
    #return _http_client.fetch("http://httpbin.org/post", method="POST", body={"a": 1})
    return _http_client.fetch(req)

@tornado.gen.coroutine
def login():

    response = yield http_client()
	
    raise tornado.gen.Return(response.body)

@tornado.gen.coroutine
def test():
    #body = yield login()
    #print body
    tornado.ioloop.IOLoop.instance().stop()
    
if __name__ == '__main__':
    test()
    tornado.ioloop.IOLoop.instance().start()

上面的代码,当 http://httpbin.org/post 不存在时,李云上会抛出404的HTTPError ,但是在这里不会。而且POST请求,如果没有body参数,也应该会抛出异常,但是也没有。

这是因为 [Tornado][tornado] 是通过异常来实现协程,在@tornado.gen.coroutine装饰器里把异常给捕获了。但是在RequestHandler里,web 会处理这个异常并输出。但是我写爬虫类时还没有使用到 web。然后 Google 一通后,就发现了问题了所在了。

只要加上这段代码就可以了

 if __name__ == '__main__':
    #test()
    def done_callback(future):
        future.result()

    future = test()
    future.add_done_callback(done_callback)
    tornado.ioloop.IOLoop.instance().start()

这里的future.result()会执行

If the operation succeeded, return its result. If it failed, re-raise its exception.

文档里面有写。

HTTPClient 重定向不带 cookie###

爬虫就要涉及到 Cookie 的处理,本来一直用的requests,但是这个不支持 [Tornado][tornado] 的异步,就只能用 [Tornado][tornado] 的HTTPClient了,所以就要自己处理 session。
然后就遇到了一个问题了,我要爬取的网站很多地方当登录成功后就用302重定向了,但是HTTPClient的重定向不带 cookie,导致登录失败。
然后我就这样解决了

        try:
            response = yield self.post(self.LOGIN_URL, body=data, follow_redirects=False)
        except Exception as e:
            if e.response.headers["Location"] == "/stScore.aspx":
                raise Return(True)

tornado.httpclient.HTTPRequest的参数里,有一个follow_redirects的参数,可以控制是否自动重定向,给False,然后会抛出HTTPError,这个时候自己捕获并且处理一下就可以了。(还是requests好用,自动把这个处理掉了)

后记###

其实可以用requests,把请求操作放到队列里去执行,还可以用到gevent,这个以后再去研究以下吧。

致谢###