0%

Python

我们可以把互联网比作一张大网,而爬虫(即网络爬虫)便是在网上爬行的蜘蛛。把网的节点比作一个个网页,爬虫爬到这就相当于访问了该页面,获取了其信息。可以把节点间的连线比作网页与网页之间的链接关系,这样蜘蛛通过一个节点后,可以顺着节点连线继续爬行到达下一个节点,即通过一个网页继续获取后续的网页,这样整个网的节点便可以被蜘蛛全部爬行到,网站的数据就可以被抓取下来了。

1. 爬虫概述

简单来说,爬虫就是获取网页并提取和保存信息的自动化程序,下面概要介绍一下。

(1) 获取网页

爬虫首先要做的工作就是获取网页,这里就是获取网页的源代码。源代码里包含了网页的部分有用信息,所以只要把源代码获取下来,就可以从中提取想要的信息了。

前面讲了请求和响应的概念,向网站的服务器发送一个请求,返回的响应体便是网页源代码。所以,最关键的部分就是构造一个请求并发送给服务器,然后接收到响应并将其解析出来,那么这个流程怎样实现呢?总不能手工去截取网页源码吧?

不用担心,Python提供了许多库来帮助我们实现这个操作,如urllib、requests等。我们可以用这些库来帮助我们实现HTTP请求操作,请求和响应都可以用类库提供的数据结构来表示,得到响应之后只需要解析数据结构中的Body部分即可,即得到网页的源代码,这样我们可以用程序来实现获取网页的过程了。

(2) 提取信息

获取网页源代码后,接下来就是分析网页源代码,从中提取我们想要的数据。首先,最通用的方法便是采用正则表达式提取,这是一个万能的方法,但是在构造正则表达式时比较复杂且容易出错。

另外,由于网页的结构有一定的规则,所以还有一些根据网页节点属性、CSS选择器或XPath来提取网页信息的库,如Beautiful Soup、pyquery、lxml等。使用这些库,我们可以高效快速地从中提取网页信息,如节点的属性、文本值等。

提取信息是爬虫非常重要的部分,它可以使杂乱的数据变得条理清晰,以便我们后续处理和分析数据。

(3) 保存数据

提取信息后,我们一般会将提取到的数据保存到某处以便后续使用。这里保存形式有多种多样,如可以简单保存为TXT文本或JSON文本,也可以保存到数据库,如MySQL和MongoDB等,也可保存至远程服务器,如借助SFTP进行操作等。

(4) 自动化程序

说到自动化程序,意思是说爬虫可以代替人来完成这些操作。首先,我们手工当然可以提取这些信息,但是当量特别大或者想快速获取大量数据的话,肯定还是要借助程序。爬虫就是代替我们来完成这份爬取工作的自动化程序,它可以在抓取过程中进行各种异常处理、错误重试等操作,确保爬取持续高效地运行。

2. 能抓怎样的数据

在网页中我们能看到各种各样的信息,最常见的便是常规网页,它们对应着HTML代码,而最常抓取的便是HTML源代码。

另外,可能有些网页返回的不是HTML代码,而是一个JSON字符串(其中API接口大多采用这样的形式),这种格式的数据方便传输和解析,它们同样可以抓取,而且数据提取更加方便。

此外,我们还可以看到各种二进制数据,如图片、视频和音频等。利用爬虫,我们可以将这些二进制数据抓取下来,然后保存成对应的文件名。

另外,还可以看到各种扩展名的文件,如CSS、JavaScript和配置文件等,这些其实也是最普通的文件,只要在浏览器里面可以访问到,就可以将其抓取下来。

上述内容其实都对应各自的URL,是基于HTTP或HTTPS协议的,只要是这种数据,爬虫都可以抓取。

3. JavaScript渲染页面

有时候,我们在用urllib或requests抓取网页时,得到的源代码实际和浏览器中看到的不一样。

这是一个非常常见的问题。现在网页越来越多地采用Ajax、前端模块化工具来构建,整个网页可能都是由JavaScript渲染出来的,也就是说原始的HTML代码就是一个空壳,例如:

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>This is a Demo</title>
</head>
<body>
<div id="container">
</div>
</body>
<script src="app.js"></script>
</html>

body节点里面只有一个idcontainer的节点,但是需要注意在body节点后引入了app.js,它便负责整个网站的渲染。

在浏览器中打开这个页面时,首先会加载这个HTML内容,接着浏览器会发现其中引入了一个app.js文件,然后便会接着去请求这个文件,获取到该文件后,便会执行其中的JavaScript代码,而JavaScript则会改变HTML中的节点,向其添加内容,最后得到完整的页面。

但是在用urllib或requests等库请求当前页面时,我们得到的只是这个HTML代码,它不会帮助我们去继续加载这个JavaScript文件,这样也就看不到浏览器中的内容了。

这也解释了为什么有时我们得到的源代码和浏览器中看到的不一样。

因此,使用基本HTTP请求库得到的源代码可能跟浏览器中的页面源代码不太一样。对于这样的情况,我们可以分析其后台Ajax接口,也可使用Selenium、Splash这样的库来实现模拟JavaScript渲染。

后面,我们会详细介绍如何采集JavaScript渲染的网页。

本节介绍了爬虫的一些基本原理,这可以帮助我们在后面编写爬虫时更加得心应手。

Python

用浏览器访问网站时,页面各不相同,你有没有想过它为何会呈现这个样子呢?本节中,我们就来了解一下网页的基本组成、结构和节点等内容。

1. 网页的组成

网页可以分为三大部分——HTML、CSS 和 JavaScript。如果把网页比作一个人的话,HTML 相当于骨架,JavaScript 相当于肌肉,CSS 相当于皮肤,三者结合起来才能形成一个完善的网页。下面我们分别来介绍一下这三部分的功能。

(1) HTML

HTML 是用来描述网页的一种语言,其全称叫作 Hyper Text Markup Language,即超文本标记语言。网页包括文字、按钮、图片和视频等各种复杂的元素,其基础架构就是 HTML。不同类型的文字通过不同类型的标签来表示,如图片用img标签表示,视频用video标签表示,段落用p标签表示,它们之间的布局又常通过布局标签div嵌套组合而成,各种标签通过不同的排列和嵌套才形成了网页的框架。

在 Chrome 浏览器中打开百度,右击并选择“检查”项(或按 F12 键),打开开发者模式,这时在 Elements 选项卡中即可看到网页的源代码,如图 2-9 所示。

图 2-9 源代码

这就是 HTML,整个网页就是由各种标签嵌套组合而成的。这些标签定义的节点元素相互嵌套和组合形成了复杂的层次关系,就形成了网页的架构。

(2) CSS

HTML 定义了网页的结构,但是只有 HTML 页面的布局并不美观,可能只是简单的节点元素的排列,为了让网页看起来更好看一些,这里借助了 CSS。

CSS,全称叫作 Cascading Style Sheets,即层叠样式表。“层叠”是指当在 HTML 中引用了数个样式文件,并且样式发生冲突时,浏览器能依据层叠顺序处理。“样式”指网页中文字大小、颜色、元素间距、排列等格式。

CSS 是目前唯一的网页页面排版样式标准,有了它的帮助,页面才会变得更为美观。

图 2-9 的右侧即为 CSS,例如:

1
2
3
4
5
6
#head_wrapper.s-ps-islite .s-p-top {
position: absolute;
bottom: 40px;
width: 100%;
height: 181px;
}

就是一个 CSS 样式。大括号前面是一个 CSS 选择器,此选择器的意思是首先选中idhead_wrapperclasss-ps-islite的节点,然后再选中其内部的classs-p-top的节点。大括号内部写的就是一条条样式规则,例如position指定了这个元素的布局方式为绝对布局,bottom指定元素的下边距为 40 像素,width指定了宽度为 100%占满父元素,height则指定了元素的高度。也就是说,我们将位置、宽度、高度等样式配置统一写成这样的形式,然后用大括号括起来,接着在开头再加上 CSS 选择器,这就代表这个样式对 CSS 选择器选中的元素生效,元素就会根据此样式来展示了。

在网页中,一般会统一定义整个网页的样式规则,并写入 CSS 文件中(其后缀为 css)。在 HTML 中,只需要用link标签即可引入写好的 CSS 文件,这样整个页面就会变得美观、优雅。

(3) JavaScript

JavaScript,简称 JS,是一种脚本语言。HTML 和 CSS 配合使用,提供给用户的只是一种静态信息,缺乏交互性。我们在网页里可能会看到一些交互和动画效果,如下载进度条、提示框、轮播图等,这通常就是 JavaScript 的功劳。它的出现使得用户与信息之间不只是一种浏览与显示的关系,而是实现了一种实时、动态、交互的页面功能。

JavaScript 通常也是以单独的文件形式加载的,后缀为 js,在 HTML 中通过script标签即可引入,例如:

1
<script src="jquery-2.1.0.js"></script>

综上所述,HTML 定义了网页的内容和结构,CSS 描述了网页的布局,JavaScript 定义了网页的行为。

2. 网页的结构

我们首先用例子来感受一下 HTML 的基本结构。新建一个文本文件,名称可以自取,后缀为 html,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>This is a Demo</title>
</head>
<body>
<div id="container">
<div class="wrapper">
<h2 class="title">Hello World</h2>
<p class="text">Hello, this is a paragraph.</p>
</div>
</div>
</body>
</html>

这就是一个最简单的 HTML 实例。开头用DOCTYPE定义了文档类型,其次最外层是html标签,最后还有对应的结束标签来表示闭合,其内部是head标签和body标签,分别代表网页头和网页体,它们也需要结束标签。head标签内定义了一些页面的配置和引用,如:

1
<meta charset="UTF-8">

它指定了网页的编码为 UTF-8。

title标签则定义了网页的标题,会显示在网页的选项卡中,不会显示在正文中。body标签内则是在网页正文中显示的内容。div标签定义了网页中的区块,它的idcontainer,这是一个非常常用的属性,且id的内容在网页中是唯一的,我们可以通过它来获取这个区块。然后在此区块内又有一个div标签,它的classwrapper,这也是一个非常常用的属性,经常与 CSS 配合使用来设定样式。然后此区块内部又有一个h2标签,这代表一个二级标题。另外,还有一个p标签,这代表一个段落。在这两者中直接写入相应的内容即可在网页中呈现出来,它们也有各自的class属性。

将代码保存后,在浏览器中打开该文件,可以看到如图 2-10 所示的内容。

图 2-10 运行结果

可以看到,在选项卡上显示了 This is a Demo 字样,这是我们在head中的title里定义的文字。而网页正文是body标签内部定义的各个元素生成的,可以看到这里显示了二级标题和段落。

这个实例便是网页的一般结构。一个网页的标准形式是html标签内嵌套headbody标签,head内定义网页的配置和引用,body内定义网页的正文。

3. 节点树及节点间的关系

在 HTML 中,所有标签定义的内容都是节点,它们构成了一个 HTML DOM 树。

我们先看下什么是 DOM,DOM 是 W3C(万维网联盟)的标准,其英文全称 Document Object Model,即文档对象模型。它定义了访问 HTML 和 XML 文档的标准:

W3C 文档对象模型(DOM)是中立于平台和语言的接口,它允许程序和脚本动态地访问和更新文档的内容、结构和样式。

W3C DOM 标准被分为 3 个不同的部分。

  • 核心 DOM: 针对任何结构化文档的标准模型。
  • XML DOM:针对 XML 文档的标准模型。
  • HTML DOM:针对 HTML 文档的标准模型。

根据 W3C 的 HTML DOM 标准,HTML 文档中的所有内容都是节点。

  • 整个文档是一个文档节点;
  • 每个 HTML 元素是元素节点;
  • HTML 元素内的文本是文本节点;
  • 每个 HTML 属性是属性节点;
  • 注释是注释节点。

HTML DOM 将 HTML 文档视作树结构,这种结构被称为节点树,如图 2-11 所示。

图 2-11 节点树

通过 HTML DOM,树中的所有节点均可通过 JavaScript 访问,所有 HTML 节点元素均可被修改,也可以被创建或删除。

节点树中的节点彼此拥有层级关系。我们常用父(parent)、子(child)和兄弟(sibling)等术语描述这些关系。父节点拥有子节点,同级的子节点被称为兄弟节点。

在节点树中,顶端节点称为根(root)。除了根节点之外,每个节点都有父节点,同时可拥有任意数量的子节点或兄弟节点。图 2-12 展示了节点树以及节点之间的关系。

图 2-12 节点树及节点间的关系

本段参考 W3SCHOOL,链接:http://www.w3school.com.cn/htmldom/dom_nodes.asp

4. 选择器

我们知道网页由一个个节点组成,CSS 选择器会根据不同的节点设置不同的样式规则,那么怎样来定位节点呢?

在 CSS 中,我们使用 CSS 选择器来定位节点。例如,上例中div节点的idcontainer,那么就可以表示为#container,其中#开头代表选择id,其后紧跟id的名称。另外,如果我们想选择classwrapper的节点,便可以使用.wrapper,这里以点(.)开头代表选择class,其后紧跟class的名称。另外,还有一种选择方式,那就是根据标签名筛选,例如想选择二级标题,直接用h2即可。这是最常用的 3 种表示,分别是根据idclass、标签名筛选,请牢记它们的写法。

另外,CSS 选择器还支持嵌套选择,各个选择器之间加上空格分隔开便可以代表嵌套关系,如#container .wrapper p则代表先选择idcontainer的节点,然后选中其内部的classwrapper的节点,然后再进一步选中其内部的p节点。另外,如果不加空格,则代表并列关系,如div#container .wrapper p.text代表先选择idcontainerdiv节点,然后选中其内部的classwrapper的节点,再进一步选中其内部的classtextp节点。这就是 CSS 选择器,其筛选功能还是非常强大的。

另外,CSS 选择器还有一些其他语法规则,具体如表 2-4 所示。

表 2-4 CSS 选择器的其他语法规则

选择器

例子

例子描述

.class

.intro

选择class="intro"的所有节点

#id

#firstname

选择id="firstname"的所有节点

*

*

选择所有节点

element

p

选择所有p节点

element,element

div,p

选择所有div节点和所有p节点

element element

div p

选择div节点内部的所有p节点

element>element

div>p

选择父节点为div节点的所有p节点

element+element

div+p

选择紧接在div节点之后的所有p节点

[attribute]

[target]

选择带有target属性的所有节点

[attribute=value]

[target=blank]

选择target="blank"的所有节点

[attribute~=value]

[title~=flower]

选择title属性包含单词flower的所有节点

:link

a:link

选择所有未被访问的链接

:visited

a:visited

选择所有已被访问的链接

:active

a:active

选择活动链接

:hover

a:hover

选择鼠标指针位于其上的链接

:focus

input:focus

选择获得焦点的input节点

:first-letter

p:first-letter

选择每个p节点的首字母

:first-line

p:first-line

选择每个p节点的首行

:first-child

p:first-child

选择属于父节点的第一个子节点的所有p节点

:before

p:before

在每个p节点的内容之前插入内容

:after

p:after

在每个p节点的内容之后插入内容

:lang(language)

p:lang

选择带有以it开头的lang属性值的所有p节点

element1~element2

p~ul

选择前面有p节点的所有ul节点

[attribute^=value]

a[src^="https"]

选择其src属性值以https开头的所有a节点

[attribute$=value]

a[src$=".pdf"]

选择其src属性以.pdf结尾的所有a节点

[attribute*=value]

a[src*="abc"]

选择其src属性中包含abc子串的所有a节点

:first-of-type

p:first-of-type

选择属于其父节点的首个p节点的所有p节点

:last-of-type

p:last-of-type

选择属于其父节点的最后p节点的所有p节点

:only-of-type

p:only-of-type

选择属于其父节点唯一的p节点的所有p节点

:only-child

p:only-child

选择属于其父节点的唯一子节点的所有p节点

:nth-child(n)

p:nth-child

选择属于其父节点的第二个子节点的所有p节点

:nth-last-child(n)

p:nth-last-child

同上,从最后一个子节点开始计数

:nth-of-type(n)

p:nth-of-type

选择属于其父节点第二个p节点的所有p节点

:nth-last-of-type(n)

p:nth-last-of-type

同上,但是从最后一个子节点开始计数

:last-child

p:last-child

选择属于其父节点最后一个子节点的所有p节点

:root

:root

选择文档的根节点

:empty

p:empty

选择没有子节点的所有p节点(包括文本节点)

:target

#news:target

选择当前活动的#news节点

:enabled

input:enabled

选择每个启用的input节点

:disabled

input:disabled

选择每个禁用的input节点

:checked

input:checked

选择每个被选中的input节点

:not(selector)

:not

选择非p节点的所有节点

::selection

::selection

选择被用户选取的节点部分

另外,还有一种比较常用的选择器是 XPath,这种选择方式后面会详细介绍。

本节介绍了网页的基本结构和节点间的关系,了解了这些内容,我们才有更加清晰的思路去解析和提取网页内容。

Python

在本节中,我们会详细了解 HTTP 的基本原理,了解在浏览器中敲入 URL 到获取网页内容之间发生了什么。了解了这些内容,有助于我们进一步了解爬虫的基本原理。

1. URI 和 URL

这里我们先了解一下 URI 和 URL,URI 的全称为 Uniform Resource Identifier,即统一资源标志符,URL 的全称为 Universal Resource Locator,即统一资源定位符。

举例来说,https://github.com/favicon.ico是 GitHub 的网站图标链接,它是一个 URL,也是一个 URI。即有这样的一个图标资源,我们用 URL/URI 来唯一指定了它的访问方式,这其中包括了访问协议 https、访问路径(/即根目录)和资源名称 favicon.ico。通过这样一个链接,我们便可以从互联网上找到这个资源,这就是 URL/URI。

URL 是 URI 的子集,也就是说每个 URL 都是 URI,但不是每个 URI 都是 URL。那么,怎样的 URI 不是 URL 呢?URI 还包括一个子类叫作 URN,它的全称为 Universal Resource Name,即统一资源名称。URN 只命名资源而不指定如何定位资源,比如 urn:isbn:0451450523 指定了一本书的 ISBN,可以唯一标识这本书,但是没有指定到哪里定位这本书,这就是 URN。URL、URN 和 URI 的关系可以用图 2-1 表示。

图 2-1 URL、URN 和 URI 关系图

但是在目前的互联网中,URN 用得非常少,所以几乎所有的 URI 都是 URL,一般的网页链接我们既可以称为 URL,也可以称为 URI,我个人习惯称为 URL。

2. 超文本

接下来,我们再了解一个概念——超文本,其英文名称叫作 hypertext,我们在浏览器里看到的网页就是超文本解析而成的,其网页源代码是一系列 HTML 代码,里面包含了一系列标签,比如img显示图片,p指定显示段落等。浏览器解析这些标签后,便形成了我们平常看到的网页,而网页的源代码 HTML 就可以称作超文本。

例如,我们在 Chrome 浏览器里面打开任意一个页面,如淘宝首页,右击任一地方并选择“检查”项(或者直接按快捷键 F12),即可打开浏览器的开发者工具,这时在 Elements 选项卡即可看到当前网页的源代码,这些源代码都是超文本,如图 2-2 所示。

图 2-2 源代码

3. HTTP 和 HTTPS

在淘宝的首页https://www.taobao.com/中,URL 的开头会有 http 或 https,这就是访问资源需要的协议类型。有时,我们还会看到 ftp、sftp、smb 开头的 URL,它们都是协议类型。在爬虫中,我们抓取的页面通常就是 http 或 https 协议的,这里首先了解一下这两个协议的含义。

HTTP 的全称是 Hyper Text Transfer Protocol,中文名叫作超文本传输协议。HTTP 协议是用于从网络传输超文本数据到本地浏览器的传送协议,它能保证高效而准确地传送超文本文档。HTTP 由万维网协会(World Wide Web Consortium)和 Internet 工作小组 IETF(Internet Engineering Task Force)共同合作制定的规范,目前广泛使用的是 HTTP 1.1 版本。

HTTPS 的全称是 Hyper Text Transfer Protocol over Secure Socket Layer,是以安全为目标的 HTTP 通道,简单讲是 HTTP 的安全版,即 HTTP 下加入 SSL 层,简称为 HTTPS。

HTTPS 的安全基础是 SSL,因此通过它传输的内容都是经过 SSL 加密的,它的主要作用可以分为两种。

  • 建立一个信息安全通道来保证数据传输的安全。
  • 确认网站的真实性,凡是使用了 HTTPS 的网站,都可以通过点击浏览器地址栏的锁头标志来查看网站认证之后的真实信息,也可以通过 CA 机构颁发的安全签章来查询。

现在越来越多的网站和 App 都已经向 HTTPS 方向发展,例如:

  • 苹果公司强制所有 iOS App 在 2017 年 1 月 1 日前全部改为使用 HTTPS 加密,否则 App 就无法在应用商店上架;
  • 谷歌从 2017 年 1 月推出的 Chrome 56 开始,对未进行 HTTPS 加密的网址链接亮出风险提示,即在地址栏的显著位置提醒用户“此网页不安全”;
  • 腾讯微信小程序的官方需求文档要求后台使用 HTTPS 请求进行网络通信,不满足条件的域名和协议无法请求。

而某些网站虽然使用了 HTTPS 协议,但还是会被浏览器提示不安全,例如我们在 Chrome 浏览器里面打开 12306,链接为:https://www.12306.cn/,这时浏览器就会提示“您的连接不是私密连接”这样的话,如图 2-3 所示。

图 2-3 12306 页面

这是因为 12306 的 CA 证书是中国铁道部自行签发的,而这个证书是不被 CA 机构信任的,所以这里证书验证就不会通过而提示这样的话,但是实际上它的数据传输依然是经过 SSL 加密的。如果要爬取这样的站点,就需要设置忽略证书的选项,否则会提示 SSL 链接错误。

4. HTTP 请求过程

我们在浏览器中输入一个 URL,回车之后便会在浏览器中观察到页面内容。实际上,这个过程是浏览器向网站所在的服务器发送了一个请求,网站服务器接收到这个请求后进行处理和解析,然后返回对应的响应,接着传回给浏览器。响应里包含了页面的源代码等内容,浏览器再对其进行解析,便将网页呈现了出来,模型如图 2-4 所示。

图 2-4 模型图

此处客户端即代表我们自己的 PC 或手机浏览器,服务器即要访问的网站所在的服务器。

为了更直观地地说明这个过程,这里用 Chrome 浏览器的开发者模式下的 Network 监听组件来做下演示,它可以显示访问当前请求网页时发生的所有网络请求和响应。

打开 Chrome 浏览器,右击并选择“检查”项,即可打开浏览器的开发者工具。这里访问百度http://www.baidu.com/,输入该 URL 后回车,观察这个过程中发生了怎样的网络请求。可以看到,在 Network 页面下方出现了一个个的条目,其中一个条目就代表一次发送请求和接收响应的过程,如图 2-5 所示。

图 2-5 Network 面板

我们先观察第一个网络请求,即www.baidu.com。

其中各列的含义如下。

  • 第一列 Name:请求的名称,一般会将 URL 的最后一部分内容当作名称。
  • 第二列 Status:响应的状态码,这里显示为 200,代表响应是正常的。通过状态码,我们可以判断发送了请求之后是否得到了正常的响应。
  • 第三列 Type:请求的文档类型。这里为 document,代表我们这次请求的是一个 HTML 文档,内容就是一些 HTML 代码。
  • 第四列 Initiator:请求源。用来标记请求是由哪个对象或进程发起的。
  • 第五列 Size:从服务器下载的文件和请求的资源大小。如果是从缓存中取得的资源,则该列会显示 from cache。
  • 第六列 Time:发起请求到获取响应所用的总时间。
  • 第七列 Waterfall:网络请求的可视化瀑布流。

点击这个条目,即可看到更详细的信息,如图 2-6 所示。

图 2-6 详细信息

首先是 General 部分,Request URL 为请求的 URL,Request Method 为请求的方法,Status Code 为响应状态码,Remote Address 为远程服务器的地址和端口,Referrer Policy 为 Referrer 判别策略。

再继续往下看,可以看到,有 Response Headers 和 Request Headers,这分别代表响应头和请求头。请求头里带有许多请求信息,例如浏览器标识、Cookies、Host 等信息,这是请求的一部分,服务器会根据请求头内的信息判断请求是否合法,进而作出对应的响应。图中看到的 Response Headers 就是响应的一部分,例如其中包含了服务器的类型、文档类型、日期等信息,浏览器接受到响应后,会解析响应内容,进而呈现网页内容。

下面我们分别来介绍一下请求和响应都包含哪些内容。

5. 请求

请求,由客户端向服务端发出,可以分为 4 部分内容:请求方法(Request Method)、请求的网址(Request URL)、请求头(Request Headers)、请求体(Request Body)。

(1) 请求方法

常见的请求方法有两种:GET 和 POST。

在浏览器中直接输入 URL 并回车,这便发起了一个 GET 请求,请求的参数会直接包含到 URL 里。例如,在百度中搜索 Python,这就是一个 GET 请求,链接为https://www.baidu.com/s?wd=Python,其中 URL 中包含了请求的参数信息,这里参数wd表示要搜寻的关键字。POST 请求大多在表单提交时发起。比如,对于一个登录表单,输入用户名和密码后,点击“登录”按钮,这通常会发起一个 POST 请求,其数据通常以表单的形式传输,而不会体现在 URL 中。

GET 和 POST 请求方法有如下区别。

  • GET 请求中的参数包含在 URL 里面,数据可以在 URL 中看到,而 POST 请求的 URL 不会包含这些数据,数据都是通过表单形式传输的,会包含在请求体中。
  • GET 请求提交的数据最多只有 1024 字节,而 POST 方式没有限制。

一般来说,登录时,需要提交用户名和密码,其中包含了敏感信息,使用 GET 方式请求的话,密码就会暴露在 URL 里面,造成密码泄露,所以这里最好以 POST 方式发送。上传文件时,由于文件内容比较大,也会选用 POST 方式。

我们平常遇到的绝大部分请求都是 GET 或 POST 请求,另外还有一些请求方法,如 GET、HEAD、POST、PUT、DELETE、OPTIONS、CONNECT、TRACE 等,我们简单将其总结为表 2-1。

表 2-1 其他请求方法

方法

描述

GET

请求页面,并返回页面内容

HEAD

类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头

POST

大多用于提交表单或上传文件,数据包含在请求体中

PUT

从客户端向服务器传送的数据取代指定文档中的内容

DELETE

请求服务器删除指定的页面

CONNECT

把服务器当作跳板,让服务器代替客户端访问其他网页

OPTIONS

允许客户端查看服务器的性能

TRACE

回显服务器收到的请求,主要用于测试或诊断

本表参考:http://www.runoob.com/http/http-methods.html

(2) 请求的网址

请求的网址,即统一资源定位符 URL,它可以唯一确定我们想请求的资源。

(3) 请求头

请求头,用来说明服务器要使用的附加信息,比较重要的信息有 Cookie、Referer、User-Agent 等。下面简要说明一些常用的头信息。

  • Accept:请求报头域,用于指定客户端可接受哪些类型的信息。
  • Accept-Language:指定客户端可接受的语言类型。
  • Accept-Encoding:指定客户端可接受的内容编码。
  • Host:用于指定请求资源的主机 IP 和端口号,其内容为请求 URL 的原始服务器或网关的位置。从 HTTP 1.1 版本开始,请求必须包含此内容。
  • Cookie:也常用复数形式 Cookies,这是网站为了辨别用户进行会话跟踪而存储在用户本地的数据。它的主要功能是维持当前访问会话。例如,我们输入用户名和密码成功登录某个网站后,服务器会用会话保存登录状态信息,后面我们每次刷新或请求该站点的其他页面时,会发现都是登录状态,这就是 Cookies 的功劳。Cookies 里有信息标识了我们所对应的服务器的会话,每次浏览器在请求该站点的页面时,都会在请求头中加上 Cookies 并将其发送给服务器,服务器通过 Cookies 识别出是我们自己,并且查出当前状态是登录状态,所以返回结果就是登录之后才能看到的网页内容。
  • Referer:此内容用来标识这个请求是从哪个页面发过来的,服务器可以拿到这一信息并做相应的处理,如作来源统计、防盗链处理等。
  • User-Agent:简称 UA,它是一个特殊的字符串头,可以使服务器识别客户使用的操作系统及版本、浏览器及版本等信息。在做爬虫时加上此信息,可以伪装为浏览器;如果不加,很可能会被识别出为爬虫。
  • Content-Type:也叫互联网媒体类型(Internet Media Type)或者 MIME 类型,在 HTTP 协议消息头中,它用来表示具体请求中的媒体类型信息。例如,text/html 代表 HTML 格式,image/gif 代表 GIF 图片,application/json 代表 JSON 类型,更多对应关系可以查看此对照表:http://tool.oschina.net/commons

因此,请求头是请求的重要组成部分,在写爬虫时,大部分情况下都需要设定请求头。

(4) 请求体

请求体一般承载的内容是 POST 请求中的表单数据,而对于 GET 请求,请求体则为空。

例如,这里我登录 GitHub 时捕获到的请求和响应如图 2-7 所示。

图 2-7 详细信息

登录之前,我们填写了用户名和密码信息,提交时这些内容就会以表单数据的形式提交给服务器,此时需要注意 Request Headers 中指定 Content-Type 为 application/x-www-form-urlencoded。只有设置 Content-Type 为 application/x-www-form-urlencoded,才会以表单数据的形式提交。另外,我们也可以将 Content-Type 设置为 application/json 来提交 JSON 数据,或者设置为 multipart/form-data 来上传文件。

表 2-2 列出了 Content-Type 和 POST 提交数据方式的关系。

表 2-2 Content-Type 和 POST 提交数据方式的关系

Content-Type

提交数据的方式

application/x-www-form-urlencoded

表单数据

multipart/form-data

表单文件上传

application/json

序列化 JSON 数据

text/xml

XML 数据

在爬虫中,如果要构造 POST 请求,需要使用正确的 Content-Type,并了解各种请求库的各个参数设置时使用的是哪种 Content-Type,不然可能会导致 POST 提交后无法正常响应。

6. 响应

响应,由服务端返回给客户端,可以分为三部分:响应状态码(Response Status Code)、响应头(Response Headers)和响应体(Response Body)。

(1) 响应状态码

响应状态码表示服务器的响应状态,如 200 代表服务器正常响应,404 代表页面未找到,500 代表服务器内部发生错误。在爬虫中,我们可以根据状态码来判断服务器响应状态,如状态码为 200,则证明成功返回数据,再进行进一步的处理,否则直接忽略。表 2-3 列出了常见的错误代码及错误原因。

表 2-3 常见的错误代码及错误原因

状态码

说明

详情

100

继续

请求者应当继续提出请求。服务器已收到请求的一部分,正在等待其余部分

101

切换协议

请求者已要求服务器切换协议,服务器已确认并准备切换

200

成功

服务器已成功处理了请求

201

已创建

请求成功并且服务器创建了新的资源

202

已接受

服务器已接受请求,但尚未处理

203

非授权信息

服务器已成功处理了请求,但返回的信息可能来自另一个源

204

无内容

服务器成功处理了请求,但没有返回任何内容

205

重置内容

服务器成功处理了请求,内容被重置

206

部分内容

服务器成功处理了部分请求

300

多种选择

针对请求,服务器可执行多种操作

301

永久移动

请求的网页已永久移动到新位置,即永久重定向

302

临时移动

请求的网页暂时跳转到其他页面,即暂时重定向

303

查看其他位置

如果原来的请求是 POST,重定向目标文档应该通过 GET 提取

304

未修改

此次请求返回的网页未修改,继续使用上次的资源

305

使用代理

请求者应该使用代理访问该网页

307

临时重定向

请求的资源临时从其他位置响应

400

错误请求

服务器无法解析该请求

401

未授权

请求没有进行身份验证或验证未通过

403

禁止访问

服务器拒绝此请求

404

未找到

服务器找不到请求的网页

405

方法禁用

服务器禁用了请求中指定的方法

406

不接受

无法使用请求的内容响应请求的网页

407

需要代理授权

请求者需要使用代理授权

408

请求超时

服务器请求超时

409

冲突

服务器在完成请求时发生冲突

410

已删除

请求的资源已永久删除

411

需要有效长度

服务器不接受不含有效内容长度标头字段的请求

412

未满足前提条件

服务器未满足请求者在请求中设置的其中一个前提条件

413

请求实体过大

请求实体过大,超出服务器的处理能力

414

请求 URI 过长

请求网址过长,服务器无法处理

415

不支持类型

请求格式不被请求页面支持

416

请求范围不符

页面无法提供请求的范围

417

未满足期望值

服务器未满足期望请求标头字段的要求

500

服务器内部错误

服务器遇到错误,无法完成请求

501

未实现

服务器不具备完成请求的功能

502

错误网关

服务器作为网关或代理,从上游服务器收到无效响应

503

服务不可用

服务器目前无法使用

504

网关超时

服务器作为网关或代理,但是没有及时从上游服务器收到请求

505

HTTP 版本不支持

服务器不支持请求中所用的 HTTP 协议版本

(2) 响应头

响应头包含了服务器对请求的应答信息,如 Content-Type、Server、Set-Cookie 等。下面简要说明一些常用的头信息。

  • Date:标识响应产生的时间。
  • Last-Modified:指定资源的最后修改时间。
  • Content-Encoding:指定响应内容的编码。
  • Server:包含服务器的信息,比如名称、版本号等。
  • Content-Type:文档类型,指定返回的数据类型是什么,如 text/html 代表返回 HTML 文档,application/x-javascript 则代表返回 JavaScript 文件,image/jpeg 则代表返回图片。
  • Set-Cookie:设置 Cookies。响应头中的 Set-Cookie 告诉浏览器需要将此内容放在 Cookies 中,下次请求携带 Cookies 请求。
  • Expires:指定响应的过期时间,可以使代理服务器或浏览器将加载的内容更新到缓存中。如果再次访问时,就可以直接从缓存中加载,降低服务器负载,缩短加载时间。

(3) 响应体

最重要的当属响应体的内容了。响应的正文数据都在响应体中,比如请求网页时,它的响应体就是网页的 HTML 代码;请求一张图片时,它的响应体就是图片的二进制数据。我们做爬虫请求网页后,要解析的内容就是响应体,如图 2-8 所示。

图 2-8 响应体内容

在浏览器开发者工具中点击 Preview,就可以看到网页的源代码,也就是响应体的内容,它是解析的目标。

在做爬虫时,我们主要通过响应体得到网页的源代码、JSON 数据等,然后从中做相应内容的提取。

本节中,我们了解了 HTTP 的基本原理,大概了解了访问网页时背后的请求和响应过程。本节涉及的知识点需要好好掌握,后面分析网页请求时会经常用到。

Python

在写爬虫之前,我们还需要了解一些基础知识,如HTTP原理、网页的基础知识、爬虫的基本原理、Cookies的基本原理等。本章中,我们就对这些基础知识做一个简单的总结。

Python

本节来说明一下 JavaScript 加密逻辑分析并利用 Python 模拟执行 JavaScript 实现数据爬取的过程。在这里以中国空气质量在线监测分析平台为例来进行分析,主要分析其加密逻辑及破解方法,并利用 PyExecJS 来实现 JavaScript 模拟执行来实现该网站的数据爬取。

疑难杂症

中国空气质量在线监测分析平台是一个收录全国各大城市天气数据的网站,包括温度、湿度、PM 2.5、AQI 等数据,链接为:https://www.aqistudy.cn/html/city_detail.html,预览图如下: 通过这个网站我们可以获取到各大城市任何一天的天气数据,对数据分析还是非常有用的。 然而不幸的是,该网站的数据接口通信都被加密了。经过分析之后发现其页面数据是通过 Ajax 加载的,数据接口地址是:https://www.aqistudy.cn/apinew/aqistudyapi.php,是一个 POST 形式访问的接口,这个接口的请求数据和返回数据都被加密了,即 POST 请求的 Data、返回的数据都被加密了,下图是数据接口的 Form Data 部分,可见传输数据是一个加密后的字符串: 下图是该接口返回的内容,同样是经过加密的字符串: 遇到这种接口加密的情况,一般来说我们会选择避开请求接口的方式进行数据爬取,如使用 Selenium 模拟浏览器来执行。但这个网站的数据是图表展示的,所以其数据会变得难以提取。 那怎么办呢?刚啊!

一刚到底

之前的老法子都行不通了,那就只能上了!接下来我们就不得不去分析这个网站接口的加密逻辑,并通过一些技巧来破解这个接口了。 首先找到突破口,当我们点击了这个搜索按钮之后,后台便会发出 Ajax 请求,说明这个点击动作是被监听的,所以我们可以找一下这个点击事件对应的处理代码在哪里,这里可以借助于 Firefox 来实现,它可以分析页面某个元素的绑定事件以及定位到具体的代码在哪一行,如图所示: 这里我们发现这个搜索按钮绑定了三个事件,blur、click、focus,同时 Firefox 还帮助我们列出来了对应事件的处理函数在哪个代码的哪一行,这里可以看到 click 事件是在 city_detail.html 的第 139 行处理的,而且是调用了 getData() 函数。 接下来我们就可以顺藤摸瓜,找到 city_detail.html 文件的 getData() 函数,然后再找到这个函数的定义即可,很容易地,我们在 city_detail.html 的第 463 行就找到了这个函数的定义: 经过分析发现它又调用了 getAQIData() 和 getWeatherData() 两个方法,而这两个方法的声明就在下面,再进一步分析发现这两个方法都调用了 getServerData() 这个方法,并传递了 method、param 等参数,然后还有一个回调函数很明显是对返回数据进行处理的,这说明 Ajax 请求就是由这个 getServerData() 方法发起的,如图所示: 所以这里我们只需要再找到 getServerData() 方法的定义即可分析它的加密逻辑了。继续搜索,然而在原始 html 文件中没有搜索到该方法,那就继续去搜寻其他的 JavaScript 文件有没有这个定义,终于经过一番寻找,居然在 jquery-1.8.0.min.js 这个文件中找到了: 有的小伙伴可能会说,jquery.min.js 不是一个库文件吗,怎么会有这种方法声明?嗯,我只想说,最危险的地方就是最安全的地方。 好了,现在终于找到这个方法了,可为什么看不懂呢?这个方法名后面怎么直接跟了一些奇怪的字符串,而且不符合一般的 JavaScript 写法。其实这里是经过 JavaScript 混淆加密了,混淆加密之后,代码将变为不可读的形式,但是功能是完全一致的,这是一种常见的 JavaScript 加密手段。 那到这里了该怎么解呢?当然是接着刚啊!

反混淆

JavaScript 混淆之后,其实是有反混淆方法的,最简单的方法便是搜索在线反混淆网站,这里提供一个:http://www.bm8.com.cn/jsConfusion/,我们将 jquery-1.8.0.min.js 中第二行 eval 开头的混淆后的 JavaScript 代码复制一下,然后粘贴到这个网站中进行反混淆,就可以看到正常的 JavaScript 代码了,搜索一下就可以找到 getServerData() 方法了,可以看到这个方法确实发出了一个 Ajax 请求,请求了刚才我们分析到的接口: 那么到这里我们又可以发现一个很关键的方法,那就是 getParam(),它接受了 method 和 object 参数,然后返回得到的 param 结果就作为 POST Data 参数请求接口了,所以 param 就是加密后的 POST Data,一些加密逻辑都在 getParam() 方法里面,其方法实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var getParam = (function () {
function ObjectSort(obj) {
var newObject = {};
Object.keys(obj).sort().map(function (key) {
newObject[key] = obj[key]
});
return newObject
}
return function (method, obj) {
var appId = '1a45f75b824b2dc628d5955356b5ef18';
var clienttype = 'WEB';
var timestamp = new Date().getTime();
var param = {
appId: appId,
method: method,
timestamp: timestamp,
clienttype: clienttype,
object: obj,
secret: hex_md5(appId + method + timestamp + clienttype + JSON.stringify(ObjectSort(obj)))
};
param = BASE64.encrypt(JSON.stringify(param));
return AES.encrypt(param, aes_client_key, aes_client_iv)
}
})();

可以看到这里使用了 Base64 和 AES 加密。加密之后的字符串便作为 POST Data 传送给服务器了,然后服务器再进行解密处理,然后进行逻辑处理,然后再对处理后的数据进行加密,返回了加密后的数据,那么 JavaScript 再接收到之后再进行一次解密,再渲染才能得到正常的结果。 所以这里还需要分析服务器传回的数据是怎样解密的。顺腾摸瓜,很容易就找到一个 decodeData() 方法,其定义如下:

1
2
3
4
5
6
function decodeData(data) {
data = AES.decrypt(data, aes_server_key, aes_server_iv);
data = DES.decrypt(data, des_key, des_iv);
data = BASE64.decrypt(data);
return data
}

嗯,这里又经过了三层解密,才把正常的明文数据解析出来。 所以一切都清晰了,我们需要实现两个过程才能正常使用这个接口,即实现 POST Data 的加密过程和 Response Data 的解密过程。其中 POST Data 的加密过程是 Base64 + AES 加密,Response Data 的解密是 AES + DES + Base64 解密。加密解密的 Key 也都在 JavaScript 文件里能找到,我们用 Python 实现这些加密解密过程就可以了。 所以接下来怎么办?接着刚啊! 接着刚才怪! 何必去费那些事去用 Python 重写一遍 JavaScript,万一二者里面有数据格式不统一或者二者由于语言不兼容问题导致计算结果偏差,上哪里去 Debug? 那怎么办?这里我们借助于 PyExecJS 库来实现 JavaScript 模拟就好了。

PyExecJS

PyExecJS 是一个可以使用 Python 来模拟运行 JavaScript 的库。大家可能听说过 PyV8,它也是用来模拟执行 JavaScript 的库,可是由于这个项目已经不维护了,而且对 Python3 的支持不好,而且安装出现各种问题,所以这里选用了 PyExecJS 库来代替它。 首先我们来安装一下这个库:

1
pip install PyExecJS

使用 pip 安装即可。 在使用这个库之前请确保你的机器上安装了以下其中一个 JS 运行环境:

  • JScript
  • JavaScriptCore
  • Nashorn
  • Node
  • PhantomJS
  • PyV8
  • SlimerJS
  • SpiderMonkey

PyExecJS 库会按照优先级调用这些引擎来实现 JavaScript 执行,这里推荐安装 Node.js 或 PhantomJS。 接着我们运行代码检查一下运行环境:

1
2
import execjs
print(execjs.get().name)

运行之后,由于我安装了 Node.js,所以这里会使用 Node.js 作为渲染引擎,结果如下:

1
Node.js (V8)

接下来我们将刚才反混淆的 JavaScript 保存成一个文件,叫做 encryption.js,然后用 PyExecJS 模拟运行相关的方法即可。 首先我们来实现加密过程,这里 getServerData() 方法其实已经帮我们实现好了,并实现了 Ajax 请求,但这个方法里面有获取 Storage 的方法,Node.js 不适用,所以这里我们直接改写下,实现一个 getEncryptedData() 方法实现加密,在 encryption.js 里面实现如下方法:

1
2
3
4
5
6
7
8
function getEncryptedData(method, city, type, startTime, endTime) {
var param = {};
param.city = city;
param.type = type;
param.startTime = startTime;
param.endTime = endTime;
return getParam(method, param);
}

接着我们模拟执行这些方法即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import execjs

# Init environment
node = execjs.get()

# Params
method = 'GETCITYWEATHER'
city = '北京'
type = 'HOUR'
start_time = '2018-01-25 00:00:00'
end_time = '2018-01-25 23:00:00'

# Compile javascript
file = 'encryption.js'
ctx = node.compile(open(file).read())

# Get params
js = 'getEncryptedData("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time)
params = ctx.eval(js)

这里我们首先定义一些参数,如 method、city、start_time 等,这些都可以通过分析 JavaScript 很容易得出其规则。 然后这里首先通过 execjs(即 PyExecJS)的 get() 方法声明一个运行环境,然后调用 compile() 方法来执行刚才保存下来的加密库 encryption.js,因为这里面包含了一些加密方法和自定义方法,所以只有执行一遍才能调用。 接着我们再构造一个 js 字符串,传递这些参数,然后通过 eval() 方法来模拟执行,得到的结果赋值为 params,这个就是 POST Data 的加密数据。 接着我们直接用 requests 库来模拟 POST 请求就好了,也没必要用 jQuery 自带的 Ajax 了,当然后者也是可行的,只不过需要加载一下 jQuery 库。 接着我们用 requests 库来模拟 POST 请求:

1
2
3
# Get encrypted response text
api = 'https://www.aqistudy.cn/apinew/aqistudyapi.php'
response = requests.post(api, data={'d': params})

这样 response 的内容就是服务器返回的加密的内容了。 接下来我们再调用一下 JavaScript 中的 decodeData() 方法即可实现解密:

1
2
3
# Decode data
js = 'decodeData("{0}")'.format(response.text)
decrypted_data = ctx.eval(js)

这样 decrypted_data 就是解密后的字符串了,解密之后,实际上是一个 JSON 字符串:

1
{'success': True, 'errcode': 0, 'errmsg': 'success', 'result': {'success': True, 'data': {'total': 22, 'rows': [{'time': '2018-01-25 00:00:00', 'temp': '-7', 'humi': '35', 'wse': '1', 'wd': '东北风', 'tq': '晴'}, {'time': '2018-01-25 01:00:00', 'temp': '-9', 'humi': '38', 'wse': '1', 'wd': '西风', 'tq': '晴'}, {'time': '2018-01-25 02:00:00', 'temp': '-10', 'humi': '40', 'wse': '1', 'wd': '东北风', 'tq': '晴'}, {'time': '2018-01-25 03:00:00', 'temp': '-8', 'humi': '27', 'wse': '2', 'wd': '东北风', 'tq': '晴'}, {'time': '2018-01-25 04:00:00', 'temp': '-8', 'humi': '26', 'wse': '2', 'wd': '东风', 'tq': '晴'}, {'time': '2018-01-25 05:00:00', 'temp': '-8', 'humi': '23', 'wse': '2', 'wd': '东北风', 'tq': '晴'}, {'time': '2018-01-25 06:00:00', 'temp': '-9', 'humi': '27', 'wse': '2', 'wd': '东北风', 'tq': '多云'}, {'time': '2018-01-25 07:00:00', 'temp': '-9', 'humi': '24', 'wse': '2', 'wd': '东北风', 'tq': '多云'}, {'time': '2018-01-25 08:00:00', 'temp': '-9', 'humi': '25', 'wse': '2', 'wd': '东风', 'tq': '晴转多云转多云间晴'}, {'time': '2018-01-25 09:00:00', 'temp': '-8', 'humi': '21', 'wse': '3', 'wd': '东北风', 'tq': '晴转多云转多云间晴'}, {'time': '2018-01-25 10:00:00', 'temp': '-7', 'humi': '19', 'wse': '3', 'wd': '东北风', 'tq': '晴转多云转多云间晴'}, {'time': '2018-01-25 11:00:00', 'temp': '-6', 'humi': '18', 'wse': '3', 'wd': '东北风', 'tq': '多云'}, {'time': '2018-01-25 12:00:00', 'temp': '-6', 'humi': '17', 'wse': '3', 'wd': '东北风', 'tq': '多云'}, {'time': '2018-01-25 13:00:00', 'temp': '-5', 'humi': '17', 'wse': '2', 'wd': '东北风', 'tq': '多云'}, {'time': '2018-01-25 14:00:00', 'temp': '-5', 'humi': '16', 'wse': '2', 'wd': '东风', 'tq': '多云'}, {'time': '2018-01-25 15:00:00', 'temp': '-5', 'humi': '15', 'wse': '2', 'wd': '北风', 'tq': '多云'}, {'time': '2018-01-25 16:00:00', 'temp': '-5', 'humi': '16', 'wse': '2', 'wd': '东北风', 'tq': '多云'}, {'time': '2018-01-25 17:00:00', 'temp': '-5', 'humi': '16', 'wse': '2', 'wd': '东风', 'tq': '多云'}, {'time': '2018-01-25 18:00:00', 'temp': '-6', 'humi': '18', 'wse': '2', 'wd': '东风', 'tq': '晴间多云'}, {'time': '2018-01-25 19:00:00', 'temp': '-7', 'humi': '19', 'wse': '2', 'wd': '东风', 'tq': '晴间多云'}, {'time': '2018-01-25 20:00:00', 'temp': '-7', 'humi': '19', 'wse': '1', 'wd': '东风', 'tq': '晴间多云'}, {'time': '2018-01-25 21:00:00', 'temp': '-7', 'humi': '19', 'wse': '0', 'wd': '南风', 'tq': '晴间多云'}]}}}

大功告成! 这样我们就可以成功获取温度、湿度、风力、天气等信息了。 另外这部分数据其实不全,还有 PM 2.5、AQI 等数据需要用另外一个 method 参数 GETDETAIL,修改一下即可获取这部分数据了。 再往后的数据就是解析和存储了,这里不再赘述。

结语

本文通过分析 JavaScript 并进行反混淆,然后用 Python 模拟运行 JavaScript 的方式实现了数据抓取。 代码地址:https://github.com/Germey/AQIStudy

Python

Gerapy是一个Scrapy分布式管理模块,本节就来介绍一下它的安装方式。

1. 相关链接

2. pip安装

这里推荐使用pip安装,命令如下:

1
pip3 install gerapy

3. 测试安装

安装完成后,可以在Python命令行下测试:

1
2
$ python3
>>> import gerapy

如果没有错误报出,则证明库已经安装好了。

Python

Scrapyrt为Scrapy提供了一个调度的HTTP接口,有了它,我们就不需要再执行Scrapy命令而是通过请求一个HTTP接口来调度Scrapy任务了。Scrapyrt比Scrapyd更轻量,如果不需要分布式多任务的话,可以简单使用Scrapyrt实现远程Scrapy任务的调度。

1. 相关链接

2. pip安装

这里推荐使用pip安装,命令如下:

1
pip3 install scrapyrt

接下来,在任意一个Scrapy项目中运行如下命令来启动HTTP服务:

1
scrapyrt

运行之后,会默认在9080端口上启动服务,类似的输出结果如下:

1
2
3
4
scrapyrt
2017-07-12 22:31:03+0800 [-] Log opened.
2017-07-12 22:31:03+0800 [-] Site starting on 9080
2017-07-12 22:31:03+0800 [-] Starting factory <twisted.web.server.Site object at 0x10294b160>

如果想更换运行端口,可以使用\-p参数,如:

1
scrapyrt -p 9081

这样就会在9081端口上运行了。

3. Docker安装

另外,Scrapyrt也支持Docker。比如,要想在9080端口上运行,且本地Scrapy项目的路径为/home/quotesbot,可以使用如下命令运行:

1
docker run -p 9080:9080 -tid -v /home/user/quotesbot:/scrapyrt/project scrapinghub/scrapyrt

这样同样可以在9080端口上监听指定的Scrapy项目。

Python

安装好了Scrapyd之后,我们可以直接请求它提供的API来获取当前主机的Scrapy任务运行状况。比如,某台主机的IP为192.168.1.1,则可以直接运行如下命令获取当前主机的所有Scrapy项目:

1
curl http://localhost:6800/listprojects.json

运行结果如下:

1
{"status": "ok", "projects": ["myproject", "otherproject"]}

返回结果是JSON字符串,通过解析这个字符串,便可以得到当前主机的所有项目。

但是用这种方式来获取任务状态还是有点烦琐,所以Scrapyd API就为它做了一层封装,下面我们来看下它的安装方式。

1. 相关链接

2. pip安装

这里推荐使用pip安装,命令如下:

1
pip install python-scrapyd-api

3. 验证安装

安装完成之后,便可以使用Python来获取主机状态了,所以上面的操作便可以用Python代码实现:

1
2
3
from scrapyd_api import ScrapydAPI
scrapyd = ScrapydAPI('http://localhost:6800')
print(scrapyd.list_projects())

运行结果如下:

1
["myproject", "otherproject"]

这样我们便可以用Python直接来获取各个主机上Scrapy任务的运行状态了。

Python

在将 Scrapy 代码部署到远程 Scrapyd 的时候,第一步就是要将代码打包为 EGG 文件,其次需要将 EGG 文件上传到远程主机。这个过程如果用程序来实现,也是完全可以的,但是我们并不需要做这些工作,因为 Scrapyd-Client 已经为我们实现了这些功能。

下面我们就来看看 Scrapyd-Client 的安装过程。

1. 相关链接

2. pip 安装

这里推荐使用 pip 安装,相关命令如下:

1
pip3 install scrapyd-client

3. 验证安装

安装成功后会有一个可用命令,叫作 scrapyd-deploy,即部署命令。

我们可以输入如下测试命令测试 Scrapyd-Client 是否安装成功:

1
scrapyd-deploy -h

如果出现类似如图 1-86 所示的输出,则证明 Scrapyd-Client 已经成功安装。

图 1-86 运行结果

Python

Scrapyd 是一个用于部署和运行 Scrapy 项目的工具,有了它,你可以将写好的 Scrapy 项目上传到云主机并通过 API 来控制它的运行。

既然是 Scrapy 项目部署,基本上都使用 Linux 主机,所以本节的安装是针对于 Linux 主机的。

1. 相关链接

2. pip 安装

这里推荐使用 pip 安装,命令如下:

1
pip3 install scrapyd

3. 配置

安装完毕之后,需要新建一个配置文件/etc/scrapyd/scrapyd.conf,Scrapyd 在运行的时候会读取此配置文件。

在 Scrapyd 1.2 版本之后,不会自动创建该文件,需要我们自行添加。

首先,执行如下命令新建文件:

1
2
sudo mkdir /etc/scrapyd
sudo vi /etc/scrapyd/scrapyd.conf

接着写入如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[scrapyd]
eggs_dir = eggs
logs_dir = logs
items_dir =
jobs_to_keep = 5
dbs_dir = dbs
max_proc = 0
max_proc_per_cpu = 10
finished_to_keep = 100
poll_interval = 5.0
bind_address = 0.0.0.0
http_port = 6800
debug = off
runner = scrapyd.runner
application = scrapyd.app.application
launcher = scrapyd.launcher.Launcher
webroot = scrapyd.website.Root

[services]
schedule.json = scrapyd.webservice.Schedule
cancel.json = scrapyd.webservice.Cancel
addversion.json = scrapyd.webservice.AddVersion
listprojects.json = scrapyd.webservice.ListProjects
listversions.json = scrapyd.webservice.ListVersions
listspiders.json = scrapyd.webservice.ListSpiders
delproject.json = scrapyd.webservice.DeleteProject
delversion.json = scrapyd.webservice.DeleteVersion
listjobs.json = scrapyd.webservice.ListJobs
daemonstatus.json = scrapyd.webservice.DaemonStatus

配置文件的内容可以参见官方文档https://scrapyd.readthedocs.io/en/stable/config.html#example-configuration-file。这里的配置文件有所修改,其中之一是max_proc_per_cpu官方默认为 4,即一台主机每个 CPU 最多运行 4 个 Scrapy 任务,在此提高为 10。另外一个是bind_address,默认为本地 127.0.0.1,在此修改为 0.0.0.0,以使外网可以访问。

4. 后台运行

Scrapyd 是一个纯 Python 项目,这里可以直接调用它来运行。为了使程序一直在后台运行,Linux 和 Mac 可以使用如下命令:

1
(scrapyd > /dev/null &)

这样 Scrapyd 就会在后台持续运行了,控制台输出直接忽略。当然,如果想记录输出日志,可以修改输出目标,如:

1
(scrapyd > ~/scrapyd.log &)

此时会将 Scrapyd 的运行结果输出到~/scrapyd.log 文件中。

当然也可以使用 screen、tmux、supervisor 等工具来实现进程守护。

运行之后,便可以在浏览器的 6800 端口访问 Web UI 了,从中可以看到当前 Scrapyd 的运行任务、日志等内容,如图 1-85 所示。

图 1-85 Scrapyd 首页

当然,运行 Scrapyd 更佳的方式是使用 Supervisor 守护进程,如果感兴趣,可以参考:http://supervisord.org/

另外,Scrapyd 也支持 Docker,后面我们会介绍 Scrapyd Docker 镜像的制作和运行方法。

5. 访问认证

配置完成后,Scrapyd 和它的接口都是可以公开访问的。如果想配置访问认证的话,可以借助于 Nginx 做反向代理,这里需要先安装 Nginx 服务器。

在此以 Ubuntu 为例进行说明,安装命令如下:

1
sudo apt-get install nginx

然后修改 Nginx 的配置文件 nginx.conf,增加如下配置:

1
2
3
4
5
6
7
8
9
10
http {
server {
listen 6801;
location / {
proxy_pass http://127.0.0.1:6800/;
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/conf.d/.htpasswd;
}
}
}

这里使用的用户名和密码配置放置在/etc/nginx/conf.d 目录下,我们需要使用htpasswd命令创建。例如,创建一个用户名为 admin 的文件,命令如下:

1
htpasswd -c .htpasswd admin

接着就会提示我们输入密码,输入两次之后,就会生成密码文件。此时查看这个文件的内容:

1
2
cat .htpasswd
admin:5ZBxQr0rCqwbc

配置完成后,重启一下 Nginx 服务,运行如下命令:

1
sudo nginx -s reload

这样就成功配置了 Scrapyd 的访问认证了。

Python

Docker 是一种容器技术,可以将应用和环境等进行打包,形成一个独立的、类似于 iOS 的 App 形式的“应用”。这个应用可以直接被分发到任意一个支持 Docker 的环境中,通过简单的命令即可启动运行。Docker 是一种最流行的容器化实现方案,和虚拟化技术类似,它极大地方便了应用服务的部署;又与虚拟化技术不同,它以一种更轻量的方式实现了应用服务的打包。使用 Docker,可以让每个应用彼此相互隔离,在同一台机器上同时运行多个应用,不过它们彼此之间共享同一个操作系统。Docker 的优势在于,它可以在更细的粒度上进行资源管理,也比虚拟化技术更加节约资源。

对于爬虫来说,如果我们需要大规模部署爬虫系统的话,用 Docker 会大大提高效率。工欲善其事,必先利其器。

本节中,我们就来介绍三大平台下 Docker 的安装方式。

1. 相关链接

2. Windows 下的安装

如果你的系统是 Windows 10 64 位,那么推荐使用 Docker for Windows。此时直接从 Docker 官方网站下载最新的 Docker for Windows 安装包即可:https://docs.docker.com/docker-for-windows/install/

如果不是 Windows 10 64 位系统,则可以下载 Docker Toolbox:https://docs.docker.com/toolbox/toolbox_install_windows/

下载后直接双击安装即可,详细过程可以参考文档说明。安装完成后,进入命令行。

运行docker命令测试:

1
docker

运行结果如图 1-81 所示,这就证明 Docker 安装成功了。

图 1-81 运行结果

3. Linux 下的安装

详细的分步骤安装说明可以参见官方文档:https://docs.docker.com/engine/installation/linux/ubuntu/

官方文档中详细说明了不同 Linux 系统的安装方法,根据文档一步步执行即可安装成功。但是为了使安装更加方便,Docker 官方还提供了一键安装脚本。使用它,会使安装更加便捷,不用再去一步步执行命令安装了。

首先是 Docker 官方提供的安装脚本。相比其他脚本,官方提供的一定更靠谱,安装命令如下:

1
curl -sSL https://get.docker.com/ | sh

只要执行如上一条命令,等待一会儿 Docker 便会安装完成,这非常方便。

但是使用官方脚本安装有一个缺点,那就是慢,也可能下载超时,所以为了加快下载速度,我们可以使用国内的镜像来安装,所以这里还有阿里云和 DaoCloud 的安装脚本。

阿里云的安装脚本:

1
curl -sSL http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/docker-engine/internet | sh -

DaoCloud 的安装脚本:

1
curl -sSL https://get.daocloud.io/docker | sh

这两个脚本可以任选其一,速度都非常不错。

等待脚本执行完毕之后,就可以使用 Docker 相关命令了,如运行测试 Hello World 镜像:

1
docker run hello-world

运行结果:

1
2
3
4
5
6
7
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
78445dd45222: Pull complete
Digest: sha256:c5515758d4c5e1e838e9cd307f6c6a0d620b5e07e6f927b07d05f6d12a1ac8d7
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.

如果出现类似上面提示的内容,则证明 Docker 可以正常使用了。

4. Mac 下的安装

Mac 平台同样有两种选择:Docker for Mac 和 Docker Toolbox。

Docker for Mac 要求系统为 OS X EI Captain 10.11 或更新,至少 4GB 内存。如果你的系统满足此要求,则强烈建议安装 Docker for Mac。

这里可以使用 Homebrew 安装,安装命令如下:

1
brew cask install docker

另外,也可以手动下载安装包(下载地址为:https://download.docker.com/mac/stable/Docker.dmg)安装。

下载完成后,直接双击安装包,然后将程序拖动到应用程序中即可。

点击程序图标运行 Docker,会发现在菜单栏中出现了 Docker 的图标,如图 1-82 中的第三个小鲸鱼图标。

图 1-82 菜单栏

点击小鲸鱼图标,展开菜单之后,再点击 Start 按钮即可启动 Docker。启动成功后,便会提示 Docker is running,如图 1-83 所示。

图 1-83 运行页面

随后,我们就可以在命令行下使用 Docker 命令了。

可以使用如下命令测试运行:

1
sudo docker run hello-world

运行结果如图 1-84 所示,这就证明 Docker 已经成功安装了。

图 1-84 运行结果

如果系统不满足要求,可以下载 Docker Toolbox,其安装说明为:https://docs.docker.com/toolbox/overview/

关于 Docker for Mac 和 Docker Toolbox 的区别,可以参见:https://docs.docker.com/docker-for-mac/docker-toolbox/

5. 镜像加速

安装好 Docker 之后,在运行测试命令时,我们会发现它首先会下载一个 Hello World 的镜像,然后将其运行。但是这里的下载速度有时候会非常慢,这是因为它默认还是从国外的 Docker Hub 下载的。因此,为了提高镜像的下载速度,我们还可以使用国内镜像来加速下载,于是就有了 Docker 加速器一说。

推荐的 Docker 加速器有 DaoCloud(详见https://www.daocloud.io/mirror)和阿里云(详见https://cr.console.aliyun.com/#/accelerator)。

不同平台的镜像加速方法配置可以参考 DaoCloud 的官方文档:http://guide.daocloud.io/dcs/daocloud-9153151.html

配置完成之后,可以发现镜像的下载速度会快非常多。

Python

如果想要大规模抓取数据,那么一定会用到分布式爬虫。对于分布式爬虫来说,我们需要多台主机,每台主机多个爬虫任务,但是源代码其实只有一份。此时我们需要做的就是将一份代码同时部署到多台主机上来协同运行,那么怎么去部署就是另一个值得思考的问题。

对于Scrapy来说,它有一个扩展组件,叫作Scrapyd,我们只需要安装该扩展组件,即可远程管理Scrapy任务,包括部署源码、启动任务、监听任务等。另外,还有Scrapyd-Client和Scrapyd API来帮助我们更方便地完成部署和监听操作。

另外,还有一种部署方式,那就是Docker集群部署。我们只需要将爬虫制作为Docker镜像,只要主机安装了Docker,就可以直接运行爬虫,而无需再去担心环境配置、版本问题。

本节中,我们就来介绍相关环境的配置过程。

Python

Scrapy-Redis是Scrapy的分布式扩展模块,有了它,我们就可以方便地实现Scrapy分布式爬虫的搭建。本节中,我们将介绍Scrapy-Redis的安装方式。

相关链接

pip安装

这里推荐使用pip安装,命令如下:

1
pip3 install scrapy-redis

wheel安装

此外,也可以到PyPI下载wheel文件安装(详见https://pypi.python.org/pypi/scrapy-redis#downloads),如当前的最新版本为0.6.8,则可以下载scrapy_redis-0.6.8-py2.py3-none-any.whl,然后通过pip安装即可:

1
pip3 install scrapy_redis-0.6.8-py2.py3-none-any.whl

测试安装

安装完成之后,可以在Python命令行下测试:

1
2
$ python3
>>> import scrapy_redis

如果没有错误报出,则证明库已经安装好了。

Python

Scrapy-Splash 是一个 Scrapy 中支持 JavaScript 渲染的工具,本节来介绍它的安装方式。

Scrapy-Splash 的安装分为两部分。一个是 Splash 服务的安装,具体是通过 Docker,安装之后,会启动一个 Splash 服务,我们可以通过它的接口来实现 JavaScript 页面的加载。另外一个是 Scrapy-Splash 的 Python 库的安装,安装之后即可在 Scrapy 中使用 Splash 服务。

1. 相关链接

2. 安装 Splash

Scrapy-Splash 会使用 Splash 的 HTTP API 进行页面渲染,所以我们需要安装 Splash 来提供渲染服务。这里通过 Docker 安装,在这之前请确保已经正确安装好了 Docker。

安装命令如下:

1
docker run -p 8050:8050 scrapinghub/splash

安装完成之后,会有类似的输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2017-07-03 08:53:28+0000 [-] Log opened.
2017-07-03 08:53:28.447291 [-] Splash version: 3.0
2017-07-03 08:53:28.452698 [-] Qt 5.9.1, PyQt 5.9, WebKit 602.1, sip 4.19.3, Twisted 16.1.1, Lua 5.2
2017-07-03 08:53:28.453120 [-] Python 3.5.2 (default, Nov 17 2016, 17:05:23) [GCC 5.4.0 20160609]
2017-07-03 08:53:28.453676 [-] Open files limit: 1048576
2017-07-03 08:53:28.454258 [-] Can't bump open files limit
2017-07-03 08:53:28.571306 [-] Xvfb is started: ['Xvfb', ':1599197258', '-screen', '0', '1024x768x24', '-nolisten', 'tcp']
QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-root'
2017-07-03 08:53:29.041973 [-] proxy profiles support is enabled, proxy profiles path: /etc/splash/proxy-profiles
2017-07-03 08:53:29.315445 [-] verbosity=1
2017-07-03 08:53:29.315629 [-] slots=50
2017-07-03 08:53:29.315712 [-] argument_cache_max_entries=500
2017-07-03 08:53:29.316564 [-] Web UI: enabled, Lua: enabled (sandbox: enabled)
2017-07-03 08:53:29.317614 [-] Site starting on 8050
2017-07-03 08:53:29.317801 [-] Starting factory <twisted.web.server.Site object at 0x7ffaa4a98cf8>

这样就证明 Splash 已经在 8050 端口上运行了。这时我们打开http://localhost:8050,即可看到 Splash 的主页,如图 1-80 所示。

图 1-80 运行页面

当然,Splash 也可以直接安装在远程服务器上。我们在服务器上以守护态运行 Splash 即可,命令如下:

1
docker run -d -p 8050:8050 scrapinghub/splash

这里多了\-d参数,它代表将 Docker 容器以守护态运行,这样在中断远程服务器连接后,不会终止 Splash 服务的运行。

3. Scrapy-Splash 的安装

成功安装 Splash 之后,接下来再来安装其 Python 库,命令如下:

1
pip3 install scrapy-splash

命令运行完毕后,就会成功安装好此库,后面会详细介绍它的用法。

Python

Scrapy 是一个十分强大的爬虫框架,依赖的库比较多,至少需要依赖的库有 Twisted 14.0、lxml 3.4 和 pyOpenSSL 0.14。在不同的平台环境下,它所依赖的库也各不相同,所以在安装之前,最好确保把一些基本库安装好。本节就来介绍 Scrapy 在不同平台的安装方法。

1. 相关链接

2. Anaconda 安装

这是一种比较简单的安装 Scrapy 的方法(尤其是对于 Windows 来说),如果你的 Python 是使用 Anaconda 安装的,或者还没有安装 Python 的话,可以使用此方法安装,这种方法简单、省力。当然,如果你的 Python 不是通过 Anaconda 安装的,可以继续看后面的内容。

关于 Anaconda 的安装方式,可以查看 1.1 节,在此不再赘述。

如果已经安装好了 Anaconda,那么可以通过conda命令安装 Scrapy,具体如下:

1
conda install Scrapy

3. Windows 下的安装

如果你的 Python 不是使用 Anaconda 安装的,可以参考如下方式来一步步安装 Scrapy。

安装 lxml

lxml 的安装过程请参见 1.3.1 节,在此不再赘述,此库非常重要,请一定要安装成功。

安装 pyOpenSSL

在官方网站下载 wheel 文件(详见https://pypi.python.org/pypi/pyOpenSSL#downloads)即可,如图 1-76 所示。

图 1-76 下载页面

下载后利用 pip 安装即可:

1
pip3 install pyOpenSSL-17.2.0-py2.py3-none-any.whl

安装 Twisted

http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted下载 wheel 文件,利用 pip 安装即可。

比如,对于 Python 3.6 版本、Windows 64 位系统,则当前最新版本为 Twisted‑17.5.0‑cp36‑cp36m‑win_amd64.whl,直接下载即可,如图 1-77 所示。

图 1-77 下载页面

然后通过 pip 安装:

1
pip3 install Twisted‑17.5.0cp36cp36mwin_amd64.whl

安装 PyWin32

从官方网站下载对应版本的安装包即可,链接为:https://sourceforge.net/projects/pywin32/files/pywin32/Build%20221/,如图 1-78 所示。

图 1-78 下载列表

比如对于 Python 3.6 版本,可以选择下载 pywin32-221.win-amd64-py3.6.exe,下载完毕之后双击安装即可。

注意,这里使用的是 Build 221 版本,随着时间推移,版本肯定会继续更新,最新的版本可以查看https://sourceforge.net/projects/pywin32/files/pywin32/,到时查找最新的版本安装即可。

安装 Scrapy

安装好了以上的依赖库后,安装 Scrapy 就非常简单了,这里依然使用 pip,命令如下:

1
pip3 install Scrapy

等待命令结束,如果没有报错,就证明 Scrapy 已经安装好了。

4. Linux 下的安装

在 Linux 下的安装方式依然分为两类平台来介绍。

CentOS 和 Red Hat

在 CentOS 和 Red Hat 下,首先确保一些依赖库已经安装,运行如下命令:

1
2
sudo yum groupinstall -y development tools
sudo yum install -y epel-release libxslt-devel libxml2-devel openssl-devel

最后利用 pip 安装 Scrapy 即可:

1
pip3 install Scrapy

Ubuntu、Debian 和 Deepin

在 Ubuntu、Debian 和 Deepin 平台下,首先确保一些依赖库已经安装,运行如下命令:

1
sudo apt-get install build-essential python3-dev libssl-dev libffi-dev libxml2 libxml2-dev libxslt1-dev zlib1g-dev

然后利用 pip 安装 Scrapy 即可:

1
pip3 install Scrapy

运行完毕后,就完成 Scrapy 的安装了。

5. Mac 下的安装

在 Mac 下,首先也是进行依赖库的安装。

在 Mac 上构建 Scrapy 的依赖库需要 C 编译器以及开发头文件,它一般由 Xcode 提供,具体命令如下:

1
xcode-select --install

随后利用 pip 安装 Scrapy 即可:

1
pip3 install Scrapy

6. 验证安装

安装之后,在命令行下输入scrapy,如果出现类似如图 1-79 所示的结果,就证明 Scrapy 安装成功了。

图 1-79 验证安装

7. 常见错误

在安装过程中,常见的错误汇总如下。

pkg_resources.VersionConflict: (six 1.5.2 (/usr/lib/python3/dist-packages), Requirement.parse('six>=1.6.0'))

这是 six 包版本过低出现的错误。six 包是一个提供兼容 Python 2 和 Python 3 的库,这时升级 six 包即可:

1
sudo pip3 install -U six
c/_cffi_backend.c:15:17: fatal error: ffi.h: No such file or directory

这是在 Linux 下常出现的错误,缺少 libffi 库造成的。什么是 libffi?FFI 的全名是 Foreign Function Interface,通常指的是允许以一种语言编写的代码调用另一种语言的代码。而 libffi 库只提供了最底层的、与架构相关的、完整的 FFI。此时安装相应的库即可。

在 Ubuntu 和 Debian 下,直接执行如下命令即可:

1
sudo apt-get install build-essential libssl-dev libffi-dev python3-dev

在 CentOS 和 Red Hat 下,直接执行如下命令即可:

1
sudo yum install gcc libffi-devel python-devel openssl-devel
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build/cryptography/

这是缺少加密的相关组件,此时利用 pip 安装即可:

1
pip3 install cryptography
ImportError: No module named 'packaging'

这是因为缺少 packaging 包出现的错误,这个包提供了 Python 包的核心功能,此时利用 pip 安装即可。

ImportError: No module named '_cffi_backend'

这个错误表示缺少 cffi 包,直接使用 pip 安装即可:

1
pip3 install cffi
ImportError: No module named 'pyparsing'

这个错误表示缺少 pyparsing 包,直接使用 pip 安装即可:

1
pip3 install pyparsing appdirs

Python

pyspider 是国人 binux 编写的强大的网络爬虫框架,它带有强大的 WebUI、脚本编辑器、任务监控器、项目管理器以及结果处理器,同时支持多种数据库后端、多种消息队列,另外还支持 JavaScript 渲染页面的爬取,使用起来非常方便,本节介绍一下它的安装过程。

1. 相关链接

2. 准备工作

pyspider 是支持 JavaScript 渲染的,而这个过程是依赖于 PhantomJS 的,所以还需要安装 PhantomJS(具体的安装过程详见 1.2.5 节)。

3. pip 安装

这里推荐使用 pip 安装,命令如下:

1
pip3 install pyspider

命令执行完毕即可完成安装。

4. 常见错误

Windows 下可能会出现这样的错误提示:

1
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-vXo1W3/pycurl

这是 PyCurl 安装错误,此时需要安装 PyCurl 库。从http://www.lfd.uci.edu/~gohlke/pythonlibs/#pycurl找到对应的 Python 版本,然后下载相应的 wheel 文件即可。比如 Windows 64 位、Python 3.6,则需要下载 pycurl‑7.43.0‑cp36‑cp36m‑win_amd64.whl,随后用 pip 安装即可,命令如下:

1
pip3 install pycurl‑7.43.0cp36cp36mwin_amd64.whl

如果在 Linux 下遇到 PyCurl 的错误,可以参考本文:https://imlonghao.com/19.html

5. 验证安装

安装完成之后,可以直接在命令行下启动 pyspider:

1
pyspider all

此时控制台会有类似如图 1-74 所示的输出。

图 1-74 控制台

这时 pyspider 的 Web 服务就会在本地 5000 端口运行。直接在浏览器中打开http://localhost:5000/,即可进入 pyspider 的 WebUI 管理页面,如图 1-75 所示,这证明 pyspider 已经安装成功了。

图 1-75 管理页面

后面,我们会详细介绍 pyspider 的用法。

Python

我们直接用Requests、Selenium等库写爬虫,如果爬取量不是太大,速度要求不高,是完全可以满足需求的。但是写多了会发现其内部许多代码和组件是可以复用的,如果我们把这些组件抽离出来,将各个功能模块化,就慢慢会形成一个框架雏形,久而久之,爬虫框架就诞生了。

利用框架,我们可以不用再去关心某些功能的具体实现,只需要关心爬取逻辑即可。有了它们,可以大大简化代码量,而且架构也会变得清晰,爬取效率也会高许多。所以,如果有一定的基础,上手框架是一种好的选择。

本书主要介绍的爬虫框架有pyspider和Scrapy。本节中,我们来介绍一下pyspider、Scrapy及其扩展库的安装方式。

Python

Appium 是移动端的自动化测试工具,类似于前面所说的 Selenium,利用它可以驱动 Android、iOS 等设备完成自动化测试,比如模拟点击、滑动、输入等操作,其官方网站为:http://appium.io/。本节中,我们就来了解一下 Appium 的安装方式。

1. 相关链接

2. 安装 Appium

首先,需要安装 Appium。Appium 负责驱动移动端来完成一系列操作,对于 iOS 设备来说,它使用苹果的 UIAutomation 来实现驱动;对于 Android 来说,它使用 UIAutomator 和 Selendroid 来实现驱动。

同时 Appium 也相当于一个服务器,我们可以向它发送一些操作指令,它会根据不同的指令对移动设备进行驱动,以完成不同的动作。

安装 Appium 有两种方式,一种是直接下载安装包 Appium Desktop 来安装,另一种是通过 Node.js 来安装,下面我们介绍一下这两种安装方式。

Appium Desktop

Appium Desktop 支持全平台的安装,我们直接从 GitHub 的 Releases 里面安装即可,链接为https://github.com/appium/appium-desktop/releases。目前的最新版本是 1.1,下载页面如图 1-71 所示。

图 1-71 下载页面

Windows 平台可以下载 exe 安装包 appium-desktop-Setup-1.1.0.exe,Mac 平台可以下载 dmg 安装包如 appium-desktop-1.1.0.dmg,Linux 平台可以选择下载源码,但是更推荐用 Node.js 安装方式。

安装完成后运行,看到的页面如图 1-72 所示。

图 1-72 运行页面

如果出现此页面,则证明安装成功。

Node.js

首先需要安装 Node.js,具体的安装方式可以参见http://www.runoob.com/nodejs/nodejs-install-setup.html,安装完成之后就可以使用npm命令了。

接下来,使用npm命令全局安装 Appium 即可:

1
npm install -g appium

此时等待命令执行完成即可,这样就成功安装了 Appium。

3. Android 开发环境配置

如果我们要使用 Android 设备做 App 抓取的话,还需要下载和配置 Android SDK,这里推荐直接安装 Android Studio,其下载地址为https://developer.android.com/studio/index.html?hl=zh-cn。下载后直接安装即可。

然后,我们还需要下载 Android SDK。直接打开首选项里面的 Android SDK 设置页面,勾选要安装的 SDK 版本,点击 OK 按钮即可下载和安装勾选的 SDK 版本,如图 1-73 所示。

图 1-73 Android SDK 设置页面

另外,还需要配置一下环境变量,添加 ANDROID_HOME 为 Android SDK 所在路径,然后再添加 SDK 文件夹下的 tools 和 platform-tools 文件夹到 PATH 中。

更详细的配置可以参考 Android Studio 的官方文档:https://developer.android.com/studio/intro/index.html

4. iOS 开发环境

首先需要声明的是,Appium 是一个做自动化测试的工具,用它来测试我们自己开发的 App 是完全没问题的,因为它携带的是开发证书(Development Certificate)。但如果我们想拿 iOS 设备来做数据爬取的话,那又是另外一回事了。一般情况下,我们做数据爬取都是使用现有的 App,在 iOS 上一般都是通过 App Store 下载的,它携带的是分发证书(Distribution Certificate),而携带这种证书的应用都是禁止被测试的,所以只有获取 ipa 安装包再重新签名之后才可以被 Appium 测试,具体的方法这里不再展开阐述。

这里推荐直接使用 Android 来进行测试。如果你可以完成上述重签名操作,那么可以参考如下内容配置 iOS 开发环境。

Appium 驱动 iOS 设备必须要在 Mac 下进行,Windows 和 Linux 平台是无法完成的,所以下面介绍一下 Mac 平台的相关配置。

Mac 平台需要的配置如下:

  • macOS 10.12 及更高版本
  • XCode 8 及更高版本

配置满足要求之后,执行如下命令即可配置开发依赖的一些库和工具:

1
xcode-select --install

这样 iOS 部分的开发环境就配置完成了,我们就可以用 iOS 模拟器来进行测试和数据抓取了。

如果想要用真机进行测试和数据抓取,还需要额外配置其他环境,具体可以参考https://github.com/appium/appium/blob/master/docs/en/appium-setup/real-devices-ios.md

5. Python 驱动

另外还需要安装 Python 驱动,命令如下:

1
pip3 install appium-python-client

Python

mitmproxy 是一个支持 HTTP 和 HTTPS 的抓包程序,类似 Fiddler、Charles 的功能,只不过它通过控制台的形式操作。

此外,mitmproxy 还有两个关联组件,一个是 mitmdump,它是 mitmproxy 的命令行接口,利用它可以对接 Python 脚本,实现监听后的处理;另一个是 mitmweb,它是一个 Web 程序,通过它以清楚地观察到 mitmproxy 捕获的请求。

本节中,我们就来了解一下 mitmproxy、mitmdump 和 mitmweb 的安装方式。

1. 相关链接

2. pip 安装

最简单的安装方式还是使用 pip,直接执行如下命令即可安装:

1
pip3 install mitmproxy

这是最简单和通用的安装方式,执行完毕之后即可完成 mitmproxy 的安装,另外还附带安装了 mitmdump 和 mitmweb 这两个组件。如果不想用这种方式安装,也可以选择后面列出的专门针对各个平台的安装方式或者 Docker 安装方式。

3. Windows 下的安装

可以到 GitHub 上的 Releases 页面(链接为:https://github.com/mitmproxy/mitmproxy/releases/)获取安装包,如图 1-59 所示。 图 1-59 下载页面

比如,当前的最新版本为 2.0.2,则可以选择下载 Windows 下的 exe 安装包 mitmproxy-2.0.2-windows-installer.exe,下载后直接双击安装包即可安装。

注意,在 Windows 上不支持 mitmproxy 的控制台接口,但是可以使用 mitmdump 和 mitmweb。

4. Linux 下的安装

在 Linux 下,可以下载编译好的二进制包(下载地址https://github.com/mitmproxy/mitmproxy/releases/),此发行包一般是最新版本,它包含了最新版本的 mitmproxy 和内置的 Python 3 环境,以及最新的 OpenSSL 环境。

如果你的环境里没有 Python 3 和 OpenSSL 环境,建议使用此种方式安装。

下载之后,需要解压并将其配置到环境变量:

1
2
tar -zxvf mitmproxy-2.0.2-linux.tar.gz
sudo mv mitmproxy mitmdump mitmweb /usr/bin

这样就可以将 3 个可执行文件移动到了/usr/bin 目录。而一般情况下,/usr/bin 目录都已经配置在了环境变量下,所以接下来可以直接调用这 3 个工具了。

5. Mac 下的安装

Mac 下的安装非常简单,直接使用 Homebrew 即可,命令如下:

1
brew install mitmproxy

执行命令后,即可完成 mitmproxy 的安装。

6. Docker 安装

mitmproxy 也支持 Docker,其 DockerHub 的地址为https://hub.docker.com/r/mitmproxy/mitmproxy/

在 Docker 下,mitmproxy 的安装命令为:

1
docker run --rm -it -p 8080:8080 mitmproxy/mitmproxy mitmdump

这样就在 8080 端口上启动了 mitmproxy 和 mitmdump。

如果想要获取 CA 证书,可以选择挂载磁盘选项,命令如下:

1
docker run --rm -it -v ~/.mitmproxy:/home/mitmproxy/.mitmproxy -p 8080:8080 mitmproxy/mitmproxy mitmdump

这样就可以在~/.mitmproxy 目录下找到 CA 证书。

另外,还可以在 8081 端口上启动 mitmweb,命令如下:

1
docker run --rm -it -p 8080:8080 -p 127.0.0.1:8081:8081 mitmproxy/mitmproxy mitmweb

更多启动方式可以参考 Docker Hub 的安装说明。

7. 证书配置

对于 mitmproxy 来说,如果想要截获 HTTPS 请求,就需要设置证书。mitmproxy 在安装后会提供一套 CA 证书,只要客户端信任了 mitmproxy 提供的证书,就可以通过 mitmproxy 获取 HTTPS 请求的具体内容,否则 mitmproxy 是无法解析 HTTPS 请求的。

首先,运行以下命令产生 CA 证书,并启动 mitmdump:

1
mitmdump

接下来,我们就可以在用户目录下的.mitmproxy 目录里面找到 CA 证书,如图 1-60 所示。

图 1-60 证书文件

证书一共 5 个,表 1-1 简要说明了这 5 个证书。

表 1-1 5 个证书及其说明

名称

描述

mitmproxy-ca.pem

PEM 格式的证书私钥

mitmproxy-ca-cert.pem

PEM 格式证书,适用于大多数非 Windows 平台

mitmproxy-ca-cert.p12

PKCS12 格式的证书,适用于 Windows 平台

mitmproxy-ca-cert.cer

与 mitmproxy-ca-cert.pem 相同,只是改变了后缀,适用于部分 Android 平台

mitmproxy-dhparam.pem

PEM 格式的秘钥文件,用于增强 SSL 安全性

下面我们介绍一下 Windows、Mac、iOS 和 Android 平台下的证书配置过程。

Windows

双击 mitmproxy-ca.p12,就会出现导入证书的引导页,如图 1-61 所示。

图 1-61 证书导入向导

直接点击“下一步”按钮即可,会出现密码设置提示,如图 1-62 所示。

图 1-62 密码设置提示

这里不需要设置密码,直接点击“下一步”按钮即可。

接下来需要选择证书的存储区域,如图 1-63 所示。这里点击第二个选项“将所有的证书都放入下列存储”,然后点击“浏览”按钮,选择证书存储位置为“受信任的根证书颁发机构”,接着点击“确定”按钮,然后点击“下一步”按钮。

图 1-63 选择证书存储区域

最后,如果有安全警告弹出,如图 1-64 所示,直接点击“是”按钮即可。

图 1-64 安全警告

这样就在 Windows 下配置完 CA 证书了。

Mac

Mac 下双击 mitmproxy-ca-cert.pem 即可弹出钥匙串管理页面,然后找到 mitmproxy 证书,打开其设置选项,选择“始终信任”即可,如图 1-65 所示。

图 1-65 证书配置

iOS

将 mitmproxy-ca-cert.pem 文件发送到 iPhone 上,推荐使用邮件方式发送,然后在 iPhone 上可以直接点击附件并识别安装,如图 1-66 所示。

图 1-66 证书安装页面

点击“安装”按钮之后,会跳到安装描述文件的页面,点击“安装”按钮,此时会有警告提示,如图 1-67 所示。

图 1-67 安装警告页面

继续点击右上角的“安装”按钮,安装成功之后会有已安装的提示,如图 1-68 所示。

图 1-68 安装成功页面

如果你的 iOS 版本是 10.3 以下的话,此处信任 CA 证书的流程就已经完成了。

如果你的 iOS 版本是 10.3 及以上版本,还需要在“设置”→“通用”→“关于本机”→“证书信任设置”将 mitmproxy 的完全信任开关打开,如图 1-69 所示。此时,在 iOS 上配置信任 CA 证书的流程就结束了。

图 1-69 证书信任设置

Android

在 Android 手机上,同样需要将证书 mitmproxy-ca-cert.pem 文件发送到手机上,例如直接复制文件。

接下来,点击证书,便会出现一个提示窗口,如图 1-70 所示。

图 1-70 证书安装页面

这时输入证书的名称,然后点击“确定”按钮即可完成安装。

Python

Charles 是一个网络抓包工具,相比 Fiddler,其功能更为强大,而且跨平台支持得更好,所以这里选用它来作为主要的移动端抓包工具。

1. 相关链接

2. 下载 Charles

我们可以在官网下载最新的稳定版本,如图 1-43 所示。可以发现,它支持 Windows、Linux 和 Mac 三大平台。

图 1-43 Charles 下载页面

直接点击对应的安装包下载即可,具体的安装过程这里不再赘述。

Charles 是收费软件,不过可以免费试用 30 天。如果试用期过了,其实还可以试用,不过每次试用不能超过 30 分钟,启动有 10 秒的延时,但是完整的软件功能还是可以使用的,所以还算比较友好。

3. 证书配置

现在很多页面都在向 HTTPS 方向发展,HTTPS 通信协议应用得越来越广泛。如果一个 App 通信应用了 HTTPS 协议,那么它通信的数据都会是被加密的,常规的截包方法是无法识别请求内部的数据的。

安装完成后,如果我们想要做 HTTPS 抓包的话,那么还需要配置一下相关 SSL 证书。接下来,我们再看看各个平台下的证书配置过程。

Charles 是运行在 PC 端的,我们要抓取的是 App 端的数据,所以要在 PC 和手机端都安装证书。

Windows

如果你的 PC 是 Windows 系统,可以按照下面的操作进行证书配置。

首先打开 Charles,点击 Help→SSL Proxying→Install Charles Root Certificate,即可进入证书的安装页面,如图 1-44 所示。

图 1-44 证书安装页面入口

接下来,会弹出一个安装证书的页面,如图 1-45 所示。

图 1-45 证书安装页面

点击“安装证书”按钮,就会打开证书导入向导,如图 1-46 所示。

图 1-46 证书导入向导

直接点击“下一步”按钮,此时需要选择证书的存储区域,点击第二个选项“将所有的证书放入下列存储”,然后点击“浏览”按钮,从中选择证书存储位置为“受信任的根证书颁发机构”,再点击“确定”按钮,然后点击“下一步”按钮,如图 1-47 所示。

图 1-47 选择证书存储区域

再继续点击“下一步”按钮完成导入。

Mac

如果你的 PC 是 Mac 系统,可以按照下面的操作进行证书配置。

同样是点击 Help→SSL Proxying→Install Charles Root Certificate,即可进入证书的安装页面。

接下来,找到 Charles 的证书并双击,将“信任”设置为“始终信任”即可,如图 1-48 所示。

图 1-48 证书配置

这样就成功安装了证书。

iOS

如果你的手机是 iOS 系统,可以按照下面的操作进行证书配置。

首先,查看电脑的 Charles 代理是否开启,具体操作是点击 Proxy→Proxy Settings,打开代理设置页面,确保当前的 HTTP 代理是开启的,如图 1-49 所示。这里的代理端口为 8888,也可以自行修改。

图 1-49 代理设置

接下来,将手机和电脑连在同一个局域网下。例如,当前电脑的 IP 为 192.168.1.76,那么首先设置手机的代理为 192.168.1.76:8888,如图 1-50 所示。

图 1-50 代理设置

设置完毕后,电脑上会出现一个提示窗口,询问是否信任此设备,如图 1-51 所示。

图 1-51 提示窗口

此时点击 Allow 按钮即可。这样手机就和 PC 连在同一个局域网内了,而且设置了 Charles 的代理,即 Charles 可以抓取到流经 App 的数据包了。

接下来,再安装 Charles 的 HTTPS 证书。

在电脑上打开 Help→SSL Proxying→Install Charles Root Certificate on a Mobile Device or Remote Browser,如图 1-52 所示。

图 1-52 证书安装页面入口

此时会看到如图 1-53 所示的提示。

图 1-53 提示窗口

它提示我们在手机上设置好 Charles 的代理(刚才已经设置好了),然后在手机浏览器中打开 chls.pro/ssl 下载证书。

在手机上打开 chls.pro/ssl 后,便会弹出证书的安装页面,如图 1-54 所示:

图 1-54 证书安装页面

点击“安装”按钮,然后输入密码即可完成安装,如图 1-55 所示。

图 1-55 安装成功页面

如果你的 iOS 版本是 10.3 以下的话,信任 CA 证书的流程就已经完成了。

如果你的 iOS 版本是 10.3 及以上,还需要在“设置”→“通用”→“关于本机”→“证书信任设置”中将证书的完全信任开关打开,如图 1-56 所示。

图 1-56 证书信任设置

Android

如果你的手机是 Android 系统,可以按照下面的操作进行证书配置。

在 Android 系统中,同样需要设置代理为 Charles 的代理,如图 1-57 所示。

图 1-57 代理设置

设置完毕后,电脑上就会出现一个提示窗口,询问是否信任此设备,如图 1-51 所示,此时直接点击 Allow 按钮即可。

接下来,像 iOS 设备那样,在手机浏览器上打开 chls.pro/ssl,这时会出现一个提示框,如图 1-58 所示。

图 1-58 证书安装页面

我们为证书添加一个名称,然后点击“确定”按钮即可完成证书的安装。

Python

除了Web网页,爬虫也可以抓取App的数据。App中的页面要加载出来,首先需要获取数据,而这些数据一般是通过请求服务器的接口来获取的。由于App没有浏览器这种可以比较直观地看到后台请求的工具,所以主要用一些抓包技术来抓取数据。

本书介绍的抓包工具有Charles、mitmproxy和mitmdump。一些简单的接口可以通过Charles或mitmproxy分析,找出规律,然后直接用程序模拟来抓取了。但是如果遇到更复杂的接口,就需要利用mitmdump对接Python来对抓取到的请求和响应进行实时处理和保存。另外,既然要做规模采集,就需要自动化App的操作而不是人工去采集,所以这里还需要一个工具叫作Appium,它可以像Selenium一样对App进行自动化控制,如自动化模拟App的点击、下拉等操作。

本节中,我们就来介绍一下Charles、mitmproxy、mitmdump、Appium的安装方法。

Python

Tornado 是一个支持异步的 Web 框架,通过使用非阻塞 I/O 流,它可以支撑成千上万的开放连接,效率非常高,本节就来介绍一下它的安装方式。

1. 相关链接

2. pip 安装

这里推荐使用 pip 安装,相关命令如下:

1
pip3 install tornado

执行完毕后,即可完成安装。

3. 验证安装

同样,这里也可以用一个 Hello World 程序测试一下,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")

def make_app():
return tornado.web.Application([
(r"/", MainHandler),
])

if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()

直接运行程序,可以发现系统在 8888 端口运行了 Web 服务,控制台没有输出内容,此时访问http://127.0.0.1:8888/,可以观察到网页中呈现了 Hello,world,如图 1-42 所示,这就说明 Tornado 成功安装了。

图 1-42 运行结果

4.结语

后面,我们会利用 Tornado+Redis 来搭建一个 ADSL 拨号代理池。

Python

Flask 是一个轻量级的 Web 服务程序,它简单、易用、灵活,这里主要用来做一些 API 服务。

1. 相关链接

2. pip 安装

这里推荐使用 pip 安装,命令如下:

1
pip3 install flask

运行完毕后,就完成安装了。

3. 验证安装

安装成功后,可以运行如下实例代码测试一下:

1
2
3
4
5
6
7
8
9
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
return "Hello World!"

if __name__ == "__main__":
app.run()

可以发现,系统会在 5000 端口开启 Web 服务,控制台输出如下:

1
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

直接访问http://127.0.0.1:5000/,可以观察到网页中呈现了 Hello World!,如图 1-41 所示,一个最简单的 Flask 程序就运行成功了。

图 1-41 运行结果

4. 结语

后面,我们会利用 Flask+Redis 维护动态代理池和 Cookies 池。

Python

对于Web,我们应该都不陌生,现在日常访问的网站都是Web服务程序搭建而成的。Python同样不例外,也有一些这样的Web服务程序,比如Flask、Django等,我们可以拿它来开发网站和接口等。

在本书中,我们主要使用这些Web服务程序来搭建一些API接口,供我们的爬虫使用。例如,维护一个代理池,代理保存在Redis数据库中,我们要将代理池作为一个公共的组件使用,那么如何构建一个方便的平台来供我们获取这些代理呢?最合适不过的就是通过Web服务提供一个API接口,我们只需要请求接口即可获取新的代理,这样做简单、高效、实用!

书中用到的一些Web服务程序主要有Flask和Tornado,这里就分别介绍它们的安装方法。

Python

RedisDump是一个用于Redis数据导入/导出的工具,是基于Ruby实现的,所以要安装RedisDump,需要先安装Ruby。

1. 相关链接

2. 安装Ruby

有关Ruby的安装方式可以参考http://www.ruby-lang.org/zh_cn/documentation/installation,这里列出了所有平台的安装方式,可以根据对应的平台选用合适的安装方式。

3. gem安装

安装完成之后,就可以执行gem命令了,它类似于Python中的pip命令。利用gem命令,我们可以安装RedisDump,具体如下:

1
gem install redis-dump

执行完毕之后,即可完成RedisDump的安装。

4. 验证安装

安装成功后,就可以执行如下两个命令:

1
2
redis-dump
redis-load

如果可以成功调用,则证明安装成功。

Python

对于Redis来说,我们要使用redis-py库来与其交互,这里就来介绍一下它的安装方法。

1. 相关链接

2. pip安装

这里推荐使用pip安装,命令如下:

1
pip3 install redis

运行完毕之后,即可完成redis-py的安装。

3. 验证安装

为了验证redis-py库是否已经安装成功,可以在命令行下测试一下:

1
2
3
4
5
$ python3
>>> import redis
>>> redis.VERSION
(2, 10, 5)
>>>

如果成功输出了其版本内容,那么证明成功安装了redis-py。

Python

在Python中,如果想要和MongoDB进行交互,就需要借助于PyMongo库,这里就来了解一下它的安装方法。

1. 相关链接

2. pip安装

这里推荐使用pip安装,命令如下:

1
pip3 install pymongo

运行完毕之后,即可完成PyMongo的安装。

3. 验证安装

为了验证PyMongo库是否已经安装成功,可以在命令行下测试一下:

1
2
3
4
5
$ python3
>>> import pymongo
>>> pymongo.version
'3.4.0'
>>>

如果成功输出了其版本内容,那么证明成功安装。

Python

在Python 3中,如果想要将数据存储到MySQL中,就需要借助PyMySQL来操作,本节中我们介绍一下它的安装方式。

1. 相关链接

2. pip安装

这里推荐使用pip安装,命令如下:

1
pip3 install pymysql

执行完命令后即可完成安装。

3. 验证安装

为了验证库是否已经安装成功,可以在命令行下测试一下。这里首先输入python3,进入命令行模式,接着输入如下内容:

1
2
3
4
5
$ python3
>>> import pymysql
>>> pymysql.VERSION
(0, 7, 11, None)
>>>

如果成功输出了其版本内容,那么证明PyMySQL成功安装。

Python

1.4节中,我们介绍了几个数据库的安装方式,但这仅仅是用来存储数据的数据库,它们提供了存储服务,但如果想要和Python交互的话,还需要安装一些Python存储库,如MySQL需要安装PyMySQL,MongoDB需要安装PyMongo等。本节中,我们来说明一下这些存储库的安装方式。

Python

Redis 是一个基于内存的高效的非关系型数据库,本节中我们来了解一下它在各个平台的安装过程。

1. 相关链接

2. Windows 下的安装

在 Windows 下,Redis 可以直接到 GitHub 的发行版本里面下载,具体下载地址是https://github.com/MSOpenTech/redis/releases

打开下载页面后,会发现有许多发行版本及其安装包,如图 1-39 所示。

图 1-39 下载页面

可以下载 Redis-x64-3.2.100.msi 安装即可。

安装过程比较简单,直接点击 Next 按钮安装即可。安装完成后,Redis 便会启动。

在系统服务页面里,可以观察到多了一个正在运行到 Redis 服务,如图 1-40 所示。

图 1-40 系统服务页面

另外,推荐下载 Redis Desktop Manager 可视化管理工具,来管理 Redis。这既可以到官方网站(链接为:https://redisdesktop.com/download)下载,也可以到 GitHub(链接为:https://github.com/uglide/RedisDesktopManager/releases)下载最新发行版本。

安装后,直接连接本地 Redis 即可。

3. Linux 下的安装

这里依然分为两类平台来介绍。

Ubuntu、Debian 和 Deepin

在 Ubuntu、Debian 和 Deepin 系统下,使用apt-get命令安装 Redis:

1
sudo apt-get -y install redis-server

然后输入redis-cli进入 Redis 命令行模式:

1
2
3
4
5
$ redis-cli
127.0.0.1:6379> set 'name' 'Germey'
OK
127.0.0.1:6379> get 'name'
"Germey"

这样就证明 Redis 成功安装了,但是现在 Redis 还是无法远程连接的,依然需要修改配置文件,配置文件的路径为/etc/redis/redis.conf。

首先,注释这一行:

1
bind 127.0.0.1

另外,推荐给 Redis 设置密码,取消注释这一行:

1
requirepass foobared

foobared即当前密码,可以自行修改。

然后重启 Redis 服务,使用的命令如下:

1
sudo /etc/init.d/redis-server restart

现在就可以使用密码远程连接 Redis 了。

另外,停止和启动 Redis 服务的命令分别如下:

1
2
sudo /etc/init.d/redis-server stop
sudo /etc/init.d/redis-server start

CentOS 和 Red Hat

在 CentOS 和 Red Hat 系统中,首先添加 EPEL 仓库,然后更新 yum 源:

1
2
sudo yum install epel-release
sudo yum update

然后安装 Redis 数据库:

1
sudo yum -y install redis

安装好后启动 Redis 服务即可:

1
sudo systemctl start redis

这里同样可以使用redis-cli进入 Redis 命令行模式操作。

另外,为了可以使 Redis 能被远程连接,需要修改配置文件,路径为/etc/redis.conf。

参见上文来修改配置文件实现远程连接和密码配置。

修改完成之后保存。

然后重启 Redis 服务即可,命令如下:

1
sudo systemctl restart redis

4. Mac 下的安装

这里推荐使用 Homebrew 安装,直接执行如下命令即可:

1
brew install redis

启动 Redis 服务的命令如下:

1
2
brew services start redis
redis-server /usr/local/etc/redis.conf

这里同样可以使用redis-cli进入 Redis 命令行模式。

在 Mac 下 Redis 的配置文件路径是/usr/local/etc/redis.conf,可以通过修改它来配置访问密码。

修改配置文件后,需要重启 Redis 服务。停止和重启 Redis 服务的命令分别如下:

1
2
brew services stop redis
brew services restart redis

另外,在 Mac 下也可以安装 Redis Desktop Manager 可视化管理工具来管理 Redis。

Python

更新 2020/3/8

MongoDB 现在已经出到了 4.x 版本,下面的安装教程是基于 3.x 版本,可能已经过期。

关于 4.x 的安装教程,可以参考如下内容:


以下为原文:

MongoDB 是由 C++语言编写的非关系型数据库,是一个基于分布式文件存储的开源数据库系统,其内容存储形式类似 JSON 对象,它的字段值可以包含其他文档、数组及文档数组,非常灵活。

MongoDB 支持多种平台,包括 Windows、Linux、Mac OS、Solaris 等,在其官方网站(https://www.mongodb.com/download-center)均可找到对应的安装包。

本节中,我们来看下它的安装过程。

1. 相关链接

2. Windows 下的安装

这里直接在官网(如图 1-29 所示)点击 DOWNLOAD 按钮下载 msi 安装包即可。

图 1-29 MongoDB 官网

下载完成后,双击它开始安装,指定 MongoDB 的安装路径,例如此处我指定的安装路径为 C:\MongoDB\Server\3.4,如图 1-30 所示。当然,这里也可以自行选择路径。

图 1-30 指定安装路径

点击 Next 按钮执行安装即可。

安装成功之后,进入 MongoDB 的安装目录,此处是 C:\MongoDB\Server\3.4,在 bin 目录下新建同级目录 data,如图 1-31 所示。

图 1-31 新建 data 目录

然后进入 data 文件夹,新建子文件夹 db 来存储数据目录,如图 1-32 所示。

图 1-32 新建 db 目录

之后打开命令行,进入 MongoDB 安装目录的 bin 目录下,运行 MongoDB 服务:

1
mongod --dbpath "C:\MongoDB\Server\3.4\data\db"

请记得将此处的路径替换成你的主机 MongoDB 安装路径。

运行之后,会出现一些输出信息,如图 1-33 所示。

图 1-33 运行结果

这样我们就启动 MongoDB 服务了。

但是如果我们想一直使用 MongoDB,就不能关闭此命令行了。如果意外关闭或重启,MongoDB 服务就不能使用了。这显然不是我们想要的。所以,接下来还需将 MongoDB 配置成系统服务。

首先,以管理员模式运行命令行。注意,此处一定要以管理员身份运行,否则可能配置失败,如图 1-34 所示。

图 1-34 以管理员身份运行

在“开始”菜单中搜索 cmd,找到命令行,然后右击它以管理员身份运行即可。

随后新建一个日志文件,在 bin 目录新建 logs 同级目录,进入之后新建一个 mongodb.log 文件,用于保存 MongoDB 的运行日志,如图 1-35 所示。

图 1-35 新建 mongodb.log 文件

在命令行下输入如下内容:

1
mongod --bind_ip 0.0.0.0 --logpath "C:\MongoDB\Server\3.4\logs\mongodb.log" --logappend --dbpath "C:\MongoDB\Server\3.4\data\db" --port 27017 --serviceName "MongoDB" --serviceDisplayName "MongoDB" --install

这里的意思是绑定 IP 为 0.0.0.0(即任意 IP 均可访问),指定日志路径、数据库路径和端口,指定服务名称。需要注意的是,这里依然需要把路径替换成你的 MongoDB 安装路径,运行此命令后即可安装服务,运行结果如图 1-36 所示。图 1-36 运行结果

如果没有出现错误提示,则证明 MongoDB 服务已经安装成功。

可以在服务管理页面查看到系统服务,如图 1-37 所示。

图 1-37 系统服务页面

然后就可以设置它的开机启动方式了,如自动启动或手动启动等,这样我们就可以非常方便地管理 MongoDB 服务了。

启动服务后,在命令行下就可以利用mongo命令进入 MongoDB 命令交互环境了,如图 1-38 所示。

图 1-38 命令行模式

这样,Windows 下的 MongoDB 配置就完成了。

3. Linux 下的安装

这里以 MongoDB 3.4 为例说明 MongoDB 的安装过程。

Ubuntu

首先,导入 MongoDB 的 GPG key:

1
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6

随后创建 apt-get 源列表,各个系统版本对应的命令分别如下。

  • Ubuntu 12.04 对应的命令如下:

    1
    echo "deb [ arch=amd64 ] http://repo.mongodb.org/apt/ubuntu precise/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list
  • Ubuntu 14.04 对应的命令如下:

    1
    echo "deb [ arch=amd64 ] http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list
  • Ubuntu 16.04 对应的命令如下:

    1
    echo "deb [ arch=amd64,arm64 ] http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list

随后更新 apt-get 源:

1
sudo apt-get update

之后安装 MongoDB 即可:

1
sudo apt-get install -y mongodb-org

安装完成后运行 MongoDB,命令如下:

1
mongod --port 27017 --dbpath /data/db

运行命令之后,MongoDB 就在 27017 端口上运行了,数据文件会保存在/data/db 路径下。

一般情况下,我们在 Linux 上配置 MongoDB 都是为了远程连接使用的,所以这里还需要配置一下 MongoDB 的远程连接以及用户名和密码。

接着,进入 MongoDB 命令行:

1
mongo --port 27017

现在我们就已经进入到 MongoDB 的命令行交互模式下了,在此模式下运行如下命令:

1
2
3
4
5
6
7
8
9
10
11
12
> use admin
switched to db admin
> db.createUser({user: 'admin', pwd: 'admin123', roles: [{role: 'root', db: 'admin'}]})
Successfully added user: {
"user" : "admin",
"roles" : [
{
"role" : "root",
"db" : "admin"
}
]
}

这样我们就创建了一个用户名为 admin,密码为 admin123 的用户,赋予最高权限。

随后需要修改 MongoDB 的配置文件,此时执行如下命令:

1
sudo vi /etc/mongod.conf

然后修改net部分为:

1
2
3
net:
port: 27017
bindIp: 0.0.0.0

这样配置后,MongoDB 可被远程访问。

另外,还需要添加如下的权限认证配置,此时直接添加如下内容到配置文件即可:

1
2
security:
authorization: enabled

配置完成之后,我们需要重新启动 MongoDB 服务,命令如下:

1
sudo service mongod restart

这样远程连接和权限认证就配置完成了。

CentOS 和 Red Hat

首先,添加 MongoDB 源:

1
sudo vi /etc/yum.repos.d/mongodb-org.repo

接着修改如下内容并保存:

1
2
3
4
5
6
[mongodb-org-3.4]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.4/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-3.4.asc

然后执行yum命令安装:

1
sudo yum install mongodb-org

这里启动 MongoDB 服务的命令如下:

1
sudo systemctl start mongod

停止和重新加载 MongoDB 服务的命令如下:

1
2
sudo systemctl stop mongod
sudo systemctl reload mongod

有关远程连接和认证配置,可以参考前面,方式是相同的。

更多 Linux 发行版的 MongoDB 安装方式可以参考官方文档:https://docs.mongodb.com/manual/administration/install-on-linux/

4. Mac 下的安装

这里推荐使用 Homebrew 安装,直接执行brew命令即可:

1
brew install mongodb

然后创建一个新文件夹/data/db,用于存放 MongoDB 数据。

这里启动 MongoDB 服务的命令如下:

1
2
brew services start mongodb
sudo mongod

停止和重启 MongoDB 服务的命令分别是:

1
2
brew services stop mongodb
brew services restart mongodb

5. 可视化工具

这里推荐一个可视化工具 RoboMongo/Robo 3T,它使用简单,功能强大,官方网站为https://robomongo.org/,三大平台都支持,下载链接为https://robomongo.org/download

另外,还有一个简单易用的可视化工具——Studio 3T,它同样具有方便的图形化管理界面,官方网站为https://studio3t.com,同样支持三大平台,下载链接为https://studio3t.com/download/

Python

MySQL 是一个轻量级的关系型数据库,本节中我们来了解下它的安装方式。

1. 相关链接

2. Windows 下的安装

对于 Windows 来说,可以直接在百度软件中心搜索 MySQL,下载其提供的 MySQL 安装包,速度还是比较快的。

当然,最安全稳妥的方式是直接到官网下载安装包进行安装,但是这样做有个缺点,那就是需要登录才可以下载,而且速度不快。

下载完成后,双击安装包即可安装,这里直接选择默认选项,点击 Next 按钮安装即可。这里需要记住图 1-27 所设置的密码。

图 1-27 设置密码页面

安装完成后,我们可以在“计算机”→“管理”→“服务”页面开启和关闭 MySQL 服务,如图 1-28 所示。

图 1-28 系统服务页面

如果启动了 MySQL 服务,就可以使用它来存储数据了。

3. Linux 下的安装

下面我们仍然分平台来介绍。

Ubuntu、Debian 和 Deepin

在 Ubuntu、Debian 和 Deepin 系统中,我们直接使用apt-get命令即可安装 MySQL:

1
2
sudo apt-get update
sudo apt-get install -y mysql-server mysql-client

在安装过程中,会提示输入用户名和密码,输入后等待片刻即可完成安装。

启动、关闭和重启 MySQL 服务的命令如下:

1
2
3
sudo service mysql start
sudo service mysql stop
sudo service mysql restart

CentOS 和 Red Hat

这里以 MySQL 5.6 的 Yum 源为例来说明(如果需要更高版本,可以另寻),安装命令如下:

1
2
3
wget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm
sudo rpm -ivh mysql-community-release-el7-5.noarch.rpm
yum install -y mysql mysql-server

运行如上命令即可完成安装,初始密码为空。接下来,需要启动 MySQL 服务。

启动 MySQL 服务的命令如下:

1
sudo systemctl start mysqld

停止、重启 MySQL 服务的命令如下:

1
2
sudo systemctl stop mysqld
sudo systemctl restart mysqld

上面我们完成了 Linux 下 MySQL 的安装,之后可以修改密码,此时可以执行如下命令:

1
mysql -uroot -p

输入密码后,进入 MySQL 命令行模式,接着输入如下命令:

1
2
3
use mysql;
UPDATE user SET Password = PASSWORD('newpass') WHERE user = 'root';
FLUSH PRIVILEGES;

其中newpass为修改的新的 MySQL 密码,请自行替换。

由于 Linux 一般会作为服务器使用,为了使 MySQL 可以被远程访问,我们需要修改 MySQL 的配置文件,配置文件的路径一般为/etc/mysql/my.cnf。

比如,使用vi进行修改的命令如下:

1
vi /etc/mysql/my.cnf

取消此行的注释如下:

1
bind-address = 127.0.0.1

此行限制了 MySQL 只能本地访问而不能远程访问,取消注释即可解除此限制。

修改完成后重启 MySQL 服务,此时 MySQL 就可以被远程访问了。

到此为止,在 Linux 下安装 MySQL 的过程就结束了。

4. Mac 下的安装

这里推荐使用 Homebrew 安装,直接执行brew命令即可:

1
brew install mysql

启动、停止和重启 MySQL 服务的命令如下:

1
2
3
sudo mysql.server start
sudo mysql.server stop
sudo mysql.server restart

Mac 一般不会作为服务器使用,如果想取消本地 host 绑定,那么需要修改 my.cnf 文件,然后重启服务。

Python

作为数据存储的重要部分,数据库同样是必不可少的,数据库可以分为关系型数据库和非关系型数据库。

关系型数据库如SQLite、MySQL、Oracle、SQL Server、DB2等,其数据库是以表的形式存储,非关系型数据库如MongoDB、Redis,它们的存储形式是键值对,存储形式更加灵活。

本书用到的数据库主要有关系型数据库MySQL及非关系型数据库MongoDB、Redis。

本节中,我们来了解一下它们的安装方式。

Python

在爬虫过程中,难免会遇到各种各样的验证码,而大多数验证码还是图形验证码,这时候我们可以直接用 OCR 来识别。

1. OCR

OCR,即 Optical Character Recognition,光学字符识别,是指通过扫描字符,然后通过其形状将其翻译成电子文本的过程。对于图形验证码来说,它们都是一些不规则的字符,这些字符确实是由字符稍加扭曲变换得到的内容。

例如,对于如图 1-22 和图 1-23 所示的验证码,我们可以使用 OCR 技术来将其转化为电子文本,然后爬虫将识别结果提交给服务器,便可以达到自动识别验证码的过程。

图 1-22 验证码

图 1-23 验证码

tesserocr 是 Python 的一个 OCR 识别库,但其实是对 tesseract 做的一层 Python API 封装,所以它的核心是 tesseract。因此,在安装 tesserocr 之前,我们需要先安装 tesseract。

2. 相关链接

3. Windows 下的安装

在 Windows 下,首先需要下载 tesseract,它为 tesserocr 提供了支持。

进入下载页面,可以看到有各种.exe 文件的下载列表,这里可以选择下载 3.0 版本。图 1-24 所示为 3.05 版本。

图 1-24 下载页面

其中文件名中带有 dev 的为开发版本,不带 dev 的为稳定版本,可以选择下载不带 dev 的版本,例如可以选择下载 tesseract-ocr-setup-3.05.01.exe。

下载完成后双击,此时会出现如图 1-25 所示的页面。

图 1-25 安装页面

此时可以勾选 Additional language data(download)选项来安装 OCR 识别支持的语言包,这样 OCR 便可以识别多国语言。然后一路点击 Next 按钮即可。

接下来,再安装 tesserocr 即可,此时直接使用 pip 安装:

1
pip3 install tesserocr pillow

4. Linux 下的安装

对于 Linux 来说,不同系统已经有了不同的发行包了,它可能叫作 tesseract-ocr 或者 tesseract,直接用对应的命令安装即可。

Ubuntu、Debian 和 Deepin

在 Ubuntu、Debian 和 Deepin 系统下,安装命令如下:

1
sudo apt-get install -y tesseract-ocr libtesseract-dev libleptonica-dev

CentOS、Red Hat

在 CentOS 和 Red Hat 系统下,安装命令如下:

1
yum install -y tesseract

在不同发行版本运行如上命令,即可完成 tesseract 的安装。

安装完成后,便可以调用tesseract命令了。

接着,我们查看一下其支持的语言:

1
tesseract --list-langs

运行结果示例:

1
2
3
4
List of available languages (3):
eng
osd
equ

结果显示它只支持几种语言,如果想要安装多国语言,还需要安装语言包,官方叫作 tessdata(其下载链接为:https://github.com/tesseract-ocr/tessdata)。

利用 Git 命令将其下载下来并迁移到相关目录即可,不同版本的迁移命令如下所示。

在 Ubuntu、Debian 和 Deepin 系统下的迁移命令如下:

1
2
git clone https://github.com/tesseract-ocr/tessdata.git
sudo mv tessdata/* /usr/share/tesseract-ocr/tessdata

在 CentOS 和 Red Hat 系统下的迁移命令如下:

1
2
git clone https://github.com/tesseract-ocr/tessdata.git
sudo mv tessdata/* /usr/share/tesseract/tessdata

这样就可以将下载下来的语言包全部安装了。

这时我们重新运行列出所有语言的命令:

1
tesseract --list-langs

结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
List of available languages (107):
afr
amh
ara
asm
aze
aze_cyrl
bel
ben
bod
bos
bul
cat
ceb
ces
chi_sim
chi_tra
...

可以发现,这里列出的语言就多了很多,比如 chi_sim 就代表简体中文,这就证明语言包安装成功了。

接下来再安装 tesserocr 即可,这里直接使用 pip 安装:

1
pip3 install tesserocr pillow

5. Mac 下的安装

在 Mac 下,我们首先使用 Homebrew 安装 ImageMagick 和 tesseract 库:

1
2
brew install imagemagick
brew install tesseract --all-languages

接下来再安装 tesserocr 即可:

1
pip3 install tesserocr pillow

这样我们便完成了 tesserocr 的安装。

6. 验证安装

接下来,我们可以使用 tesseract 和 tesserocr 来分别进行测试。

下面我们以如图 1-26 所示的图片为样例进行测试。

图 1-26 测试样例

该图片的链接为https://raw.githubusercontent.com/Python3WebSpider/TestTess/master/image.png,可以直接保存或下载。

首先用命令行进行测试,将图片下载下来并保存为 image.png,然后用tesseract命令测试:

1
tesseract image.png result -l eng && cat result.txt

运行结果如下:

1
2
Tesseract Open Source OCR Engine v3.05.01 with Leptonica
Python3WebSpider

这里我们调用了tesseract命令,其中第一个参数为图片名称,第二个参数result为结果保存的目标文件名称,\-l指定使用的语言包,在此使用英文(eng)。然后,再用cat命令将结果输出。

运行结果便是图片的识别结果:Python3WebSpider。可以看到,这时已经成功将图片文字转为电子文本了。

然后还可以利用 Python 代码来测试,这里就需要借助于 tesserocr 库了,测试代码如下:

1
2
3
4
import tesserocr
from PIL import Image
image = Image.open('image.png')
print(tesserocr.image_to_text(image))

我们首先利用Image读取了图片文件,然后调用了tesserocrimage_to_text()方法,再将其识别结果输出。

运行结果如下:

1
Python3WebSpider

另外,我们还可以直接调用file_to_text()方法,这可以达到同样的效果:

1
2
import tesserocr
print(tesserocr.file_to_text('image.png'))

运行结果:

1
Python3WebSpider

如果成功输出结果,则证明 tesseract 和 tesserocr 都已经安装成功。

Python

pyquery同样是一个强大的网页解析工具,它提供了和jQuery类似的语法来解析HTML文档,支持CSS选择器,使用非常方便。本节中,我们就来了解一下它的安装方式。

1. 相关链接

2. pip安装

这里推荐使用pip安装,命令如下:

1
pip3 install pyquery

命令执行完毕之后即可完成安装。

3. wheel安装

当然,我们也可以到PyPI(https://pypi.python.org/pypi/pyquery/#downloads)下载对应的wheel文件安装。比如如果当前版本为1.2.17,则下载的文件名称为pyquery-1.2.17-py2.py3-none-any.whl,此时下载到本地再进行pip安装即可,命令如下:

1
pip3 install pyquery-1.2.17-py2.py3-none-any.whl

4. 验证安装

安装完成之后,可以在Python命令行下测试:

1
2
$ python3
>>> import pyquery

如果没有错误报出,则证明库已经安装好了。

Python

Beautiful Soup是Python的一个HTML或XML的解析库,我们可以用它来方便地从网页中提取数据。它拥有强大的API和多样的解析方式,本节就来了解下它的安装方式。

1. 相关链接

2. 准备工作

Beautiful Soup的HTML和XML解析器是依赖于lxml库的,所以在此之前请确保已经成功安装好了lxml库,具体的安装方式参见上节。

3. pip安装

目前,Beautiful Soup的最新版本是4.x版本,之前的版本已经停止开发了。这里推荐使用pip来安装,安装命令如下:

1
pip3 install beautifulsoup4

命令执行完毕之后即可完成安装。

4. wheel安装

当然,我们也可以从PyPI下载wheel文件安装,链接如下:https://pypi.python.org/pypi/beautifulsoup4

然后使用pip安装wheel文件即可。

5. 验证安装

安装完成之后,可以运行下面的代码验证一下:

1
2
3
from bs4 import BeautifulSoup
soup = BeautifulSoup('<p>Hello</p>', 'lxml')
print(soup.p.string)

运行结果如下:

1
Hello

如果运行结果一致,则证明安装成功。

注意,这里我们虽然安装的是beautifulsoup4这个包,但是在引入的时候却是bs4。这是因为这个包源代码本身的库文件夹名称就是bs4,所以安装完成之后,这个库文件夹就被移入到本机Python3的lib库里,所以识别到的库文件名就叫作bs4。

因此,包本身的名称和我们使用时导入的包的名称并不一定是一致的。

Python

lxml是Python的一个解析库,支持HTML和XML的解析,支持XPath解析方式,而且解析效率非常高。本节中,我们了解一下lxml的安装方式,这主要从Windows、Linux和Mac三大平台来介绍。

1. 相关链接

2. Windows下的安装

在Windows下,可以先尝试利用pip安装,此时直接执行如下命令即可:

1
pip3 install lxml

如果没有任何报错,则证明安装成功。

如果出现报错,比如提示缺少libxml2库等信息,可以采用wheel方式安装。

推荐直接到这里(链接为:http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml)下载对应的wheel文件,找到本地安装Python版本和系统对应的lxml版本,例如Windows 64位、Python 3.6,就选择lxml‑3.8.0‑cp36‑cp36m‑win_amd64.whl,将其下载到本地。

然后利用pip安装即可,命令如下:

1
pip3 install lxml‑3.8.0cp36cp36mwin_amd64.whl

这样我们就可以成功安装lxml了。

3. Linux下的安装

在Linux平台下安装问题不大,同样可以先尝试pip安装,命令如下:

1
pip3 install lxml

如果报错,可以尝试下面的解决方案。

CentOS、Red Hat

对于此类系统,报错主要是因为缺少必要的库。

执行如下命令安装所需的库即可:

1
2
sudo yum groupinstall -y development tools
sudo yum install -y epel-release libxslt-devel libxml2-devel openssl-devel

主要是libxslt-devel和libxml2-devel这两个库,lxml依赖它们。安装好之后,重新尝试pip安装即可。

Ubuntu、Debian和Deepin

在这些系统下,报错的原因同样可能是缺少了必要的类库,执行如下命令安装:

1
sudo apt-get install -y python3-dev build-essential libssl-dev libffi-dev libxml2 libxml2-dev libxslt1-dev zlib1g-dev

安装好之后,重新尝试pip安装即可。

4. Mac下的安装

在Mac平台下,仍然可以首先尝试pip安装,命令如下:

1
pip3 install lxml

如果产生错误,可以执行如下命令将必要的类库安装:

1
xcode-select --install

之后再重新尝试pip安装,就没有问题了。

lxml是一个非常重要的库,后面的Beautiful Soup、Scrapy框架都需要用到此库,所以请一定安装成功。

5. 验证安装

安装完成之后,可以在Python命令行下测试:

1
2
$ python3
>>> import lxml

如果没有错误报出,则证明库已经安装好了。

Python

抓取网页代码之后,下一步就是从网页中提取信息。提取信息的方式有多种多样,可以使用正则来提取,但是写起来相对比较烦琐。这里还有许多强大的解析库,如lxml、Beautiful Soup、pyquery等。此外,还提供了非常强大的解析方法,如XPath解析和CSS选择器解析等,利用它们,我们可以高效便捷地从网页中提取有效信息。

本节中,我们就来介绍一下这些库的安装过程。

Python

之前介绍的Requests库是一个阻塞式HTTP请求库,当我们发出一个请求后,程序会一直等待服务器响应,直到得到响应后,程序才会进行下一步处理。其实,这个过程比较耗费资源。如果程序可以在这个等待过程中做一些其他的事情,如进行请求的调度、响应的处理等,那么爬取效率一定会大大提高。

aiohttp就是这样一个提供异步Web服务的库,从Python 3.5版本开始,Python中加入了async/await关键字,使得回调的写法更加直观和人性化。aiohttp的异步操作借助于async/await关键字的写法变得更加简洁,架构更加清晰。使用异步请求库进行数据抓取时,会大大提高效率,下面我们来看一下这个库的安装方法。

1. 相关链接

2. pip安装

这里推荐使用pip安装,命令如下:

1
pip3 install aiohttp

另外,官方还推荐安装如下两个库:一个是字符编码检测库cchardet,另一个是加速DNS的解析库aiodns。安装命令如下:

1
pip3 install cchardet aiodns

3. 测试安装

安装完成之后,可以在Python命令行下测试:

1
2
$ python3
>>> import aiohttp

如果没有错误报出,则证明库已经安装好了。

4. 结语

我们会在后面的实例中用到这个库,比如维护一个代理池时,利用异步方式检测大量代理的运行状况,会极大地提升效率。

Python

PhantomJS 是一个无界面的、可脚本编程的 WebKit 浏览器引擎,它原生支持多种 Web 标准:DOM 操作、CSS 选择器、JSON、Canvas 以及 SVG。

Selenium 支持 PhantomJS,这样在运行的时候就不会再弹出一个浏览器了。而且 PhantomJS 的运行效率也很高,还支持各种参数配置,使用非常方便。下面我们就来了解一下 PhantomJS 的安装过程。

1. 相关链接

2. 下载 PhantomJS

我们需要在官方网站下载对应的安装包,PhantomJS 支持多种操作系统,比如 Windows、Linux、Mac、FreeBSD 等,我们可以选择对应的平台并将安装包下载下来。

下载完成后,将 PhantomJS 可执行文件所在的路径配置到环境变量里。比如在 Windows 下,将下载的文件解压之后并打开,会看到一个 bin 文件夹,里面包括一个可执行文件 phantomjs.exe,我们需要将它直接放在配置好环境变量的路径下或者将它所在的路径配置到环境变量里。比如,我们既可以将它直接复制到 Python 的 Scripts 文件夹,也可以将它所在的 bin 目录加入到环境变量。

Windows 下环境变量的配置可以参见 1.1 节,Linux 及 Mac 环境变量的配置可以参见 1.2.3 节,在此不再赘述,关键在于将 PhantomJS 的可执行文件所在路径配置到环境变量里。

配置成功后,可以在命令行下测试一下,输入:

1
phantomjs

如果可以进入到 PhantomJS 的命令行,那就证明配置完成了,如图 1-21 所示。

图 1-21 控制台

3. 验证安装

在 Selenium 中使用的话,我们只需要将 Chrome 切换为 PhantomJS 即可:

1
2
3
4
from selenium import webdriver
browser = webdriver.PhantomJS()
browser.get('https://www.baidu.com')
print(browser.current_url)

运行之后,我们就不会发现有浏览器弹出了,但实际上 PhantomJS 已经运行起来了。这里我们访问了百度,然后将当前的 URL 打印出来。

控制台的输出如下:

1
https://www.baidu.com/

如此一来,我们便完成了 PhantomJS 的配置,后面可以利用它来完成一些页面的抓取。

这里我们介绍了 Selenium 对应的三大主流浏览器的对接方式,后面我们会对 Selenium 及各个浏览器的对接方法进行更加深入的探究。