第 10 章 采集 JavaScript

客户端脚本语言是运行在浏览器而非服务器上的语言。
通常,你在网上遇到的客户端语言只有两种:ActionScript(开发 Flash 应用的语言)和 JavaScript。ActionScript 经常用于流媒体文件播放,用作在线游戏平台,或者是网站上的“介绍”页面。
JavaScript 是网络上最常用也是支持者最多的客户端脚本语言。它可以收集用户的跟踪数据,不需要重载页面直接提交表单,在页面嵌入多媒体文件,甚至运行网页游戏。那些看起来非常简单的页面背后通常使用了许多 JavaScript 文件。你可以在网页源代码的 <script> 标签之间看到它们:

<script>
    alert("This creates a pop-up using JavaScript");
</script>

10.1 JavaScript 简介

JavaScript 是一种弱类型语言,其语法通常可以与 C++ 和 Java 做对比。虽然语法中的一些元素,比如操作符、循环条件和数组,都与 C++、Java 语法很接近,但是 JavaScript 的弱类型和脚本形式被一些程序员看成是折磨人的怪兽。
注意 JavaScript 里所有的变量都用 var 关键词字进行定义。这与 PHP 里的 $ 符号类似,或 者 Java 和 C++ 里的类型声明(int、String、List 等)。Python 不太一样,它没有这种显 式的变量声明。
JavaScript 还有一个非常好的特性,就是把函数作为变量使用:

<script>
var fibonacci = function() {
    var a = 1;
    var b = 1;
    return function() {
        var temp = b;
        b = a + b;
        a = temp;
        return b;
    }
}
var fibInstance = fibonacci();
console.log(fibInstance()+" is in the Fibonacci sequence");
console.log(fibInstance()+" is in the Fibonacci sequence");
console.log(fibInstance()+" is in the Fibonacci sequence");
</script>

常用JavaScript库
用 Python 执行 JavaScript 代码的效率非常低,既费时又费力,尤其是在处理规模较大的 JavaScript 代码时。如果有绕过 JavaScript 并直接解析它的方法(不需要执行它就可以获得 信息)会非常实用,可以帮你避开一大堆 JavaScript 的麻烦事。
1. jQuery
jQuery 是一个十分常见的库,70% 最流行的网站(约 200 万)和约 30% 的其他网站(约 2 亿)都在使用。一个网站使用 jQuery 的特征,就是源代码里包含了 jQuery 入口,比如:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></ script>

jQuery 可以动态地创建 HTML 内容,只有在 JavaScript 代码执行之后才会显示。
另外,这些页面还可能包含动画、用户交互内容和嵌入式媒体,这些内容对网络数据采集都是挑战。
2. Google Analytics
有一半的网站都在用 Google Analytics,它可能是网站最常用的 JavaScript 库和最受欢迎的用户跟踪工具。
很容易判断一个页面是不是使用了 Google Analytics。如果网站使用了它,在页面底部会有 类似如下所示的 JavaScript 代码(取自 O’Reilly Media 网站):

<!-- Google Analytics -->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-4591498-1']);
_gaq.push(['_setDomainName', 'oreilly.com']);
_gaq.push(['_addIgnoredRef', 'oreilly.com']);
_gaq.push(['_setSiteSpeedSampleRate', 50]);
_gaq.push(['_trackPageview']);

(function() { var ga = document.createElement('script');
ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s); })();

</script>

如果一个网站使用了 Google Analytics 或其他类似的网络分析系统,而你不想让网站知道你在采集数据,就要确保把那些分析工具的 cookie 或者所有 cookie 都关掉。
3. Google地图
采集任何的位置数据,理解 Google 地图的工作方式可以让你轻松地获取格式规
范的经纬度坐标和具体地址。在 Google 地图上,显示一个位置最常用的方法就是用标记 (一个大头针)。
标记可以用下面的代码插在 Google 地图上:

var marker = new google.maps.Marker({
    position: new google.maps.LatLng(-25.363882,131.044922), 
    map: map,
    title: 'Some marker text'
});

Python 可以轻松地抽取出所有位置在 google.maps.LatLng() 里的坐标,生成一组经 / 纬度坐标值。
通过 Google 的“地理坐标反向查询”API,你可以把这些经纬度坐标组解析成格式规范的地址,便于存储和分析。

10.2 Ajax 和动态 HTML

那些使用了 Ajax 或 DHTML 技术改变 / 加载内容的页面,可能有一些采集手段,但是用 Python 解决这个问题只有两种途径:直接从 JavaScript 代码里采集内容,或者用 Python 的第三方库运行 JavaScript,直接采集你在浏览器里看到的页面。
在Python中用Selenium执行JavaScript
Selenium 是一个强大的网络数据采集工具,其最初是为网站自动化测试而开发的。近几年,它还被广泛用于获取精确的网站快照,因为它们可以直接运行在浏览器上。Selenium 可以让浏览器自动加载页面,获取需要的数据,甚至页面截屏,或者判断网站上某些动作是否发生。
Selenium 不带浏览器,需要与第三方浏览器结合使用。
PhantomJS 是一个“无头”(headless)浏览器。它会把网站加载到内存并执行页面上的 JavaScript,但是它不会向用户展示网页的图形界面。把 Selenium 和 PhantomJS 结合在一 起,就可以运行一个非常强大的网络爬虫了,可以处理 cookie、JavaScrip、header,以及 任何你需要做的事情。
你可以从 PyPI 网站下载 Selenium 库,也可以用第三方管理器像 pip 用命令行安装。
PhantomJS 官方下载 , PhantomJS 是一个功能完善(虽然无头)的浏览器,并非一个 Python 库,不需要像 Python 的其他库一样安装,也不能用 pip 安装。
Selenium 库是一个在 WebDriver 上调用的 API。WebDriver 像可以加载网站的浏览器,也可以像 BeautifulSoup 对象一样用来查找页面元素,与页面上的元素进行交互 (发送文本、点击等),以及执行其他动作来运行网络爬虫。
WebDriverWait 和 expected_conditions, 这两个模块组合起来构成了 Selenium 的隐式等待(implicit wait)。
隐式等待与显式等待的不同之处:隐式等待是等 DOM 中某个状态发生后再继续运行 代码(没有明确的等待时间,但是有最大等待时限,只要在时限内就可以),而显式等待 明确设置了等待时间。
在 Selenium 库里面元素被触发的期望条件(expected condition)有很多种,包括:
• 弹出一个提示框
• 一个元素被选中(比如文本框)
• 页面的标题改变了,或者某个文字显示在页面上或者某个元素里
• 一个元素在 DOM 中变成可见的,或者一个元素从 DOM 中消失了
下面是定位器通过 By 对象进行选择的策略。
• ID
在上面的例子里用过;通过 HTML 的 id 属性查找元素。
• CLASS_NAME
通过 HTML 的 class 属性来查找元素。为什么这个函数是 CLASS_NAME,而不是简单的 CLASS ?在 Selenium 的 Java 库里使用 object.CLASS 可能会出现问题,.class 是 Java 保留的一个方法。为了让 Selenium 语法可以兼容不同的语言,就用 CLASS_NAME 代替。
• CSS_SELECTOR
通过 CSS 的 class、id、tag 属性名来查找元素,用 #idName、.className、tagName 表示。
• LINK_TEXT
通过链接文字查找 HTML 的 <a> 标签。例如,如果一个链接的文字是“Next”,就可以 用 (By.LINK_TEXT, "Next") 来选择。
• PARTIAL_LINK_TEXT
与 LINK_TEXT 类似,只是通过部分链接文字来查找。
• NAME
通过 HTML 标签的 name 属性查找。这在处理 HTML 表单时非常方便。
• TAG_NAME
通过 HTML 标签的名称查找。
• XPATH
用 XPath 表达式(语法在下面介绍)选择匹配的元素。
XPath 语法
XPath(XML Path,XML 路径)是在 XML 文档中导航和选择元素的查询语言。它由 W3C 于 1999 年创建,在 Python、Java 和 C# 这些语言中有时会用 XPath 来处理 XML 文档。
虽然 BeautifulSoup 不支持 XPath,但是很多库(lxml、Selenium、Scrapy 等) 都支持。它的使用方式通常和 CSS 选择器(比如 mytag#idname)一样,它原本被设计用于处理更规范的 XML 文档而不是 HTML 文档。
在 XPath 语法中有四个重要概念。
• 根节点和非根节点
♦ /div 选择 div 节点,只有当它是文档的根节点时
♦ //div 选择文档中所有的 div 节点(包括非根节点)
• 通过属性选择节点
♦ //@href 选择带 href 属性的所有节点
♦ //a[@href='http://google.com'] 选择页面中所有指向 Google 网站的链接
• 通过位置选择节点
♦ //a[3] 选择文档中的第三个链接
♦ //table[last()] 选择文档中的最后一个表
♦ //a[position() < 3] 选择文档中的前三个链接
• 星号(*)匹配任意字符或节点,可以在不同条件下使用
♦ //table/tr/* 选择所有表格行 tr 标签的所有的子节点(这很适合选择 th 和 td 标签)
♦ //div[@*] 选择带任意属性的所有 div 标签
如果这里介绍的几个 XPath 功能解决不了你的 HTML 或 XML 元素选择问题,请参考微软的 XPath 语法页面

10.3 处理重定向

客户端重定向是在服务器将页面内容发送到浏览器之前,由浏览器执行 JavaScript 完成的页面跳转,而不是服务器完成的跳转。当使用浏览器访问页面的时候,有时很难区分这两种重定向。由于客户端重定向执行很快,加载页面时你甚至感觉不到任何延迟,所以会让你觉得这个重定向就是一个服务器端重定向。
但是,在进行网络数据采集的时候,这两种重定向的差异是非常明显的。根据具体情况,服务器端重定向一般都可以轻松地通过 Python 的 urllib 库解决,不需要使用 Selenium。客户端重定向却不能这样处理,除非你有工具可以执行 JavaScript。
Selenium 可以执行这种 JavaScript 重定向,和它处理其他 JavaScript 的方式一样;但是这类重定向的主要问题是什么时候停止页面监控,也就是说,怎么识别一个页面已经完成重定向。
我们可以用一种智能的方法来检测客户端重定向是否完成,首先从页面开始加载时就“监视”DOM 中的一个元素,然后重复调用这个元素直到 Selenium 抛出一个 StaleElementReferenceException 异常;也就是说,元素不在页面的 DOM 里了,说明这时网站已经跳转

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • 这篇文章在介绍官网的同时使用了比较多的脚本示例,示例里遇到的问题有部分在本篇文章进行了解释,还有一篇文章专门记录了...
    顾顾314阅读 12,897评论 3 32
  • 洞见SELENIUM自动化测试 写在最前面:目前自动化测试并不属于新鲜的事物,或者说自动化测试的各种方法论已经层出...
    厲铆兄阅读 6,706评论 3 47
  • Selenium 官网Selenium WebDriver官网webdriver实用指南python版本 WebD...
    顾顾314阅读 46,952评论 0 34
  • Selenium是一款强大的基于浏览器的开源自动化测试工具,最初由 Jason Huggins 于 2004 年在...
    FifiZhuang阅读 7,573评论 5 71
  • "去你大爷的老子辞职了!"陈好天没等刚上任火势正猛的领导喝口茶说话直接撂了这句话拍门而去。留下了张大嘴巴端着茶杯的...
    妙妙猫戴帽子阅读 232评论 0 1