###每篇一句:
A man is not old as long as he is seeking something. A man is not old until regrets take the place of dreams.
动态网页简介:
在我们编写爬虫时,可能会碰到以下两种问题:
- 我们所需要爬取的数据在网页源代码中并不存在;
- 点击下一页跳转页面时,网页的URL 并没与发生变化;
造成这种问题原因是,你所正在爬取的页面采取了动态加载的方式,是一个动态网页。
所谓的动态网页,是指跟静态网页相对的一种网页编程技术。静态网页,随着html代码生成,页面的内容和显示效果就不会发生变化了。而动态网页则不然,其显示的页面则是经过Javascript处理数据后生成的结果,可以发生改变。这些数据的来源有多种,可能是经过Javascript计算生成的,也可能是通过Ajax加载的。
动态网页经常使用的一种技术是Ajax请求技术。
-
Ajax:
Ajax = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML),其最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页的内容。
- 想要了解更多有关Ajax技术的可以看这里:
目前,越来越多的网站采取的是这种动态加载网页的方式,一来是可以实现web开发的前后端分离,减少服务器直接渲染页面的压力;二来是可以作为反爬虫的一种手段。
爬虫处理动态页面的两种方式
- 逆向工程
- 渲染动态网页
- selenium模拟浏览器
接下来就以 新浪读书——书摘 这个网站为例(典型的Ajax请求技术应用),采用这两种方法分别介绍一下爬虫如何处理这种动态页面,获取所需要数据的。
逆向工程:
对于动态加载的网页,我们想要获取其网页数据,需要了解网页是如何加载数据的,该过程就被成为逆向工程。
对于使用了Ajax 请求技术的网页,我们可以找到Ajax请求的具体链接,直接得到Ajax请求得到的数据。
- 需要注意的是,构造Ajax请求有两种方式:
- 原生的Ajax请求,会直接创建一个
XMLHTTPReques
t对象。 - 调用jQuery的ajax()方法。一般情况下,
$.ajax()
会返回其创建的XMLHTTPRequest
对象;但是,如果$.ajax()
的dataType
参数指定了为script
或jsonp
类型,$.ajax()
不再返回其创建的XMLHTTPRequest
对象。
- 原生的Ajax请求,会直接创建一个
对于这两种方式,只要创建返回了XMLHTTPRequest
对象,就可以通过Chrome浏览器的调试工具在NetWork
窗口通过设置XHR
过滤条件,直接筛选出Ajax请求的链接;如果是$.ajax()
并且dataType
指定了为script
或jsonp
,则无法通过这种方式筛选出来。
示例:
接下来以 新浪读书——书摘 为例,介绍如何得到无法筛选出来的Ajax请求链接:
-
在Chrome中打开网页,右键
检查
,会发现首页中书摘列表包含在一个id为subShowContent1_static
的div中,而查看网页源代码
会发现id为subShowContent1_static
的div为空。点击
更多书摘
或下一页
时,网页URL并没有发生变化。与我们最前面所说的两种情况相同。
-
F12
打开调试工具,打开NetWork
窗口*(功能是记录浏览器的活动记录network activities
)*,F5
刷新,可以看到浏览器发送以及接收到的数据记录:其实我们所找的Ajax请求链接就是其中一条。
首先设置
XHR
过滤条件:发现为空白,可以推断出这个网页采用的Ajax请求应该是设置了
dataType
为script
或者jsonp
。 -
重新打开调试工具,点击网页上的
更多书摘
,发现NetWork
窗口出现了很多记录,网页也多了新的内容,说明浏览器向服务器发送了请求。在网页上,右键
检查
更多书摘
,观察次元素绑定的JavaScript事件:根据JavaScript的知识:
javascript:
是一个伪协议,表示在触发<a>
时,执行一段JavaScript代码,而javascript:;
表示什么都不执行,这样点击<a>
时就没有任何反应。但一般在这种情况下,会给<a>
绑定一个事件回调,来执行业务.接下来就需要在网页的JavaSCript代码中找到
更多书摘
所触发的回调函数。 -
右键
检查网页源代码
,Ctrl+F
搜索subShowContent1_loadMore”
(元素更多书摘
的id),发现并没有某个具体的函数与之相关:这说明
更多书摘
的回调函数不在网页自身写的JavaScript代码中,那就应该在网页嵌入的JS文件中(通过<script type=”text/javascript” src=“***.js”> <script>
这种形式调用的JS文件)。 -
逐个打开嵌入的JS文件,查找与
更多书摘
绑定的函数,终于在“http://n.sinaimg.cn/book/js/feedlist.js”这个JS文件中发现函数$()
如下:-
但此函数中并没有直接构造Ajax请求,而是通过调用
ListMore()
函数执行。 -
查找
ListMore()
函数,发现其内部还是未构造Ajax请求,通过ListMore()
内部调用的函数进一步寻找,经过以下过程:$()
->ListMore()
->GetmoreList()
->getMore()
-
终于在
getMore()
函数中发现了Ajax请求的构造过程,发现dataType
确实是jsonp
:
-
-
在
NetWork
窗口Initiator
栏点击feedlist.js
进入Sources
窗口:-
在
$.ajax()
处添加断点: -
F5
刷新,Javascript执行到断点处会停止,鼠标分别指在url
,data
处观察数据: -
得到了url以及data的格式,就可以根据
jQuery
的ajax()
用法构造正确的Ajax 请求链接:http://feed.mix.sina.com.cn/api/roll/get?callback=jsonp1522410869405&pageid=96&lid=560&num=20&page=1
-
在浏览器输入构造的链接进行验证,对比网页数据,发现这就是我们需要的Ajax请求链接。
-
-
之前我们说过,在
NetWork
窗口就有我们需要的Ajax请求链接,对比发现,果然存在: -
当我们点击
更多书摘
、下一页
时,发现又多了几条这样的请求:对比发现:只有
jsonp**********
与page=*
这两部分不同,jsonp
后面跟的数字为当前时间的时间戳,是jQuery.ajax()
方法自动计算添加的,page
后面就是页数,这时候我们只要改变page后面的数字就可以构造翻页的链接了。
总结一下这种方法:
- 首先通过设置
XHR
过滤条件,观察是否可以直接得到Ajax 请求;如果不可以,继续下一步。 - 查找能够发送Ajax请求的元素绑定的事件或回调函数:首先在网页源代码中查找,没有的话,逐个查找嵌入的JS文件。
- 找到之后,看函数内部是否直接构造Ajax请求,没有直接构造的话,根据函数调用关系一步一步查找,直到找到构造过程为止。
- 添加断点,调试,根据Ajax请求的构造方式以及数据形式手动构造链接。
- 验证构造的链接的正确性。
其实当你有了经验之后,对一些不是很复杂的网页,根本就不用进行这么复杂的逆向工程,凭URL形式可以很快的在NetWork
窗口很快的 选择-验证 出所需的Ajax请求。
得到Ajax请求链接之后,可以直接得到请求的数据,一般为json格式,处理后即可使用。
最后:
本文以 新浪读书——书摘 这个示例,介绍了如何得到 Ajax请求链接,这是爬取动态网页时可以采取的一种方式,这种方式可能会在寻找Ajax请求的过程中浪费一点时间,并且要求对JavaScript有一定的了解,但找到链接后,爬取的过程可以很迅速。
如果对爬虫执行效率没有过多要求,又不想浪费太多时间在了解JavaScript代码逻辑、找寻Ajax请求链接上,可以参看下一篇文章 爬虫篇:动态网页的处理方式(中)—— 渲染动态网页,这篇文章提供了爬取动态网页的另一种思路。
文中有什么错误或不足之处,欢迎指出!