爬虫抓取动态网页的6种方法

一,js 逆向分析二,使用WebKit 渲染引擎三,使用Selenium四,使用 Pyppeteer五,使用微软开源的自动化神器 Playwright六,使用 Splash
摘要由CSDN通过智能技术生成

在这里插入图片描述

一,js 逆向分析

对于动态加载的网页,我们想要获取其网页数据,需要了解网页是如何加载数据的,该过程就被成为逆向回溯。对于使用了Ajax 请求技术的网页,我们可以找到Ajax请求的具体链接,直接得到Ajax请求得到的数据。
需要注意的是,构造Ajax请求有两种方式:

  • 原生的Ajax请求:会直接创建一个XMLHTTPRequest对象。
  • 调用jQuery的ajax()方法:一般情况下,$.ajax()会返回其创建的XMLHTTPRequest对象;但是,如果$.ajax()的dataType参数指定了为script或jsonp类型,$.ajax()不再返回其创建的XMLHTTPRequest对象。

对于这两种方式,只要创建并返回了XMLHTTPRequest对象,就可以通过Chrome浏览器的调试工具在NetWork窗口设置过滤条件为 xhr ,直接筛选出Ajax请求的链接;如果是$.ajax()并且dataType指定了为script或jsonp(这种情况下NetWork 里面的 Type 都是 script,如果你懂得 jsonp 的原理的话就知道 jsonp 本质就是通过 script),则无法通过这种方式筛选出来(因为这两种方式是经典的跨域方法,而 XHR 是不能跨域的,所以设置 XHR 过滤)

示例:

接下来以 新浪读书——书摘 为例,介绍如何得到无法筛选出来的Ajax请求链接:

在Chrome中打开网页,右键检查,会发现首页中书摘列表包含在一个id为subShowContent1_static的div中,而查看网页源代码会发现id为subShowContent1_static的div为空。

如图所示:
在这里插入图片描述
在这里插入图片描述
并且点击更多书摘或下一页时,网页URL并没有发生变化。

这与我们最前面所说的两种情况相同,说明这个网页就是使用 JS 动态加载数据的。

F12打开调试工具,打开NetWork窗口,F5刷新,可以看到浏览器发送以及接收到的数据记录(我们可以点击上面的 XHR 或者 JS 对这些请求进行过滤):
在这里插入图片描述
可以发现目前两种类型的请求都是存在的,暂时还不能判断我们 div 中内容的动态加载使用的是哪一种方式,不过没关系,我们可以进一步进行测试。

1.根据 id 进行查找

我们知道,js 操作页面的数据一定要进行定位,最常用的方法就是使用 id 定位,因为 id 在整个页面中是唯一的,那么我们第一步就是在所有的 js 文件中找和 subShowContent1_static 这个 id 相关的文件,于是我在 network 页面使用 ctrl+f 进行全局搜索
在这里插入图片描述
最终定位到了可能性最大的文件 feedlist.js
在这里插入图片描述
进入这个文件以后我就定位到了一个匿名函数 $(),这个函数将参数传入 Listmore() 函数

在这里插入图片描述
listmore() 函数调用了 Getmorelist() 函数
在这里插入图片描述
Getmorelist() 函数 调用了 getMore() 函数
在这里插入图片描述
getmore() 函数定义了我们的请求
在这里插入图片描述

2.设置断点进行动态捕获

可以看到这里使用的是 jsonp 的形式跨域传递数据的,然后 URL 是一个对象,是运行中生成的,我们可以在运行中对这个函数添加一个断点
在这里插入图片描述
然后 f5 刷新
在这里插入图片描述

断下来以后就能看到我们想要看到的 URL 以及后面跟着的参数了,这样就可以根据jQuery的ajax()用法构造正确的Ajax 请求链接:

http://feed.mix.sina.com.cn/api/roll/get?callback=xxxxxxxx&pageid=96&lid=560&num=20&page=1

那么这个 callback 是多少呢,我们现在还看不出来,但是,既然这个是一个请求,那么肯定会在 network 中有记录,我们找找看
在这里插入图片描述
我们现在就锁定了我们想要找的链接,得到Ajax请求链接之后,可以直接得到请求的数据,一般为json格式,处理后即可使用。

其实当你有了经验之后,对一些不是很复杂的网页,根本就不用进行这么复杂的逆向工程,凭URL形式可以很快的在NetWork窗口 选择-验证 出所需的Ajax请求。

二,使用WebKit 渲染引擎

渲染引擎的职责就是渲染,即在浏览器窗口中显示所请求的内容。浏览器向服务器发送请求,得到服务器返回的资源文件后,需要经过渲染引擎的处理,将资源文件显示在浏览器窗口中。

目前使用较为广泛的渲染引擎有两种:

  • webkit——使用者有Chrome, Safari
  • Geoko——使用者有Firefox

渲染主流程:

渲染引擎首先通过网络获得所请求文档的内容,通常以8K分块的方式完成。

下面是渲染引擎在取得内容之后的基本流程:

在这里插入图片描述
解析html来构建dom树 -> 构建render树 -> 布局render树 -> 绘制render树

  • 渲染引擎开始解析html,并将标签转化为内容树中的dom节点。如果遇到JS,那么此时会启用另外的连接进行下载(下载过程中 dom
    树的构建不会停止),并且在下载完成后立即执行(执行过程中会阻塞 浏览器的其他行为,因为 js 的运行可能会改变 dom
    树的结构,为了不让刚刚构建好的 dom 树又被 js 改变,聪明的浏览器停止了 dom 树的构建)。
  • 接着,它解析外部CSS文件及style标签中的样式信息。这些样式信息以及html中的可见性指令将被用来构建另一棵树——render树(其实这一步是和上一步同时进行的,为了页面显示更迅速,css
    不会等到 dom 树构建完毕才开始构建 render树 )。
  • Render树由一些包含有颜色和大小等属性的矩形组成,它们将被按照正确的顺序显示到屏幕上。
  • Render树构建好了之后,将会执行布局过程,它将确定每个节点在屏幕上的确切坐标。
  • 再下一步就是绘制,即遍历render树,并使用UI后端层绘制每个节点。

补充知识:

浏览器会解析三个东西:

  • HTML/SVG/XHTML,解析这三种文件会产生一个 DOM Tree。
  • CSS,解析 CSS 会产生 CSS 规则树(CSSOM)。
    Javascript脚本,主要是通过 DOM API 和 CSSOM API 来操作 DOM Tree 和 CSS Rule Tree.

形象的HTML页面加载和解析流程:

  1. 用户输入网址(假设是个html页面,并且是第一次访问),浏览器向服务器发出请求,服务器返回html文件
  2. 浏览器开始载入html代码,发现<head>标签内有一个<link>标签引用外部CSS文件;
  3. 浏览器又发出CSS文件的请求,服务器返回这个CSS文件;
  4. 浏览器继续载入html中<body>部分的代码,并且CSS文件已经拿到手了,可以开始渲染页面了;
  5. 浏览器在代码中发现一个<img>标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片下载完,而是继续渲染后面的代码;
  6. 服务器返回图片文件,由于图片占用了一定面积,影响了后面段落的排布,因此浏览器需要回过头来重新渲染这部分代码;
  7. 浏览器发现了一个包含一行Javascript代码的<script>标签,赶快运行它;
  8. Javascript脚本执行了这条语句,它命令浏览器隐藏掉代码中的某个<div> (style.display=”none”)。突然少了这么一个元素,浏览器不得不重新渲染这部分代码;
  9. 终于等到了</html>的到来,浏览器泪流满面……
  10. 等等,还没完,用户点了一下界面中的“换肤”按钮,Javascript让浏览器换了一下<link>标签的CSS路径
  11. 浏览器召集了在座的各位<div><span><ul><li>们,“大伙儿收拾收拾行李,咱得重新来过……”,浏览器向服务器请求了新的CSS文件,重新渲染页面。

Javascript的加载和执行的特点:

  • 载入后马上执行;
  • 执行时会阻塞页面后续的内容(包括页面的渲染、其它资源的下载)。原因:因为浏览器需要一个稳定的DOM树结构,而JS中很有可能有代码直接改变了DOM树结构,比如使用 document.write或appendChild,甚至是直接使用的location.href进行跳转,浏览器为了防止出现JS修改DOM树,需要重新构建DOM树的情况,所以就会阻塞其他的下载和呈现。

当浏览器渲染引擎完成了dom树以及render树的构建之后,树中就已经包含了我们在浏览器窗口中可以看到的所有数据。所以爬虫可以在浏览器渲染引擎执行layout以及printing之前,得到dom树或者render树,从树中获取动态加载的数据。

示例:
接下来将使用WebKit 渲染引擎,通过 PySide 这个python库可以获得该引擎的一个便捷接口。

还是以 新浪读书——书摘 为例,可以发现:页面中文章列表的部分是动态加载的。

使用PySide库进行处理的示例代码如下:

#coding=utf-8

from PySide.QtGui import *
from PySide.QtCore import *
from PySide.QtWebKit import *


if __name__ == '__main__':

    url = "http://book.sina.com.cn/excerpt/rwws/"

    app = QApplication([])  # 完成其他Qt对象之前,必须先创建该对象
    webview = QWebView()  # 该对象是Web 对象的容器

    # 调用show方法显示窗口
    # webview.show()

    # 设置循环事件, 并等待网页加载完成
    loop = QEventLoop()
    webview.loadFinished.connect(loop.quit)
    webview.load(QUrl(url))
    loop.exec_()

    frame = webview.page().mainFrame()  # QWebFrame类有很多与网页交互的有用方法

    # 得到页面渲染后的html代码
    html = frame.toHtml()

    print html

通过print语句,我们可以发现:页面的源码html中已经包含了动态加载的内容。

与网站交互:

得到动态加载的内容后,需要解决的另一个问题是翻页问题。还好PySide库的QWebKit模块还有一个名为QWebFrame的类,支持很多与网页的交互操作。

如“点击”:

#根据CSS Selector 找到所需“进行翻页”的元素

elem = frame.findFirstElement('#subShowContent1_loadMore')

# 点击:通过evaluateJavaScript()函数可以执行Js代码

elem.evaluateJavaScript('this.click()')

除了点击事件,还可以进行填充表单,滚动窗口等操作

需要注意的是,在进行了翻页、或者获取更多内容时,一个最大的难点在于如何确定页面是否完成了加载,因为我们难以估计Ajax事件或者Js准备数据的时间。

对于这个问题有两种解决思路:

  • 等待固定的一段时间,比如time.sleep(3):这种方法容易实现,但效率较低。
  • 轮询网页,等待特定内容出现:这种方法虽然会在检查是否加载完成时浪费CPU周期,但更加可靠。

以下是一个简单的实现:

elem = None
while not elem:
	app.processEvents()
 	elem = frame.findAllElemnets('#pattern')

代码循环,直到出现特定元素。每次循环,调用app.processEvents()方法,用于给Qt事件循环执行任务的时间,比如响应点击事件。
但是PySide毕竟是一个为了Python的GUI 编程而开发的, 其功能对于爬虫来说实在是太过于庞大,所以我们可以把爬虫经常使用的功能进行封装,来提升编写爬虫的效率。

对PySide 常用功能的封装 —— ghost.py

ghost.py 是目前一个针对爬虫且功能比较完善的PySide的封装模块,使用它可以很方便的进行数据采集。
还是以获取列表页中每篇文章详情页地址为目标,

1.示例代码:

# coding=utf-8

import re
import time

from ghost import Ghost, Session


class SinaBookSpider(object):

    # 初始化相关参数
    gh = Ghost()
    ss = Session(gh, display=True)  # 设置display为true, 方便调试

    total = 1526  # 预先计算的总数据量
    count = 0  # 已爬取的数据量

    # 记录解析以及翻页位置
    location = 0
    click_times = 0

    def run(self):
        """
        开始爬虫
        :return:
        """
        # 打开网页
        self.ss.open("http://book.sina.com.cn/excerpt/rwws/")
        # 等待数据加载完成
        self.ss.wait_for_selector('#subShowContent1_static > div:nth-child(20)')

 
  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值