Python爬虫开发与项目实战
上QQ阅读APP看书,第一时间看更新

第2章 Web前端基础

爬虫主要是和网页打交道,了解Web前端的知识是非常重要的。Web前端的知识范围非常广泛,不可能面面俱到和深入讲解,本章主要是抽取Web前端中和爬虫相关的知识点进行讲解,帮助读者了解这些必备的知识,为之后的Python爬虫开发打下基础。

2.1 W3C标准

如果说你只知道Web前端的一个标准,估计肯定是W3C标准了。W3C,即万维网联盟,是Web技术领域最具权威和影响力的国际中立性技术标准机构。万维网联盟(W3C)标准不是某一个标准,而是一系列标准的集合。网页主要由三部分组成:结构(Structure)、表现(Presentation)和行为(Behavior)。对应的标准也分三方面:结构化标准语言主要包括XHTML和XML,表现标准语言主要包括CSS,行为标准主要包括对象模型(如W3C DOM)、ECMAScript等。本节我们主要讲解HTML、CSS、JavaScript、Xpath和JSON等5个部分,基本上覆盖了爬虫开发中需要了解的Web前端基本知识。

2.1.1 HTML

什么是HTML标记语言?HTML不是编程语言,是一种表示网页信息的符号标记语言。标记语言是一套标记,HTML使用标记来描述网页。Web浏览器的作用是读取HTML文档,并以网页的形式显示出它们。浏览器不会显示HTML标记,而是使用标记来解释页面的内容。HTML语言的特点包括:

□ 可以设置文本的格式,比如标题、字号、文本颜色、段落,等等。

□ 可以创建列表。

□ 可以插入图像和媒体。

□ 可以建立表格。

□ 超链接,可以使用鼠标点击超链接来实现页面之间的跳转。

下面从HTML的基本结构、文档设置标记、图像标记、表格和超链接五个方面讲解。

1.HTML的基本结构

首先在浏览器上访问google网站(如图2-1所示),右键查看源代码,如图2-2所示。

图2-1 谷歌网站首页

图2-2 谷歌首页源代码

从谷歌首页的源代码中可以分析出HTML的基本结构:

□ <html>内容</html>:HTML文档是由<html></html>包裹,这是HTML文档的文档标记,也称为HTML开始标记。这对标记分别位于网页的最前端和最后端,<html>在最前端表示网页的开始,</html>在最后端表示网页的结束。

□ <head>内容</head>:HTML文件头标记,也称为HTML头信息开始标记。用来包含文件的基本信息,比如网页的标题、关键字,在<head></head>内可以放<title></title>、<meta></meta>、<style></style>等标记。注意:在<head></head>标记内的内容不会在浏览器中显示。

□ <title>内容</title>:HTML文件标题标记。网页的“主题”,显示在浏览器的窗口的左上边。

□ <body>内容</body>:<body>...</body>是网页的主体部分,在此标记之间可以包含如<p></p>、<h1></h1>、<br>、<hr>等等标记,正是由这些内容组成了我们所看见的网页。

□ <meta>内容</meta>:页面的元信息(meta-information)。提供有关页面的元信息,比如针对搜索引擎和更新频度的描述和关键词。注意meta标记必须放在head元素里面。

2.文档设置标记

文档设置标记分为格式标记和文本标记。下面通过一个标准的HTML文档对格式标记进行讲解,文档如下所示:

<html>
<head>
    <title>Python爬虫开发与项目实战</title>
    <meta charset="UTF-8">
</head>
<body>
文档设置标记<br>
<p>这是段落。</p>
<p>这是段落。</p>
<p>这是段落。</p>
<hr>
<center>居中标记1</center>
<center>居中标记2</center>
<hr>
<pre>
[00:00](music)
[00:28]你我皆凡人,生在人世间;
[00:35]终日奔波苦,一刻不得闲;
[00:43]既然不是仙,难免有杂念;
</pre>
<hr>
<p>
[00:00](music)
[00:28]你我皆凡人,生在人世间;
[00:35]终日奔波苦,一刻不得闲;
[00:43]既然不是仙,难免有杂念;
</p>
<hr>
<br>
<ul>
<li>Coffee</li>
<li>Milk</li>
</ul>
<ol type="A">
<li>Coffee</li>
<li>Milk</li>
</ol>
    <dl>
    <dt>计算机</dt>
    <dd>用来计算的仪器... ...</dd>
    <dt>显示器</dt>
    <dd>以视觉方式显示信息的装置... ...</dd>
    </dl>
    <div >
        <h3>这是标题</h3>
        <p>这是段落。</p>
</div>
</body>
</html>

在浏览器中打开运行,效果如图2-3所示。

图2-3 运行效果图

格式标记包括:

□ <br>:强制换行标记。让后面的文字、图片、表格等等,显示在下一行。

□ <p>:换段落标记。换段落,由于多个空格和回车在HTML中会被等效为一个空格,所以HTML中要换段落就要用<p>, <p>段落中也可以包含<p>段落。例如:<p>This is a paragraph.</p>。

□ <center>:居中对齐标记。让段落或者是文字相对于父标记居中显示。

□ <pre>:预格式化标记。保留预先编排好的格式,常用来定义计算机源代码。和<p>进行一下对比,就可以理解。

□ <li>:列表项目标记。每一个列表使用一个<li>标记,可用在有序列表(<ol>)和无序列表(<ul>)中。

□ <ul>:无序列表标记。<ul>声明这个列表没有序号。

□ <ol>:有序列表标记。可以显示特定的一些顺序。有序列表的type属性值"1"表示阿拉伯数字1.2.3等等;默认type属性值"A"表示大小字母A、B、C等等;上面的程序使用属性"a",这表示小写字母a、b、c等等;"Ⅰ"表示大写罗马数字Ⅰ、Ⅱ、Ⅲ、Ⅳ等等;"ⅰ"表示小写罗马数字ⅰ、ⅱ、ⅲ、ⅳ等等。注意:列表可以进行嵌套。

□ <dl><dt><dd>:定义型列表。对列表条目进行简短说明。

□ <hr>:水平分割线标记。可以用作段落之间的分割线。

□ <div>:分区显示标记,也称为层标记。常用来编排一大段的HTML段落,也可以用于将表格式化,和<p>很相似,可以多层嵌套使用。

接下来通过一个HTML文档对文本标记进行讲解,文档如下所示:

<html>
<head>
    <title>Python爬虫开发与项目实战</title>
    <meta charset="UTF-8">
</head>
<body>
Hn标题标记---->>
<br>
    <h1>Python爬虫</h1>
    <h2>Python爬虫</h2>
    <h3>Python爬虫</h3>
    <h4>Python爬虫</h4>
    <h5>Python爬虫</h5>
    <h6>Python爬虫</h6>
font标记---->>
<font size="1">Python爬虫</font>
<font size="3">Python爬虫</font>
<font size="7">Python爬虫</font>
<font size="7" color="red" face="微软雅黑">Python爬虫</font>
<font size="7" color="red" face="宋体">Python爬虫</font>
<font size="7" color="red" face="新细明体">Python爬虫</font>
<br>
B标记加粗---->>
<b>Python爬虫</b>
<br>
i标记斜体---->>
<i>Python爬虫</i>
<br>
sub下标标记---->>
2<sub>2</sub>
<br>
sup上标标记---->>
2<sup>2</sup>
<br>
引用标记---->>
<cite>Python爬虫</cite>
<br>
em标记表示强调,显示为斜体---->>
<em>Python爬虫</em>
<br>
strong标记表示强调,加粗显示---->>
<strong>Python爬虫</strong>
<br>
small标记,可以显示小一号字体,可以嵌套使用---->>
<small>Python爬虫</small>
<small><small>Python爬虫</small></small>
<small><small><small>Python爬虫</small></small></small>
<br>
big标记,显示大一号的字体---->>
<big>Python爬虫</big>
<big><big>Python爬虫</big></big>
<br>
u标记是显示下划线---->>
<big><big><big><u>Python爬虫</u></big></big></big>
<br>
</body>
</html>

在浏览器中打开运行,效果如图2-4所示。

图2-4 运行效果图

其中文本标记包括:

□ <hn>:标题标记。共有6个级别,n的范围为1~6,不同级别对应不同显示大小的标题,h1最大,h6最小。

□ <font>:字体设置标记。用来设置字体的格式,一般有三个常用属性:size(字体大小), <font size="14px">; color(颜色), <font color="red">; face(字体), <font face="微软雅黑">。

□ <b>:粗字体标记。

□ <i>:斜字体标记。

□ < sub>:文字下标字体标记。

□ <sup>:文字上标字体标记。

□ <tt>:打印机字体标记。

□ <cite>:引用方式的字体,通常是斜体。

□ <em>:表示强调,通常显示为斜体字。

□ <strong>:表示强调,通常显示为粗体字。

□ <small>:小型字体标记。

□ <big>:大型字体标记。

□ <u>:下划线字体标记。

3.图像标记

<img>称为图像标记,用来在网页中显示图像。使用方法为:<img src="路径/文件名.图片格式"width="属性值"height="属性值"border="属性值"alt="属性值">。<img>标记主要包括以下属性:

□ src属性用来指定我们要加载的图片的路径、图片的名称以及图片格式。

□ width属性用来指定图片的宽度,单位为px、em、cm、mm。

□ height属性用来指定图片的高度,单位为px、em、cm、mm。

□ border属性用来指定图片的边框宽度,单位为px、em、cm、mm。

□ alt属性有三个作用:1)当网页上的图片被加载完成后,鼠标移动到上面去,会显示这个图片指定的属性文字;2)如果图像没有下载或者加载失败,会用文字来代替图像显示;3)搜索引擎可以通过这个属性的文字来抓取图片。

我们可以在浏览器上访问博客园首页,对博客园首页的图片进行审查,就可以看到的img标记的使用方法,如图2-5所示。

图2-5 img标记

注意

<img>为单标记,不需要使用</img>闭合。在加载图像文件的时候,文件的路径、文件名或者文件格式错误,将无法加载图片。

4.超链接的使用

爬虫开发中经常需要抽取链接,链接的引用使用的是<a>标记。

<a>标记的基本语法:<a href="链接地址"target="打开方式"name="页面锚点名称">链接文字或者图片</a>。<a>标记主要包括以下属性:

□ href属性值是链接的地址,链接的地址可以是一个网页,也可以是一个视频、图片、音乐等。

□ target属性用来定义超链接的打开方式。当属性值为_blank时,作用是在一个新的窗口中打开链接;当属性值为_self(默认值)时,作用是在当前窗口中打开链接;当属性值为_parent时,作用是在在父窗口中打开页面;当属性值为_top时,在顶层窗口中打开文件。

□ name属性用来指定页面的锚点名称。

5.表格

表格的基本结构包括<table>、<caption>、<tr>、<td>和<th>等标记。

<table>标记的基本格式为<table属性1="属性值1"属性2="属性值2"......>表格内容</table>。table标记有以下常见属性:

□ width属性:表示表格的宽度,它的值可以是像素(px)也可以是父级元素的百分比(%)。

□ height属性:表示表格的高度,它的值可以是像素(px)也可以是父级元素的百分比(%)。

□ border属性:表示表格外边框的宽度。

□ align属性用来表示表格的显示位置。left居左显示,center居中显示,right居右显示。

□ cellspacing属性:单元格之间的间距,默认是2px,单位为像素。

□ cellpadding属性:单元格内容与单元格边框的显示距离,单位为像素。

□ frame属性用来控制表格边框最外层的四条线框。void(默认值)表示无边框;above表示仅顶部有边框;below表示仅有底部边框;hsides表示仅有顶部边框和底部边框;lhs表示仅有左侧边框;rhs表示仅有右侧边框;vsides表示仅有左右侧边框;border表示包含全部4个边框。

□ rules属性用来控制是否显示以及如何显示单元格之间的分割线。属性值none(默认值)表示无分割线;all表示包括所有分割线;rows表示仅有行分割线;clos表示仅有列分割线;groups表示仅在行组和列组之间有分割线。

<caption>标记用于在表格中使用标题。<caption>属性的插入位置,直接位于<table>属性之后,<tr>表格行之前。<caption>标记中align属性可以取四个值:top表示标题放在表格的上部;bottom表示标题放在表格的下部;left表示标题放在表格的左部;right表示标题放在表格的右部。

<tr>标记用来定义表格的行,对于每一个表格行,都是由一对<tr>...</tr>标记表示,每一行<tr>标记内可以嵌套多个<td>或者<th>标记。<tr>标记中的常见属性包括:

□ bgcolor属性用来设置背景颜色,格式为bgcolor="颜色值"。

□ align属性用来设置垂直方向对齐方式,格式为align="值"。值为bottom时,表示靠顶端对齐;值为top时,表示靠底部对齐;值为middle时,表示居中对齐。

□ valign属性用来设置水平方向对齐方式,格式为valign="值"。值为left时,表示靠左对齐;值为right时,表示靠右对齐;值为center时,表示居中对齐。

<td>和<th>都是单元格的标记,其必须嵌套在<tr>标记内,成对出现。<th>是表头标记,通常位于首行或者首列,<th>中的文字默认会被加粗,而<td>不会。<td>是数据标记,表示该单元格的具体数据。<td>和<th>两者的标记属性都是一样的,常用属性如下:

□ bgcolor设置单元格背景。

□ align设置单元格对齐方式。

□ valign设置单元格垂直对齐方式。

□ width设置单元格宽度。

□ height设置单元格高度。

□ rowspan设置单元格所占行数。

□ colspan设置单元格所占列数。

下面通过一个HTML文档来演示表格的使用,文档如下:

<html>
<head>
    <title>学生信息表</title>
    <meta charset="UTF-8">
</head>
 <body>
    <table width="960" align="center" border="1" rules="all" cellpadding="15">
        <tr>
              <th>学号</th>
              <th>班级</th>
              <th>姓名</th>
              <th>年龄</th>
              <th>籍贯</th>

        </tr>
        <tr align="center">
              <td>1500001</td>
              <td>(1)班</td>
              <td>张三</td>
              <td>16</td>
              <td>上海</td>
        </tr>
        <tr align="center">
              <td>1500011</td>
              <td>(2)班</td>
              <td>李四</td>
              <td>15</td>
              <td bgcolor="# ccc">浙江</td>
        </tr>
    </table>
    <br/>
    <table width="960" align="center" border="1" rules="all" cellpadding=
        "15">
        <tr bgcolor="# ccc">
              <th>学号</th>
              <th>班级</th>
              <th>姓名</th>
              <th>年龄</th>
              <th>籍贯</th>
        </tr>
        <tr align="center">
              <td>1500001</td>
              <td>(1)班</td>
              <td>张三</td>
              <td>16</td>
              <td bgcolor="red"><font color="white">上海</font></td>
        </tr>
        <tr align="center">
              <td>1500011</td>
              <td>(2)班</td>
              <td>李四</td>
              <td>15</td>
              <td>浙江</td>
        </tr>
    </table>
  </body>
</html>

在浏览器中打开运行,效果如图2-6所示。

图2-6 表格运行效果图

2.1.2 CSS

CSS指层叠样式表(Cascading Style Sheets),用来定义如何显示HTML元素,一般和HTML配合使用。CSS样式表的目的是为了解决内容与表现分离的问题,即使同一个HTML文档也能表现出外观的多样化。在HTML中使用CSS样式的方式,一般有三种做法:

□ 内联样式表:CSS代码直接写在现有的HTML标记中,直接使用style属性改变样式。例如,<body style="background-color:green; margin:0; padding:0; "></body>。

□ 嵌入式样式表:CSS样式代码写在<style type="text/css"></style>标记之间,一般情况下嵌入式CSS样式写在<head></head>之间。

□ 外部样式表:CSS代码写一个单独的外部文件中,这个CSS样式文件以“.css”为扩展名,在<head>内(不是在<style>标记内)使用<link>标记将CSS样式文件链接到HTML文件内。例如,<link rel="StyleSheet" type="text/css" href="style.css">。

CSS规则由两个主要的部分构成:选择器,以及一条或多条声明。选择器通常是需要改变样式的HTML元素。每条声明由一个属性和一个值组成。属性(property)是希望设置的样式属性(style attribute)。每个属性有一个值。属性和值由冒号分开。例如:h1 { color : blue ;font-size : 12px}。其中h1为选择器,color和font-size是属性,blue和12px是属性值,这句话的意思是将h1标记中的颜色设置为蓝色,字体大小为12px。根据选择器的定义方式,可以将样式表的定义分成三种方式:

□ HTML标记定义:上面举的例子就是使用的这种方式。假如想修改<p>...</p>的样式,可以定义CSS:p{属性:属性值;属性1:属性值1}。p可以叫做选择器,定义了标记中内容所执行的样式。一个选择器可以控制若干个样式属性,他们之间需要用英语的“; ”隔开,最后一个可以不加“; ”。

□ ID选择器定义:ID选择器可以为标有特定ID的HTML元素指定特定的样式。HTML元素以ID属性来设置ID选择器,CSS中ID选择器以"#"来定义。假如定义为#word{text-align:center; color:red; },就将HTML中ID为word的元素设置为居中,颜色为红色。

□ class选择器定义:class选择器用于描述一组元素的样式,class选择器有别于ID选择器,它可以在多个元素中使用。class选择器在HTML中以class属性表示,在CSS中,class选择器以一个点“.”号显示。例如,.center{text-align:center; }将所有拥有center类的HTML元素设为居中。当然也可以指定特定的HTML元素使用class,例如,p.center{text-align:center; }是对所有的p元素使用class="center",让该元素的文本居中。

介绍完选择器,接着说一下CSS中一些常见的属性。常见属性主要说明一下颜色属性、字体属性、背景属性、文本属性和列表属性。

1.颜色属性

颜色属性color用来定义文本的颜色,可以使用以下方式定义颜色:

□ 颜色名称,如color:green。

□ 十六进制,如color:#ff6600。

□ 简写方式,如color:#f60。

□ RGB方式,如rgb(255,255,255),红(R)、绿(G)、蓝(B)的取值范围均为0~255

□ RGBA方式,如color:rgba(255,255,255,1), RGBA表示Red(红色)、Green(绿色)、Blue(蓝色)和Alpha的(色彩空间)透明度。

2.字体属性

可以使用字体属性定义文本形式,有如下方法:

□ font-size定义字体大小,如font-size:14px。

□ font-family定义字体,如font-family:微软雅黑,serif。字体之间可以使用“, ”隔开,以确保当字体不存在的时候直接使用下一个字体。

□ font-weight定义字体加粗,取值有两种方式。一种是使用名称,如normal(默认值)、bold(粗)、bolder(更粗)、lighter(更细);一种是使用数字,如100、200、300~900, 400=normal,而700=bold。

3.背景属性

可以用背景属性定义背景颜色、背景图片、背景重复和背景的位置,内容如下:

□ background-color用来定义背景的颜色,用法参考颜色属性。

□ background-image用来定义背景图片,如background-image:url(图片路径),也可以设置为background-image:none,表示不使用图片。

□ background-repeat用来定义背景重复方式。取值为repeat,表示整体重复平铺;取值为repeat-x,表示只在水平方向平铺;取值为repeat-y,表示只在垂直方向平铺;取值为no-repeat,表示不重复。

□ background-position用来定义背景位置。在横向上,可以取left、center、right;在纵向上可以取top、center、bottom。

□ 简写方式可以简化背景属性的书写,同时定义多个属性,格式为background:背景颜色url(图像)重复位置。如background:#f60 url(images/bg、jpg) no-repeat top center。

4.文本属性

可以用文本属性设置行高、缩进和字符间距,具体如下:

□ text-align设置文本对齐方式,属性值可以取left、center、right。

□ line-height设置文本行高,属性值可以取具体数值,来设置固定的行高值。也可以取百分比,是基于字体大小的百分比行高。

□ text-indent代表首行缩进,如text-indent:50px,意思是首行缩进50个像素。

□ letter-spacing用来设置字符间距。属性值默认是normal,规定字符间没有额外的空间;可以设置具体的数值(可以是负值),如letter-spacing: 3px;可以取inherit,从父元素继承letter-spacing属性的值。

5.列表

在HTML中,有两种类型的列表:无序和有序。其实使用CSS,可以列出进一步的样式,并可用图像作列表项标记。接下来主要讲解以下几种属性:

□ list-style-type用来指明列表项标记的类型。常用的属性值有:none(无标记)、disc(默认,标记是实心圆)、circle(标记是空心圆)、square(标记是实心方块)、decimal(标记是数字)、decimal-leading-zero(0开头的数字标记)、lower-roman(小写罗马数字i、ii、iii、iv、v等)、upper-roman(大写罗马数字I、II、III、IV、V等)、lower-alpha(小写英文字母a、b、c、d、e等)、upper-alpha(大写英文字母A、B、C、D、E等)。例如,ul.a{list-style-type: circle; }是将class选择器的值为a的ul标记设置为空心圆标记。

□ list-style-position用来指明列表项中标记的位置。属性值可以取inside、outside和inherit。inside指的是列表项标记放置在文本以内,且环绕文本根据标记对齐。outside为默认值,保持标记位于文本的左侧,列表项标记放置在文本以外,且环绕文本不根据标记对齐。inherit规定应该从父元素继承list-style-position属性的值。

□ list-style-image用来设置设置图像列表标记。属性值可以为URL(图像的路径)、none(默认无图形被显示)、inherit(从父元素继承list-style-image属性的值)。例如,ul{list-style-image:url('image.gif'); },意思是给ul标记前面的标记设置为image.gif图片。

以上就将关于CSS的基本知识讲解完成了,接下来通过一个综合的例子将所有知识点进行融合,采用嵌入式样式表的方式,HTML文档如下:

<! DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Python爬虫开发</title>
<style>
h1
{
    background-color:# 6495ed; /*--背景颜色--*/
    color:red; /* 字体颜色 */
    text-align:center; /* 文本居中 */
    font-size:40px; /* 字体大小*/
}
p
{
    background-color:# e0ffff;
    text-indent:50px; /* 首行缩进 */
    font-family:"Times New Roman", Times, serif; /* 设置字体 */
}
p.ex {color:rgb(0,0,255); }
div
{
    background-color:# b0c4de;
}
ul.a {list-style-type:square; }
ol.b {list-style-type:upper-roman; }
ul.c{list-style-image:url('http://www.cnblogs.com/images/logo_small.gif'); }
</style>
</head>

<body>
<h1>CSS background-color演示</h1>
<div>
该文本插入在div元素中。
<p>该段落有自己的背景颜色。</p>
<p class="ex">这是一个类为"ex"的段落。这个文本是蓝色的。</p>
我们仍然在同一个div中。
</div>
<p>无序列表实例:</p>

<ul class="a">
    <li>Coffee</li>
    <li>Tea</li>
    <li>Coca Cola</li>
</ul>
<p>有序列表实例:</p>
<ol class="b">
    <li>Coffee</li>
    <li>Tea</li>
    <li>Coca Cola</li>
</ol>

<p>图片列表示例</p>
<ul class="c">
    <li>Coffee</li>
    <li>Tea</li>
    <li>Coca Cola</li>
</ul>
</body>
</html>

在浏览器中打开文档,通过运行效果和之前的知识点进行对比,将更加容易理解。效果如图2-7所示。

图2-7 运行效果图

2.1.3 JavaScript

JavaScript是一种轻量级的脚本语言,和Python语言是一样的,只不过JavaScript是由浏览器进行解释执行。JavaScript可以插入HTML页面中,可由所有的现代浏览器执行。由于JavaScript是一门新的编程语言,知识点很多,本节不进行深入讲解,主要介绍一下JavaScript的用法和基本语法。大家如果想深入学习,需要额外看一些教程。

如何使用JavaScript呢?主要有直接插入代码和外部引用js文件两种做法:

1)直接插入代码。在<script></script>标记中编写代码。JavaScript代码可以直接嵌在网页的任何地方,不过通常我们都把JavaScript代码放到<head>中,示例如下:

<html>
<head>
    <script type='text/javascript'>
        alert('Hello, world');
    </script>
</head>
<body>
    python爬虫
</body>
</html>

<script>标记中包含的就是JavaScript代码,可以直接被浏览器执行,弹出一个警告框。

2)外部引用js文件。把JavaScript代码放到一个单独的.js文件,然后在HTML中通过<script src=’目标文档的URL'></script>的方式来引入js文件,其中目标文档的URL即是链接外部的js文件。示例如下:

<html>
<head>
    <script src="/static/js/jquery.js"></script>
</head>
<body>
    python爬虫
</body>
</html>

这样/static/js/jquery.js就会被浏览器执行。把JavaScript代码放入一个单独的.js文件中更利于维护代码,并且多个页面可以各自引用同一份.js文件,减少程序员编码量。在页面中多次编写JavaScript代码,浏览器按照顺序依次执行。

一般在正常的开发中都是采用上述两种做法结合的方式,之后在做Python爬虫开发时会经常见到。

为了能让零基础的读者在读完本节后能看懂简单的JavaScript代码,下面将从基本语法、数据类型和变量、运算符和操作符、条件判断、循环和函数等六个方面介绍JavaScript基础。

1.JavaScript基本语法

JavaScript严格区分大小写,JavaScript会忽略关键字、变量名、数字、函数名或其他各种元素之间的空格、制表符或换行符。我们可以使用缩进、换行来使代码整齐,提高可读性。一条完整的语句如下:

var x = 1;

这条语句定义了一个x的变量。从这条语句中可以看到以分号“; ”作为结束。一行可以定义多条语句,但不推荐这么做。最后一个语句的分号可以省略,但尽量不要省略。示例语句如下:

var x = 1; var y = 2;

语句块是一组语句的集合,使用{...}形成一个块block。例如,下面的代码先做了一个判断,如果判断成立,将执行{...}中的所有语句:

var x = 2; var y = 1;
if (x > y) {
    x = 3;
    y = 2;
}

{...}还可以嵌套,形成层级结构。将以上的代码进行改造,程序如下:

var x = 2; var y = 1;
if (x > y) {
        x = 3;
        y = 4;
    if(x<y){
        x = 2;
        y = 1;
    }
}

注释主要分为单行注释和多行注释。单行注释使用//作为注释符,多行注释使用/* */来注释内容。示例如下:

//var x = 2; var y = 1;
/* var x = 2; var y = 1; */

2.数据类型和变量

和Python一样,JavaScript也有自己的数据类型。在JavaScript中定义了以下几种数据类型:

□ Number类型:JavaScript中不区分整数和浮点数,统一使用Number表示。示例如下:100(整数)、0.45(浮点数)、1.234e3(科学计数法表示)、-10(负数)、NaN(无法计算时候使用)、Infinity(无限大)、0xff(十六进制)。

□ 字符串类型:字符串是以单引号或双引号括起来的任意文本,比如’abc', "xyz"等。

□ 布尔值类型:一个布尔值只有true、false两种值。

□ 数组类型:数组是一组按顺序排列的集合,集合的每个值称为元素。JavaScript的数组可以包括任意数据类型,示例如下:var array = [1, 2, 3.14, 'Hello', null, true]。上述数组包含6个元素。数组用[]表示,元素之间用“, ”分隔。另一种创建数组的方法是通过Array()函数实现,示例如下:var array = new Array(1, 2, 3)。数组的元素可以通过索引来访问,索引的起始值为0。

□ 对象类型:javaScript的对象是一组由键-值组成的无序集合,类似Python中的字典。示例如下:var person = {name: 'qiye', age: 24, tags: ['python', 'web', 'hacker'], city: 'Beijing', man:true}。JavaScript对象的键都是字符串类型,值可以是任意数据类型。要获取一个对象的属性,我们用“对象变量.属性名”的方式,如person. name。

JavaScript是弱类型的编程语言,声明变量的时候都是使用关键字var,没有int、char之说,为变量赋值时会自动判断类型并进行转换。变量名是大小写英文、数字、“$”和“_”的组合,且不能用数字开头。变量名也不能是JavaScript的关键字,如if、while等。申明一个变量用var语句,比如:vars_007 = '007'。

3.运算符和操作符

JavaScript中的运算符和操作符,与Python中的用法非常相似,表2-1总结了javaScript常用的运算符和操作符。

表2-1 运算符和操作符

4.条件判断

JavaScript使用if(){...}else{...}来进行条件判断,和C语言的使用方法一样。例如,根据年龄显示不同内容,可以用if语句实现如下:

var role = 20;
if (age >= 18) {
    alert('adult');
} else {
    alert('teenager');
}

5.循环

JavaScript的循环有两种:一种是for循环,一种是while循环。

首先说一下for循环。举个例子,计算1到100相加之和,程序如下:

var x = 0;
var i;
for (i=1; i<=100; i++) {
    x = x + i;
}

for循环常用来遍历数组。另外for循环还有一个变体是for...in循环,它可以把一个对象的所有属性依次循环出来,示例如下:

var person = {
    name: 'qiye',
    age: 20,
    city: 'Beijing'
};
for (var key in person ) {
    alert(key); //  'name', 'age', 'city'
}

最后说一下while循环。使用方法和C语言一样,分为while(){...}循环和do{...}while(),具体使用不再细说。

6.函数

在JavaScript中,定义函数使用function关键字,使用方式如下:

function add(x, y) {
    return x+y;
}

上述add()函数的定义如下:

□ function指出这是一个函数定义;

□ add是函数的名称。

□ (x, y)括号内列出函数的参数,多个参数以“, ”分隔。

□ {...}之间的代码是函数体,可以包含若干语句,甚至可以没有任何语句。

调用函数时,按顺序传入参数即可:add(10,9); //返回19。

由于JavaScript允许传入任意个参数而不影响调用,因此传入的参数比定义的参数多也没有问题,虽然函数内部并不需要这些参数:add(10, 9, 'blablabla'); //返回19。

传入的参数比定义的少也没有问题:add(); //返回NaN。此时add(x, y)函数的参数x和y收到的值为undefined,计算结果为NaN。

2.1.4 XPath

XPath是一门在XML文档中查找信息的语言,被用于在XML文档中通过元素和属性进行导航。XPath虽然是被设计用来搜寻XML文档,不过它也能很好地在HTML文档中工作,并且大部分浏览器也支持通过XPath来查询节点。在Python爬虫开发中,经常使用XPath查找提取网页中的信息,因此XPath非常重要。

XPath既然叫Path,就是以路径表达式的形式来指定元素,这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。由于XPath一开始是被用来搜寻XML文档的,所以接下来就以XML文档为例子来讲解XPath。接下来从节点、语法、轴和运算符等四个方面讲解XPath的使用。

1.XPath节点

在XPath中,XML文档是被作为节点树来对待的,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。树的根被称为文档节点或者根节点。以下面的XML文档为例进行说明,文档如下:

<? xml version="1.0" encoding="ISO-8859-1"? >
<classroom>
    <student>
        <id>1001</id>
        <name lang="en">marry</name>
        <age>20</age>
        <country>China</country>
    </student>
</classroom>

上面的XML文档中的节点例子包括:<classroom>(文档节点)、<id>1001</id>(元素节点)、lang="en"(属性节点)、marry(文本)。

接着说一下节点关系,包括父(Parent)、子(Children)、同胞(Sibling)、先辈(Ancestor)、后代(Descendant)。在上面的文档中:

□ student元素是id、name、age以及country元素的父。

□ id、name、age以及country元素都是student元素的子。

□ id、name、age以及country元素都是同胞节点,拥有相同的父节点。

□ name元素的先辈是student元素和classroom元素,也就是此节点的父、父的父等。

□ classroom的后代是id、name、age以及country元素,也就是此节点的子,子的子等。

2.XPath语法

XPath使用路径表达式来选取XML文档中的节点或节点集。节点是沿着路径(path)或者步(steps)来选取的。接下来的重点是如何选取节点,下面给出一个XML文档进行分析:

<? xml version="1.0" encoding="ISO-8859-1"? >
<classroom>
    <student>
        <id>1001</id>
        <name lang="en">marry</name>
        <age>20</age>
        <country>China</country>
    </student>
    <student>
        <id>1002</id>
        <name lang="en">jack</name>
        <age>25</age>
        <country>USA</country>
    </student>
</classroom>

首先列举出一些常用的路径表达式进行节点的选取,如表2-2所示。

表2-2 路径表达式

通过表2-2中的路径表达式,我们尝试着对上面的文档进行节点选取。以表格的形式进行说明,如表2-3所示。

表2-3 节点选取示例

上面选取的例子最后实现的效果都是选取了所有符合条件的节点,是否能选取某个特定的节点或者包含某一个指定的值的节点呢?这就需要用到谓语,谓语被嵌在方括号中,接下来通过表格2-4来解释谓语的用法。

表2-4 谓语示例

XPath在进行节点选取的时候可以使用通配符“*”匹配未知的元素,同时使用操作符“|”一次选取多条路径,还是通过一个表格进行演示,如表2-5所示。

表2-5 通配符“*”与“1”操作符

3.XPath轴

轴定义了所选节点与当前节点之间的树关系。在Python爬虫开发中,提取网页中的信息会遇到这种情况:首先提取到一个节点的信息,然后想在在这个节点的基础上提取它的子节点或者父节点,这时候就会用到轴的概念。轴的存在会使提取变得更加灵活和准确。

在说轴的用法之前,需要了解位置路径表达式中的相对位置路径、绝对位置路径和步的概念。位置路径可以是绝对的,也可以是相对的。绝对路径起始于正斜杠(/),而相对路径不会这样。在两种情况中,位置路径均包括一个或多个步,每个步均被斜杠分割:/step/step/...(绝对位置路径), step/step/...(相对位置路径)。

步(step)包括:轴(axis)、节点测试(node-test)、零个或者更多谓语(predicate),用来更深入地提炼所选的节点集。步的语法为:轴名称::节点测试[谓语],大家可能觉比较抽象,通过之后的示例分析,会明白如何使用它。

表2-6列举了XPath轴中使用的节点集。

表2-6 XPath轴

首先给出一个XML文档,实例分析就按照这个文档来进行,文档如下:

<? xml version="1.0" encoding="ISO-8859-1"? >
<classroom>
    <student>
        <id>1001</id>
        <name lang="en">marry</name>
        <age>20</age>
        <country>China</country>
    </student>
    <student>
        <id>1002</id>
        <name lang="en">jack</name>
        <age>25</age>
        <country>USA</country>
    </student>
    <teacher>
        <classid>1</classid>
        <name lang="en">tom</name>
        <age>50</age>
        <country>USA</country>
    </teacher>
</classroom>

针对上面的文档进行示例演示,如表2-7所示。

表2-7 XPath轴示例分析

4.XPath运算符

XPath表达式可返回节点集、字符串、逻辑值以及数字。表2-8列举了可用在XPath表达式中的运算符。

表2-8 XPath运算符示例分析

2.1.5 JSON

JSON是JavaScript对象表示法(JavaScript Object Notation),用于存储和交换文本信息。JSON比XML更小、更快、更易解析,因此JSON在网络传输中,尤其是Web前端中运用非常广泛。JSON使用JavaScript语法来描述数据对象,但是JSON仍然独立于语言和平台。JSON解析器和JSON库支持许多不同的编程语言,其中就包括Python。

下面主要讲解一下JSON的语法,具体的存储解析放到第5章中进行讲解。JSON语法非常简单,主要包括以下几个方面:

□ JSON名称/值对。JSON数据的书写格式是:名称/值对。名称/值对包括字段名称(在双引号中),紧接着是一个冒号,最后是值。例如,"name" : "qiye",非常像Python中字典。

□ JSON值。JSON值可以是:数字(整数或浮点数)、字符串(在双引号中)、逻辑值(true或false)、数组(在方括号中)、对象(在花括号中)、null。

□ JSON对象。JSON对象在花括号中书写,对象可以包含多个名称/值对。例如:{ "name":"qiye" , "age":"20" },其实就是Python中的字典。

□ JSON数组。JSON数组在方括号中书写,数组可包含多个对象。例如:{"reader":[{ "name":"qiye" , "age":"20"}, { "name":"marry" , "age":"21" }]},这里对象" reader "是包含两个对象的数组。

2.2 HTTP标准

HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更加高效,减少网络传输。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容首先显示(如文本先于图形)等。之后的Python爬虫开发,主要就是和HTTP协议打交道。

2.2.1 HTTP请求过程

HTTP协议采取的是请求响应模型,HTTP协议永远都是客户端发起请求,服务器回送响应。模型如图2-8所示。

图2-8 请求响应模型

HTTP协议是一个无状态的协议,同一个客户端的这次请求和上次请求没有对应关系。一次HTTP操作称为一个事务,其执行过程可分为四步:

□ 首先客户端与服务器需要建立连接,例如单击某个超链接,HTTP的工作就开始了。

□ 建立连接后,客户端发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是MIME信息,包括请求修饰符、客户机信息和可能的内容。

□ 服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息,包括服务器信息、实体信息和可能的内容。

□ 客户端接收服务器所返回的信息,通过浏览器将信息显示在用户的显示屏上,然后客户端与服务器断开连接。

如果以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端,在显示屏输出,这些过程是由HTTP协议自己完成的。

2.2.2 HTTP状态码含义

当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。在浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头(server header)用以响应浏览器的请求。HTTP状态码主要是是为了标识此次HTTP请求的运行状态。下面是常见的HTTP状态码:

□ 200——请求成功。

□ 301——资源(网页等)被永久转移到其他URL。

□ 404——请求的资源(网页等)不存在。

□ 500——内部服务器错误。

HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型。HTTP状态码共分为5种类型,如表2-9所示。

表2-9 HTTP状态码

全部的HTTP状态码的信息,请大家查询HTTP协议标准手册。

2.2.3 HTTP头部信息

HTTP头部信息由众多的头域组成,每个头域由一个域名、冒号(:)和域值三部分组成。域名是大小写无关的,域值前可以添加任何数量的空格符,头域可以被扩展为多行,在每行开始处,使用至少一个空格或制表符。

通过浏览器访问博客园首页时,使用F12打开开发者工具,里面可以监控整个HTTP访问的过程。下面就以访问博客园的HTTP请求进行分析,首先是浏览器发出请求,请求头的数据如下:

GET / HTTP/1.1
Host: www.cnblogs.com
User-Agent:   Mozilla/5.0   (Windows   NT   6.1;   WOW64;   rv:49.0)   Gecko/20100101 Firefox/49.0
Accept: text/html, application/xhtml+xml, application/xml; q=0.9, */*; q=0.8
Accept-Language: zh-CN, zh; q=0.8, en-US; q=0.5, en; q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
If-Modified-Since: Sun, 30 Oct 2016 10:13:18 GMT

在请求头中包含以下内容:

□ GET代表的是请求方式,HTTP/1.1表示使用HTTP 1.1协议标准。

□ Host头域,用于指定请求资源的Intenet主机和端口号,必须表示请求URL的原始服务器或网关的位置。HTTP/1.1请求必须包含主机头域,否则系统会以400状态码返回。

□ User-Agent头域,里面包含发出请求的用户信息,其中有使用的浏览器型号、版本和操作系统的信息。这个头域经常用来作为反爬虫的措施。

□ Accept请求报头域,用于指定客户端接受哪些类型的信息。例如:Accept:image/gif,表明客户端希望接受GIF图象格式的资源;Accept:text/html,表明客户端希望接受html文本。

□ Accept-Language请求报头域,类似于Accept,但是它用于指定一种自然语言。例如:Accept-Language:zh-cn.如果请求消息中没有设置这个报头域,服务器假定客户端对各种语言都可以接受。

□ Accept-Encoding请求报头域,类似于Accept,但是它用于指定可接受的内容编码。例如:Accept-Encoding:gzip.deflate。如果请求消息中没有设置这个域服务器假定客户端对各种内容编码都可以接受。

□ Connection报头域允许发送用于指定连接的选项。例如指定连接的状态是连续,或者指定“close”选项,通知服务器,在响应完成后,关闭连接。

□ If-Modified-Since头域用于在发送HTTP请求时,把浏览器端缓存页面的最后修改时间一起发到服务器去,服务器会把这个时间与服务器上实际文件的最后修改时间进行比较。如果时间一致,那么返回HTTP状态码304(不返回文件内容),客户端收到之后,就直接把本地缓存文件显示到浏览器中。如果时间不一致,就返回HTTP状态码200和新的文件内容,客户端收到之后,会丢弃旧文件,把新文件缓存起来,并显示到浏览器中。

请求发送成功后,服务器进行响应,接下来看一下响应的头信息,数据如下:

HTTP/1.1200 OK
Date: Sun, 30 Oct 2016 10:13:50 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
Cache-Control: public, max-age=3
Expires: Sun, 30 Oct 2016 10:13:54 GMT
Last-Modified: Sun, 30 Oct 2016 10:13:24 GMT
Content-Encoding: gzip

响应头中包含以下内容:

□ HTTP/1.1表示使用HTTP 1.1协议标准,200 OK说明请求成功。

□ Date表示消息产生的日期和时间。

□ Content-Type实体报头域用于指明发送给接收者的实体正文的媒体类型。text/html;charset=utf-8代表HTML文本文档,UTF-8编码。

□ Transfer-Encoding:chunked表示输出的内容长度不能确定。

□ Connection报头域允许发送用于指定连接的选项。例如指定连接的状态是连续,或者指定“close”选项,通知服务器,在响应完成后,关闭连接。

□ Vary头域指定了一些请求头域,这些请求头域用来决定当缓存中存在一个响应,并且该缓存没有过期失效时,是否被允许利用此响应去回复后续请求而不需要重复验证。

□ Cache-Control用于指定缓存指令,缓存指令是单向的,且是独立的。请求时的缓存指令包括:no-cache(用于指示请求或响应消息不能缓存)、no-store、max-age、max-stale、min-fresh、only-if-cached;响应时的缓存指令包括:public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age、s-maxage。

□ Expires实体报头域给出响应过期的日期和时间。为了让代理服务器或浏览器在一段时间以后更新缓存中(再次访问曾访问过的页面时,直接从缓存中加载,缩短响应时间和降低服务器负载)的页面,我们可以使用Expires实体报头域指定页面过期的时间。

□ Last-Modified实体报头域用于指示资源的最后修改日期和时间。

□ Content-Encoding实体报头域被用作媒体类型的修饰符,它的值指示了已经被应用到实体正文的附加内容的编码,因而要获得Content-Type报头域中所引用的媒体类型,必须采用相应的解码机制。

从上面分析的过程中,大家基本上了解了请求和响应的头信息,最后进行一下总结:

HTTP消息报头主要包括普通报头、请求报头、响应报头、实体报头。具体如下:

1)在普通报头中,有少数报头域用于所有的请求和响应消息,但并不用于被传输的实体,只用于传输的消息。

2)请求报头允许客户端向服务器端传递请求的附加信息以及客户端自身的信息。

3)响应报头允许服务器传递不能放在状态行中的附加响应信息,以及关于服务器的信息和对Request-URI所标识的资源进行下一步访问的信息。

4)请求和响应消息都可以传送一个实体。一个实体由实体报头域和实体正文组成,但并不是说实体报头域和实体正文要在一起发送,可以只发送实体报头域。实体报头定义了关于实体正文和请求所标识的资源的元信息。

通过表2-10~表2-13对报文头进行分类列举说明。

表2-10 常见普通报头

表2-11 常见请求报头

表2-12 常见响应报头

表2-13 常见实体报头

2.2.4 Cookie状态管理

Cookie和Session都用来保存状态信息,都是保存客户端状态的机制,它们都是为了解决HTTP无状态的问题所做的努力。对于爬虫开发来说,我们更加关注的是Cookie,因为Cookie将状态保存在客户端,Session将状态保存在服务器端。

Cookie是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器。网络服务器用HTTP头向客户端发送Cookie,浏览器则会解析这些Cookie并将它们保存为一个本地文件,它会自动将同一服务器的任何请求绑定上这些Cookie。

Cookie的工作方式:服务器给每个Session分配一个唯一的JSESSIONID,并通过Cookie发送给客户端。当客户端发起新的请求的时候,将在Cookie头中携带这个JSESSIONID。这样服务器能够找到这个客户端对应的Session,流程如图2-9所示。

图2-9 Cookie工作流程

2.2.5 HTTP请求方式

HTTP的请求方法包括如下几种:

□ GET

□ POST

□ HEAD

□ PUT

□ DELETE

□ OPTIONS

□ TRACE

□ CONNECT

其中常用的请求方式是GET和POST:

□ GET方式:是以实体的方式得到由请求URL所指定资源的信息,如果请求URL只是一个数据产生过程,那么最终要在响应实体中返回的是处理过程的结果所指向的资源,而不是处理过程的描述。

□ POST方式:用来向目的服务器发出请求,要求它接受被附在请求后的实体,并把它当作请求队列中请求URL所指定资源的附加新子项。

GET与POST方法有以下区别:

□ 在客户端,Get方式通过URL提交数据,数据在URL中可以看到:POST方式,数据放置在实体区内提交。

□ GET方式提交的数据最多只能有1024字节,而POST则没有此限制。

□ 安全性问题。使用Get的时候,参数会显示在地址栏上,而Post不会。所以,如果这些数据是非敏感数据,那么使用Get;如果用户输入的数据包含敏感数据,那么还是使用Post为好。

在爬虫开发中基本处理的也是GET和POST请求。GET请求在访问网页时很常见,POST请求则是常用在登录框、提交框的位置。下面展示一个完整的POST请求,这是登录知乎社区时捕获的请求,上面一部分是请求头,下面全部加粗的数据是请求实体。请求内容如下:

POST /login/phone_num HTTP/1.1
Host: www.zhihu.com
User-Agent:   Mozilla/5.0   (Windows   NT   6.1;   WOW64;   rv:49.0)   Gecko/20100101 Firefox/49.0
Accept: */*
Accept-Language: zh-CN, zh; q=0.8, en-US; q=0.5, en; q=0.3
Accept-Encoding: gzip, deflate, br
X-Xsrftoken: ade0896dc13cc3b2204a8f7742ad7f48
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: https://www.zhihu.com/
Content-Length: 117
Cookie:q_c1=7bc53a12dd7942d3b64776441ab69983|1477975324000|1465870098000;
    d_c0="ACAAa1M-EwqPTgdv2RIP3IIzHO2R7zKBGpw=|1465870098"; __utma=51854390.1118849962.1465870098.1466355392.1477975328.3; __utmz=51854390.1465870098.1.1. utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utmv=51854390.000--|3=entr y_date=20160614=1; _zap=7514ab27-5b42-4c95-a4cc-e31ce5757e14; _za=4ab6eb3b-c 34f-4772-aac2-7182f21894cb; _xsrf=ade0896dc13cc3b2204a8f7742ad7f48; l_cap_id="ZjBkODkyYjdiZWZkNDQ2NWE4YzI1ZTk3Njc0MDZlMWM=|1477975324|95c5032340720551391178c9ee67cd8a3e2849d5"; cap_id="ZjAxNjBmNzU5NzZkNDI2ZTlkYTk3ZDVlNDNhNzgy ZTA=|1477975324|0616dfa45cd15d66fe792484c6ae0af71557cb3c"; n_c=1; __utmb=51854390.2.10.1477975328; __utmc=51854390; __utmt=1;
    login="ZWU1NTFlM2EzYzg4NDNjNzlhODY
    wN2ZhYzgyZmExOTE=|1477975348|735a805117328df9e557f0126eb348e7712e310c"
Connection: keep-alive

_xsrf=ade0896dc13cc3b2204a8f7742ad7f48&password=xxxxxxxx&captcha_type=cn&rememb er_me=true&phone_num=xxxxxxxxx

2.3 小结

本章主要讲解了Web前端中标记语言、脚本语言和HTTP的基本概念,在这些知识中,重点掌握HTML标记语言、XPath路径表达式的书写和HTTP请求流程,这对接下来的Python爬虫开发有着非常直接的作用,有助于爬虫开发的快速入门。本章讲解的只是Web前端的基础知识,希望大家有时间系统地学习Web前端的知识,这样对之后涉及协议分析和反爬虫措施的应对方面有很大帮助。