客户端脚本语言是运行在浏览器而非服务器上的语言。
通常,你在网上遇到的客户端语言只有两种: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 里了,说明这时网站已经跳转