在写爬虫的时候,关于JavaScript的解析问题,我在网上找到的一个解决方案是使用ghost.py这个模块,他是一个基于webkit封装的一个客户端,可以用来解析动态页面。它的使用非常简单,它从2.x版本开始,变化就有点大了,在这我主要是针对他的1.0版本。

首先在GitHub上克隆它,然后在对应的文件中执行python setup.py install命令,这样就可以安装了,注意在这不要直接使用pip,使用pip会默认安装2.x版本。

安装完成后,可以编写如下代码来加载一个网页:

  1. from ghost import Ghost
  2. gh = Ghost(display = True, wait_timeout = 60)
  3. page, res = gh.open(url)
  4. for item in res:
  5. print item.url

这段代码可以打印在加载页面时,webkit向远程服务器请求了那些资源。对于AJAX请求来说,使用这个特性非常方便的就可以获取到对应的url

它在里面提供了一些特定的方法用来处理页面的事件,比如鼠标单击某个标签时调用click,通过阅读它的源代码可以知道针对这些事件的处理,它调用的是JavaScript代码,比如说click事件,click事件的源码如下

  1. @client_utils_required
  2. @can_load_page
  3. def click(self, selector):
  4. """Click the targeted element.
  5. :param selector: A CSS3 selector to targeted element.
  6. """
  7. if not self.exists(selector):
  8. raise Exception("Can't find element to click")
  9. return self.evaluate('GhostUtils.click("%s");' % selector)

它上面的两个装饰器的代码分别如下:

  1. def can_load_page(func):
  2. """Decorator that specifies if user can expect page loading from
  3. this action. If expect_loading is set to True, ghost will wait
  4. for page_loaded event.
  5. """
  6. @wraps(func)
  7. def wrapper(self, *args, **kwargs):
  8. expect_loading = False
  9. if 'expect_loading' in kwargs:
  10. expect_loading = kwargs['expect_loading']
  11. del kwargs['expect_loading']
  12. if expect_loading:
  13. self.loaded = False
  14. func(self, *args, **kwargs)
  15. return self.wait_for_page_loaded()
  16. return func(self, *args, **kwargs)
  17. return wrapper
  18. def client_utils_required(func):
  19. """Decorator that checks avabality of Ghost client side utils,
  20. injects require javascript file instead.
  21. """
  22. @wraps(func)
  23. def wrapper(self, *args, **kwargs):
  24. if not self.global_exists('GhostUtils'):
  25. self.evaluate_js_file(
  26. os.path.join(os.path.dirname(__file__), 'utils.js'))
  27. return func(self, *args, **kwargs)
  28. return wrapper

函数can_load_page是用来判断用户是否需要进行等待,等待的条件是页面加载完毕,在阅读它的源代码时可以知道,它自身给webkit注册了几个槽函数,一个用来处理页面开始加载的信息,一个用来处理页面加载结束的信息,在加载时将一个bool变量设置为true,加载结束时设置为false,另外在返回前调用等待函数,等待函数主要判断这个bool变量是否为false,为false则返回,否则就继续循环。这样当页面加载完毕后,就可以返回,同样的,这个can_load_page函数就是在执行JavaScript期间进行等待。直到页面加载完成后返回(当然,是否需要等待就看我们是否传入expect_load这个参数了,它默认是False,即不等待)

client_utils_required函数主要负责读取utils.js这个文件中的JavaScript代码并执行它,这个文件中代码都是函数,在这所谓的执行只是为了将其加载到内存,准备随时调用。

根据以上所说,大概能组织一下执行click函数时经历的步骤了:首先会调用client_utils_required函数,将对应的JavaScript函数代码加载起来,然后判断是否需要等待,如果需要等待将设置对应等待变量的值,然后真正调用对应的JavaScript函数来进行元素的点击,然后调用等待函数,如果需要等待,则会等待到新页面加载,否则直接返回,这样就完成了一个点击事件。根据这些我们扩展它的功能,从click函数的定义来看,它需要传入一个css选择器,但是我遇到的场景是我希望通过JavaScript得到的页面的dom元素,根据它的下标来进行点击,比如说

  1. document.getElementsByTagName("a")[3];

我通过上面的代码获取到了这个元素,我现在要点击这个元素,自然不能直接调用click函数,ghost中也没有对应的函数可以使用,这个时候就需要我们进行扩展。当时我给出的代码入下:

  1. @client_utils_required
  2. @can_load_page
  3. def js_click(self, jscontent): #jscontent使用js来定位元素的代码
  4. return self.evaluate('GhostUtils.jsclick("%s");' % jscontent);

然后来扩展utils.js文件,在里面新加一个对应的函数jsclick

  1. jsclick: function(jscontent) {
  2. var elem = eval(jscontent);
  3. if (!elem) {
  4. return false;
  5. }
  6. var evt = document.createEvent("MouseEvents");
  7. evt.initMouseEvent("click", true, true, window, 1, 1, 1, 1, 1,
  8. false, false, false, false, 0, elem);
  9. if (elem.dispatchEvent(evt)) {
  10. return true;
  11. }
  12. return false;
  13. }

但是我在这发现,它可以调用成功的点击,但是超时率比较高,几乎达到了70%以上,这个问题一直使我困惑,后来我仔细阅读源代码后发现,问题出在expect_loading = True,也就是让其等待页面加载完毕。有很多页面都是使用AJAX技术的,它只是改变页面的状态而不会重新加载,这样自然那个等待函数不会返回,当时间一到自然也就超时了,但是如果不加这个参数,让他立即返回,那么我们就得不到请求的url,而在webkit中也没有办法判断一个JavaScript代码是否执行完毕,所以在这我采取了一个折中的方案,每次等待1s,所以将上面的jsclick函数改为:

  1. @client_utils_required
  2. def js_click(self, jscontent): #jscontent使用js来定位元素的代码
  3. return self.main_frame.evaluateJavaScript('GhostUtils.jsclick("%s");' % jscontent); #执行js函数
  4. for i in range(0, 100):
  5. time.sleep(0.01)
  6. Ghost._app.processEvents() #在等待的时候让QT的信号槽机制仍然运转

这样可能会有一定的性能损失,但是目前我只能想到这个方案。

ghost.py在代用JavaScript时的超时问题的更多相关文章

  1. python ghost.py使用笔记

    ghost.py目前已更新到0.2版本,变化有点大,使用方法上跟0.1还是有点差别的,本文仅以0.1.1版本为例,因为我安装的是这个版本 我用ghost主要用来模拟在网站上的操作,比如登录之类的,当然 ...

  2. Ghost.py 0.1b3 : Python Package Index

    Ghost.py 0.1b3 : Python Package Index Ghost.py 0.1b3 Download Ghost.py-0.1b3.tar.gz Webkit based web ...

  3. 解决Eclipse编辑JavaScript时卡的问题

    eclipse在开发JavaEE项目时容易卡,特别是在编辑JavaScript时,经过网上各种搜索,综合整理一下,对自己的eclipse设置之后,结果不在出现卡的问题了. 原文地址:http://bl ...

  4. C#用WebClient下载File时操作超时的问题

    原文:C#用WebClient下载File时操作超时的问题 今天很SB,被这个问题卡住了.那段代码也是网上找的.结果发现只能下载一个文件,第二次下载的时候就会出现“操作超时”的问题. 这个是原代码: ...

  5. WPF 窗体中获取键盘和鼠标无操作时的超时提示

    原文:WPF 窗体中获取键盘和鼠标无操作时的超时提示 通过调用Windows API中的GetLastInputInfo来获取最后一次输入的时间 , , );            timer.Tic ...

  6. 学习JavaScript时的三部分

    JavaScript = ECMAScript + DOM + BOM 其中ECMAScript表示的是基本语法,包括我们实现JS的基本语法,如变量的声明.基本的语句(if.for.switch等) ...

  7. os.popen('python hello_out.py')中Python程序执行时默认的当前路径为MS-DOS CMD的默认路径

    >>> import os >>> os.getcwd() 'D:\\pythonCode\\pp4e' >>> os.chdir('Stream ...

  8. 使用JavaScript时要注意的7个要素

    每种语言都有它特别的地方,对于JavaScript来说,使用var就可以声明任意类型的变量,这门脚本语言看起来很简单,然而想要写出优雅的代码却是需要不断积累经验的.本文利列举了JavaScript初学 ...

  9. 代做Assignment时排比结构的使用解析

    排比句式的作用想必各位留学生都不陌生,同理,在英文写作中,不管是从形式还是内容上来说,排比结构的作用都是强调.但是要注意,不能在分析的时候用太多这种套话,尽量还是能够根据具体情况具体分析.静态,小编将 ...

随机推荐

  1. M03 利用Accord 进行机器学习的第一个小例子

    01 安装 Visual studio 2017. 不具备安装这个的话,也可安装,Microsoft Visual Studio Express (or equivalent) 02 创建 C# 的 ...

  2. EL表达式的简单实用

    EL表达式 EL(Expression Language) 是为了使JSP写起来更加简单.表达式语言的灵感来自于 ECMAScript 和 XPath 表达式语言,它提供了在 JSP 中简化表达式的方 ...

  3. 自学Python5.4-内置模块(2)

    内置模块(2) 7. xml8.conf9.requests10.logging11.paramiko12.time & datetime 时间相关的操作,时间主要分三种表示方式: 时间戳   ...

  4. iOS动态性:动态添加属性的方法——关联(e.g. 向Category添加属性)

    想到要如何为所有的对象增加实例变量吗?我们知道,使用Category可以很方便地为现有的类增加方法,但却无法直接增加实例变量.不过从Mac OS X v10.6开始,系统提供了Associative ...

  5. iOS手机截屏使用

    .截屏 保存 .data //登录成功进行截屏 //截取屏幕大小 UIGraphicsBeginImageContext([[UIScreen mainScreen]bounds].size); [s ...

  6. 【NOIP模拟】cut

    Description F大爷热爱切树.今天他找到一棵黑白树,不到两秒钟,F大爷就把这棵树切掉了.已知原先树上共n个点,每个点都是黑点或者白点,F大爷切去若干条边后,分成的若干个连通子树中每块恰有一个 ...

  7. Wincc flexable的局势视图的组态

    1.趋势视图介绍 2.实时趋势视图的组态 1)创建连接和变量 2)开始组态局势视图 3)设置趋势视图的属性,添加一个趋势 3.模拟运行HMI,观察局势图

  8. Time Complexity of Loop with Powers

    以下功能的时间复杂度是多少? void fun(int n, int k) { for (int i=1; i<=n; i++) { int p = pow(i, k); for (int j= ...

  9. bzoj 3996: [TJOI2015]线性代数

    Description 给出一个N*N的矩阵B和一个1*N的矩阵C.求出一个1*N的01矩阵A.使得 D=(A*B-C)*A^T最大.其中A^T为A的转置.输出D Input 第一行输入一个整数N,接 ...

  10. Linux Centos7配置防火墙开启端口

    在使用centos7安装完mysql.tomcat.nginx后,都需要配置防火墙才能正常访问. 下面系统的学习一下防火墙的配置. centos7默认使用firewall,需要关闭,然后使用iptab ...