从深处浅谈Web Crawler

原创
2016/05/04 08:50
阅读数 138

可在我的博客看到更多Python,ML文章 突然有个问题回绕在脑海:写Crawler为什么有时候非得模拟浏览器?然后就萌生了写点什么的想法。

问题描述

很多人都认为用Python 写一个Crawler 是一件很轻松的事情,我不想否认,也不想肯定。你的知识决定你能看多远。 一个简单的Crawler可以只有几句

import requests
 res = requests.get('http://midday.me')
 print res.content

运行上面几行代码就能拿到我博客的首页的内容。但是你知道这几句背后发生了什么吗,这样去拿数据和浏览器访问究竟有何区别呢? 为了能很好的解释这其中的过程,我会用Tornado 来写Server 然后用代码或者浏览器去访问,还会结合urllib,urllib2 ,requests 的源码初步的介绍。

HTTP 协议

引用维基百科:HTTP是一个客户端终端(用户)和服务器端(网站)请求和应答的标准(TCP)。通过使用Web浏览器、网络爬虫或者其它的工具,客户端发起一个HTTP请求到服务器上指定端口(默认端口为80)。我们称这个客户端为用户代理程式(user agent)。应答的服务器上存储着一些资源,比如HTML文件和图像。我们称这个应答服务器为源服务器(origin server)。在用户代理和源服务器中间可能存在多个「中间层」,比如代理伺服器、网关或者隧道(tunnel)。 维基百科解释的很清楚,具体点说,客户端就是浏览器,服务端在这里我访问我的博客地址。 在使用浏览器访问网页的时候,打开浏览器,F12 ,打开开发者工具,在地址栏输入需要访问的地址,在右侧的network里能看到浏览器给服务端发送了请求。有html, js, css ,图片等资源从服务端返回,如下图:其中红色的就是加载失败的资源。 chrom crawler

你说我看到了客户端,那服务端呢?那我就写个服务端和客户端,让彼此交互,看看能发生什么。


import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):

    def get(self):
        print u"收到请求"
        print self.request
        print u"返回数据"
        self.write("Hello, world")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

运行上面的代码,然后在浏览器地址栏输入'http://127.0.0.1:8888' 能在浏览器看到Hello , world ,在服务端能看到输出的信息。其中的逻辑是:客户端(也就是浏览器)向服务端发生请求,然后服务端收到请求,服务端会产生一个HTTPServerRequest,里面包含客户端发来的数据,和一些信息。服务端打印如下: HTTPServerRequest 在打印里面能看到很多如:客户端发起请求的Headers,其中就有最熟悉的User-Agent .

当然你也可以写段程序来访问,看看和浏览器访问的结果有什么不一样。你会发现User-Agent 就不一样,这也是为什么,在爬虫里面每次都要写上User-Agent 因为服务端可以通过User-Agent 来判断你是不是浏览器.User-Agent 里面都是浏览器的信息如版本号之类的。

Session && Cookies

由于HTTP 协议不是长链接是无状态的,就是每次请求之后都是断开的,服务端如何识别客户端呢?举个例子,你登陆网站的时候服务端怎么知道输入输入密码成功的那个客户端和登陆成功之后的客户端是一个呢,好吧反着说如果没有Session 和Cookies 你登陆一次是不是去任何电脑上都可以访问所有登陆才能访问的页面?。这就不得不聊的就是Session和Cookies了。简单点说,Session存在于服务端,Cookies存在于客户端,二者一一对应。服务端通过在Session 中保存一些信息,在客户端的Cookies 保存一些信息,每次请求客户端都带上这个Cookies服务端就能通过比对Session 中的信息来识别。当然浏览器有禁用Cookies 的时候,这种情况是能解决的,这里不聊。有的网站你禁用Cookies 是完全不能访问的。 为了比较好的理解,还是用Tornado 写Server, requests 来写客户端,来看看Cookies, 和Session. 由于Tornado 没有实现比较好的Session 机制,先来看看Cookies 服务端


import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        if not self.get_cookie("mycookie"):
            self.set_cookie("mycookie", "myvalue")
            self.write("Your cookie was not set yet!")
        else:
            self.write("Your cookie was set!")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

服务端通过判断客户端的cookies信息来返回不一样的内容

import requests
res = requests.get('http://127.0.0.1:8888')
print(res.text)

上面这种方式和下面这种方式请求的结果完全不一样的。具体是什么请自行设置。

import requests
res = requests.get('http://127.0.0.1:8888', cookies={'mycookie': "im cookies"})
print(res.text)

由于Tornado 没有Session 就不在这里多说,你可以去看大神们对Tornado实现的Session 。

浏览器和简单爬虫的对比

从上面我们看到,浏览器访问服务端,和爬虫访问服务端的原理是一样的,为了回答最初的问题:那为什么有时候一定要模拟浏览器来做爬虫呢? 先初步认识下浏览器。 浏览器由下面几个主要组件:

1.用户界面 :包括地址栏、前进/后退钮、书签菜单等。除了浏览器主窗口显示的您请求的2.页面外,其他显示的各个部分都属于用户界面。 3.浏览器引擎 - 在用户界面和呈现引擎之间传送指令。 4.呈现引擎 - 负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。 5.**网络 **- 用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现。 6.用户界面后端 - 用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。 7.JavaScript 解释器。用于解析和执行 JavaScript 代码。 数据存储。这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的 HTML 规范 (HTML5) 定义了“网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。 如下图: layers_browser

浏览器有7个组件,其中用户界面,用户界面后端这种肯定是不需要在爬虫里面有的。 那我们上面最简单的爬虫其实只拥有网络这个组件的基本功能,其他的都没有,缺少了这些组件会发生什么呢? 最简单的是网页内容是AJAX发起请求,然后在客户端渲染的时候,你会发现爬虫需要的数据不在Html源码里面。但是你在查看元素窗口又能看到,数据,这时候就要去寻找真正回来数据的请求,通常这个请求回来的数据都是Json. 往往JavaScript解释器,浏览器引擎就是爬虫缺少的关键东西。

回答最初的问题

知道上面一些知识之后,对一些常见反爬虫方式就会有比较清晰的认识。 反爬虫比较简单粗暴的反爬虫方式是通过访问次数,或频率限制访问。通常是1个IP 在特定时间内的访问次数有限制。这种问题解决办法已经没的选只有换代理。并不是模拟浏览器能解决的。

有时候是通过执行JavaScript 设置客户端访问服务端的Cookies, 这时候如果你找不到设置的Cookies是什么,就可以模拟浏览器.对于异步加载的数据也可以用模拟浏览器,获得浏览器渲染之后的数据,从而不用去详细分析,AJAx请求的API . 见过最麻烦的反爬虫机制,到现在除了模拟浏览器,我还没想都有什么其他方法。因为要搞懂对方的JavaScript 代码事件很麻烦的事情,特别是它执行的JavaScript 并不是每次都是相同的,所有就很难做到理解JavaScript 干了什么,只有老老实实运行那些混淆的JavaScript。

最后我想说,究竟是模拟浏览器好还是不模拟浏览器。我的观点是能不用浏览器就绝不用浏览器。对于Python 有很多可用的模拟浏览器的功能。模拟浏览器也并不是万能的。

展开阅读全文
打赏
1
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
1
分享
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部