java爬虫抓取动态网页(爬取动态网页的一种方式:逆向工程的新思路:渲染动态)

优采云 发布时间: 2021-10-02 16:13

  java爬虫抓取动态网页(爬取动态网页的一种方式:逆向工程的新思路:渲染动态)

  每篇文章一句话:

  一个坚强的人会在命运的风暴中挣扎。

  前言:

  在上一篇文章中,我们介绍了一种爬取动态网页的方法:逆向工程。

  这种方式有一个缺陷:这种方式需要我们对JavaScript和Ajax有一定的了解,而当网页的JS代码杂乱无章,难以分析时,上述过程会耗费我们大量的时间和精力.

  这时候,如果对爬虫的执行效率没有太多要求,又不想在理解JavaScript代码逻辑和寻找Ajax请求链接上浪费太多时间,可以尝试另一种方式——呈现动态网页。

  浏览器渲染引擎:简介:

  在介绍这个方法之前,我们需要了解一些浏览器渲染引擎的基础知识。

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

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

  主要渲染流程:

  

  思考:

  了解了浏览器渲染引擎的基本原理,我们可以发现:

  然后我们有了爬取动态网页的新思路:

  渲染动态网页:

  有两种选择:

  自己从头实现一个浏览器渲染引擎,并在合适的时间返回构造好的dom树或渲染树:

  这需要大量的工作,需要考虑html、js、css等不同格式文件的解析方式和解析顺序。

  我参考以下资料试了一下,最后没有成功。如果你有兴趣,你可以尝试一下。

  使用现有的渲染引擎。

  接下来会用到WebKit渲染引擎,可以通过python库PySide获取到引擎的便捷接口。

  例子:

  以新浪读书文摘为例,我们可以发现页面上的文章列表部分是动态加载的。

  

  使用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已经收录了动态加载的内容。

  

# 根据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 的更多信息,请看这里:PySide 官方文档

  但是PySide毕竟是为Python GUI编程开发的,它的功能对于爬虫来说太大了,所以我们可以把爬虫经常用到的功能封装起来,提高编写爬虫的效率。

  PySide-ghost.py常用函数的封装

  目前是PySide的一个封装模块,针对爬虫,功能比较齐全。可以用来方便的进行数据采集

  或者以获取列表页中每个文章详情页的地址为目标,直接看示例代码:

  # 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)')

self.parselist()

while self.count < self.total:

if self.click_times is 0:

# 点击加载更多

self.ss.click(&#39;#subShowContent1_loadMore&#39;)

# 每次翻页,或加载更多,要等待至加载完成

self.ss.wait_for_selector(&#39;#subShowContent1_static > div:nth-child(21)&#39;)

self.click_times += 1

self.parselist()

elif self.click_times is 1:

self.ss.click(&#39;#subShowContent1_loadMore&#39;)

self.ss.wait_for_selector(&#39;#subShowContent1_static > div:nth-child(41)&#39;)

self.click_times += 1

self.parselist()

elif self.click_times is 2:

self.ss.click(&#39;#subShowContent1_page .pagebox_next a&#39;)

self.ss.sleep(2)

self.click_times = 0

self.location = 0

self.parselist()

def parselist(self):

"""

解析列表页

:return:

"""

html = self.ss.content.encode(&#39;utf8&#39;)

# print html

pattern = re.compile(r&#39;<a href="(.*?)" target="_blank">&#39;, re.M)

links = pattern.findall(html)

for i in range(self.location, len(links)):

print links[i]

self.count += 1

self.location += 1

print self.count

if __name__ == &#39;__main__&#39;:

spider = SinaBookSpider()

spider.run()

  代码地址:dynamic-web-process —— GitHub

  添加:

  文章中的任何错误或不足欢迎指出!

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线