参考 《Python 3网络爬虫开发实战 》崔庆才著
HTTP
URI的全称为 Uniform Resource Identifier,即统一资源标志符
URL的全称为 Universal Resource Locator,即统一资源定位符
URL是URI的子集,也就是说每个URL都是URI,但不是每个URI都是URL。那么,怎样的URI不是URL呢?URI还包括一个子类叫作URN,它的全称为 Universal Resource Name,即统一资源名称。URN只命名资源而不指定如何定位资源,比如um:isbn:0451450523指定了一本书的ISBN可以唯一标识这本书,但是没有指定到哪里定位这本书,这就是URN.URL、URN和URI的关系可
协议类型http与https
HTTP的全称是 Hyper Text Transfer Protocol,中文名叫作超文本传输协议
HTTPS的全称是Hyper Text Transfer Protocol over Secure Socket Layer,是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,简称为HTTPS,安全基础是SSLSSL主要作用分两种:
- 建立一个信息安全通道来保证数据传输的安全
- 确认网站的真实性,凡是使用了HTTPS的网站,都可以通过点击浏览器地址栏的锁头标志来查看网站认证之后的真实信息,也可以通过CA机构(证书颁发机构(CA, Certificate Authority))颁发的安全签章来查询。
-
http请求
- 第一列Name:请求的名称,一般会将URL的最后一部分内容当作名称
- 第二列 Status:响应的状态码,这里显示为200,代表响应是正常的。通过状态码,我们可以判断发送了请求之后是否得到了正常的响应。
- 第三列Type:请求的文档类型。 document,代表我们这次请求的是一个HTML文档,内容就是一些HTML代码
- 第四列 Initiator:请求源。用来标记请求是由哪个对象或进程发起的。
- 第五列Size:从服务器下载的文件和请求的资源大小。如果是从缓存中取得的资源,则该列会显示 from cache
- 第六列Time:发起请求到获取响应所用的总时间。
- 第七列 Waterfal:网络请求的可视化瀑布流
点击这个条目,会出现
-
首先是 General部分
- Request URL为请求的URL,
- Request Method为请求的方法,
- Status Code为响应状态码,
- Remote Address为远程服务器的地址和端口,
- Referrer Policy为 Referrer判别策略。
-
可以看到,有 Response Headers和 Request Headers,这分别代表响应头和请求头。
请求头里带有许多请求信息,例如浏览器标识、 Cookies、Host等信息,这是请求的一部分,服务器会根据请求头内的信息判断请求是否合法,进而作出对应的响应。
图中看到的 Response Headers就是响应的一部分,例如其中包含了服务器的类型、文档类型、日期等信息,浏览器接受到响应后,会解析响应内容,进而呈现网页内容。
请求
请求,由客户端向服务端发出,可以分为4部分内容:
- 请求方法( Request Method)、
- 请求的网址Request URL)、
- 请求头( Request Headers)、
- 请求体( Request Body)
请求方法
GET和POST
- GET请求中的参数包含在URL里面,数据可以在URL中看到,而POST请求的URL不会包含这些数据,数据都是通过表单形式传输的,会包含在请求体中
- GET请求提交的数据最多只有1024字节,而POST方式没有限制。
请求网址
即URL
请求头
请求头,用来说明服务器要使用的附加信息,比较重要的信息有 Cookie、 Referer、User- Agent等下面简要说明一些常用的头信息。
-
Accept:请求报头域,用于指定客户端可接受哪些类型的信息。
-
Accept-Language:指定客户端可接受的语言类型。
-
Accept-Encoding:指定客户端可接受的内容编码。
-
Host:用于指定请求资源的主机IP和端口号,其内容为请求URL的原始服务器或网关的位置。从HTTP1.1版本开始,请求必须包含此内容。
-
Cookie:也常用复数形式 Cookies,这是网站为了辨别用户进行会话跟踪而存储在用户本地的数据。它的主要功能是维持当前访问会话。
例如,我们输入用户名和密码成功登录某个网站后,服务器会用会话保存登录状态信息,后面我们每次刷新或请求该站点的其他页面时,会发现都是登录状态,这就是 Cookies的功劳。 Cookies里有信息标识了我们所对应的服务器的会话,每次浏览器在请求该站点的页面时,都会在请求头中加上 Cookies并将其发送给服务器,服务器通过 Cookies识别出是我们自己,并且查出当前状态是登录状态,所以返回结果就是登录之后才能看到的网页内容。
-
Referer:此内容用来标识这个请求是从哪个页面发过来的,服务器可以拿到这一信息并做相应的处理,如做来源统计、防盜链处理等。
-
User- Agent:简称UA,它是一个特殊的字符串头,可以使服务器识别客户使用的操作系统及版本、浏览器及版本等信息。在做爬虫时加上此信息,可以伪装为浏览器;如果不加,很可能会被识别出为爬虫。
-
Content-Type:也叫互联网媒体类型(InternetMediaType)或者MIME类型,在HTTP协议消息头中,它用来表示具体请求中的媒体类型信息。例如, text/html代表HTML格式, image/gif代表GF图片, application/json代表JSON类型,更多对应关系可以查看此对照表:
http://tool.oschina.net/commons
请求体
请求体一般承载的内容是POST请求中的表单数据,而对于GET请求,请求体则为空。
此时需要注意 Request Headers中指定 Content-Type为 application/x-ww-form- urlencode。只有设置Content-Type为 application/x-www-form- - urlencode,才会以表单数据的形式提交。另外,我们也可以将 Content-Type设置为 application/json来提交JSON数据,或者设置为 multipart/form-data来上传文件。
响应
响应,由服务端返回给客户端,可以分为三部分:响应状态码(Response Status Code)、响应头(Response Headers)和响应体( Response Body)。
状态码
响应头
响应头包含了服务器对请求的应答信息,如 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:指定响应的过期时间,可以使代理服务器或浏览器将加载的内容更新到缓存中。如果再次访问时,就可以直接从缓存中加载,降低服务器负载,缩短加载时间。
网页基础
网页可以分为三大部分—HTML、CSS和 JavaScript.如果把网页比作一个人的话,HTML相当于骨架, Javascript相当于肌肉,CSs相当于皮肤,三者结合起来才能形成一个完善的网页。下面我们分别来介绍一下这三部分的功能。
-
HTML
Hyper Text Markup Language,即超文本标记语言
-
CSS
Cascading Style Sheets,即层叠样式表。
HTML中,只需要用 link 标签即可引入写好的 css 文件
-
JavaScript
实现实时、动态、交互的页面功能。
在 HTML 中通过 script 标签即可引入,
<script src: "jquery-2.1.o.js" ></script>
<IDOCTYPE html> <!--定义文档类型-->
<html><!--最外层的html标签-->
<head><!--网页头,定义页面的配置和引用-->
<meta charset="UTF-8"><!--指定网页的编码-->
<title>this is a Demo</titley
</head>
<body><!--网页的内容 -->
<div id="container"><!--网页的区块,id为container,这是一个非常常用的属性,id内容唯一-->
<div class="wrapper"><!--区块内的区块,它的class为wrapper,也是一个常用的属性,经常和css配合使用-->
<h2 class="title">Hello World</h2>
<p class="text">Hello, this is a paragraph. </p>
</div>
</div>
</body>
</html>
DOM
节点树及节点间的关系
在HTML中,所有标签定义的内容都是节点,它们构成了一个 HTML DOM(Document Object Model(文档对象模型))树。定义了访问HTML和XML文档的标准。
W3C文档对象模型(DOM)是中立于平台和语言的接口,它允许程序和脚本动态地访问和更新文档的内容、结构和样式。
- 核心DOM:针对任何结构化文档的标准模型。
- XML DOM:针对XML文档的标准模型。
- HTML DOM:针对HTML文档的标准模型。
根据HTMLDOM标准,HTML文档中的所有内容都是节点
- 整个文档是一个文档节点。
- 每个HTML元素是元素节点。
- HTML元素内的文本是文本节点。
- 每个HTML属性是属性节点。
- 注释是注释节点。
通过 HTML DOM,树中的所有节点均可通过 JavaScript访问,所有HTML节点元素均可被修改,也可以被创建或删除。
节点树中的节点彼此拥有层级关系。我们常用父( parent)、子(child)和兄弟( sibling)等术语描述这些关系。父节点拥有子节点,同级的子节点被称为兄弟节点。
在节点树中,顶端节点称为根(root)。除了根节点之外,每个节点都有父节点,同时可拥有任意子节点和兄弟节点。
<!DOCTYPE html>
<html>
<body>
<p id="intro">Hello World!</p>
<p>本例演示 <b>getElementById</b> 方法!</p>
<script>
x=document.getElementById("intro");//得到id为intro的元素,x是元素
document.write("<p>来自 intro 段落的文本:" + x.innerHTML + "</p>");
</script>
</body>
</html>
DOM 常用方法和属性
HTML DOM方法 | 描述 |
---|---|
getElementById(id) | 返回带有指定 ID 的元素 |
getElementsByTagName() | 返回包含带有指定标签名称的所有元素的节点列表(集合/节点数组)。 |
getElementsByClassName() | 返回包含带有指定类名的所有元素的节点列表。 |
appendChild(node) | 插入新的子节点(元素)/将新元素作为父元素的最后一个子元素进行添加。 |
父节点.removeChild(node) | 删除子节点(元素) |
replaceChild() | 替换子节点。 |
父节点.insertBefore(新节点,指定节点) | 在指定的子节点前面插入新的子节点。 |
createAttribute() | 创建属性节点。 |
createElement() | 创建元素节点。 |
createTextNode() | 创建文本节点。 |
getAttribute() | 返回指定的属性值。 |
setAttribute() | 把指定属性设置或修改为指定的值。 |
HTML DOM 属性 | 描述 |
---|---|
innerHTML | 节点(元素)的文本值 |
nodeName | nodeName 是只读的 元素节点的 nodeName 与标签名相同 属性节点的 nodeName 与属性名相同 文本节点的 nodeName 始终是 #text 文档节点的 nodeName 始终是 #document |
nodeValue | 元素节点的 nodeValue 是 undefined 或 null 文本节点的 nodeValue 是文本本身 属性节点的 nodeValue 是属性值 |
parentNode | 节点(元素)的父节点 |
firstChild | 节点(元素)的第一个子节点 |
lastChild | 节点(元素)的最后一个子节点 |
childNodes | 节点(元素)的子节点 |
attributes | 节点(元素)的属性节点 |
document.documentElement | 全部文档 |
document.body | 文档的主体 |
DOM事件
当事件发生时,可以执行 JavaScript,比如当用户点击个HTML元素时。
如需在用户点击某个元素时执行代码,请把 JavaScript代码添加到HTML事件属性中
HTML事件的例子
- 当用户点击鼠标时
onclick=" 函数"
onmousedown、onmouseup 以及 onclick 事件是鼠标点击的全部过程。
首先当某个鼠标按钮被点击时,触发 onmousedown 事件
然后,当鼠标按钮被松开时,会触发 onmouseup 事件
最后,当鼠标点击完成时,触发 onclick 事件。 - 当网页已加载时
onload="函数"
onload 事件可用于检查访客的浏览器类型和版本,以便基于这些信息来加载不同版本的网页。
onload 和 onunload 事件可用于处理 cookies。 - 当图片已加载时
- 当鼠标移动到元素上时
onmouseover="函数"
onmouseover 和 onmouseout 事件可用于在鼠标指针移动到或离开元素时触发函数。 - 当输入字段被改变时
onchange="函数"
onchange 事件常用于输入字段的验证。 - 当HTML表单被提交时
- 当用户触发按键时
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>点击按钮来执行 <b>displayDate()</b> 函数。</p>
<button id="myBtn">试一试</button>
<script>//DOM通过js给HTML分配onclick事件
document.getElementById("myBtn").onclick=function(){displayDate()};
function displayDate()
{
document.getElementById("demo").innerHTML=Date();
}
</script>
<p id="demo"></p>
</body>
</html>
选择器
在CSS中,我们使用CSS选择器来定位节点。
- 上例中div节点的id为 container,那么就可以表示为#container,其中#开头代表选择id,其后紧跟id的名称。
- 另外,如果我们想选择 class为 wrapper的节点,便可以使用. wrapper,这里以点(.)开头代表选择class,其后紧跟 class的名称
- 还有一种选择方式,那就是根据标签名筛选,例如想选择二级标题,直接用h2即可。
这是最常用的3种表示,分别是根据id、 class、标签名筛选,请牢记它们的写法。
另外,CSS选择器还支持嵌套选择,各个选择器之间加上空格分隔开便可以代表嵌套关系,如#container .wrapper p则代表先选择id为 container的节点,然后选中其内部的cass. wrapper的节点,然后再进一步选中其内部的p节点。
另外,如果不加空格,则代表并列关系,如div#container .wrapper p.text代表先选择id为 container的div节点,然后选中其内部的class为 wrapper节点,再进步选中其内部的 class为text的p节点。这就是CSS选择器,其筛选功能还是非常强大的。
爬虫的基本原理
-
获取网页
前面讲了请求和响应的概念,向网站的服务器发送一个请求,返回的响应体便是网页源代码。所以,最关键的部分就是构造 个请求并发送给服务器,然后接收到响应并将其解析出来
Python 提供了许多库来帮助我们实现这个操作,如 urllib requests 我们可以用这些库来帮助我 HTTP 请求操作,请求和响应都可以用类库提供的数据结构来表示,得到响应之后只需要解析数据结构中 Body 部分即可,即得到网页的源代码,这样我们可以用程序来实现获取网页的过程了 -
提取信息
获取网页源代码后,接下来就是分析网页源代码,从中提取我们想要的数据 首先,最通用的方法便是采用正则表达式提取,这是一个万能的方法,但是在构造正则表达式时比较复杂且容易出错,另外,由于网页的结构有一定的规 ,所以还有一些根据网页节点属性、 css 选择器或 XPath提取网页信息的库,如 Beautiful Soup pyquery lxml 使用这些库,我们可以高效快速地从中提取网页信息,如节点的属性 文本值等
-
保存数据
可以简单保存为 TXT 文本或 JSON 文本,也可以保存到数据库,如 MySQL MongoDB ,也可保存至远程服务器,如借 SFTP 进行操作 -
自动化程序
JavaScript 渲染页面
有时候,用 urllib requests“抓取网页时,得到的源代码实际和浏览器中看到的不一样。
这是个非常常见的问题 现在网页越来越多地采用 Ajax 、前端模块化工具来构建,整个网页可能都是由 JavaScript 渲染出来的,也就是说原始的 HTML 代码就是一个空壳,
使用基本HTTP请求库得到的源代码可能跟浏览器中的页面源代码不太一样。对于这样的情况,我们可以分析其后台Ajax接口,也可使用 Selenium、 Splash这样的库来实现模拟 JavaScript渲染。
会话和cookies
ookie是保存在客户端的纯文本文件。
当我们使用自己的电脑通过浏览器进行访问网页的时候,服务器就会生成一个证书并返回给我的浏览器并写入我们的本地电脑。这个证书就是cookie
那么cookie 到底是怎么工作的呢?
首先当我们访问某个网站时,服务器首先根据浏览器的编号生成一个cookie 返回给客户端。客户端下次再访问时就会将自己本地的cookie 加上url访问地址一同给服务器。服务器读出来以此来辨别用户的状态
cookie包含两种类型
一种是保存在内存中的cookie。这种一般是服务端没有设置生存周期,也就是maxage为负数或者0 。这种cookie只在当前会话中有效,关掉浏览器窗口后cookie就立即失效。
还有一种是保存在本地客户端的cookie,这种一般是服务器设置了cookie的生存周期maxage为正数,不管你关掉窗口还是重新打开浏览器,还是重启机器,只要cookie不失效,那么访问此网站时,浏览器就会找对应的webapplication的cookies(自己写入的)。
存储在本地文件的cookie可以被多个ie浏览器窗口共享,意思就是说,如果我打开一个新的浏览器窗口,输入网页地址,我们只能看到本地文件的cookie,而上一个窗口中的内存中的cookie不能再新窗口中看到。如果我们在父窗口打开一个子窗口,子窗口会继承父窗口的所有类型的cookie。
-
会话维持
那么,我们怎样利用 Cookies保持状态呢?
当客户端第一次请求服务器时,服务器会返回一个请求头中带有Set-Cookie字段的响应给客户端,用来标记是哪一个用户,客户端浏览器会把 Cookies保存起来。
当浏览器下一次再请求该网站时,浏览器会把此 Cookies放到请求头一起提交给服务器, Cookies携带了会话ID信息,服务器检查该 Cookies即可找到对应的会话是什么,然后再判断会话来以此来辨认用户状态。 -
cookies属性结构
- Name:该 Cookie的名称。一旦创建,该名称便不可更改。
- value:该 Cookie的值。如果值为 Unicode字符,需要为字符编码。如果值为二进制数据,则需要使用BASE64编码。
- Domain:可以访问该Cookie的域名。例如,如果设置为,zhihu.com,则所有以zhihu.com结尾的域名都可以访问该 Cookie
- Max Age:该 Cookie失效的时间,单位为秒,也常和 Expires一起使用,通过它可以计算出其有效时间。
Max Age如果为正数,则该 Cookie在 Max Age秒之后失效。
如果为负数,则关闭浏览器时 Cookie即失效,浏览器也不会以任何形式保存该 Cookie - Path:该 Cookie的使用路径。如果设置为/path,则只有路径为/path的页面可以访问该Cookie。如果设置为/,则本域名下的所有页面都可以访问该 Cookie
- size字段:此 Cookie的大小。
- HTTP字段:Cookie的httponly属性。若此属性为true则只有在HTTP头中会带有此Cookie的信息,而不能通过 document. cookie来访问此 Cookie
- Secure:该 Cookie是否仅被使用安全协议传输。安全协议有 Https和SSL等,在网络上传输数据之前先将数据加密。默认为 false。
代理(proxy server)
封ip:服务器会检IP在单位时间内的请求次数,如果超过了这个阈值,就会直接拒绝服务,返回一些错误信息,比如403 Forbidden,这时候打开网页一看,能会看到“您的IP访问频率太高”这样的提示。
IP 伪装:代理的基本原理
- 突破自身IP访问限制,访问一些平时不能访问的站点。
- 访问一些单位或团体内部资源:比如使用教育网内地址段免费代理服务器,就可以用于对教育网开放的各类FTP下载上传,以及各类资料查询共享等服务。
- 提高访问速度:通常代理服务器都设置一个较大的硬盘缓冲区,当有外界的信息通过时,同时也将其保存到缓冲区中,当其他用户再访问相同的信息时,则直接由缓冲区中取出信息,传给用户,以提高访问速度。
- 隐藏真实IP:上网者也可以通过这种方法隐藏自己的IP,免受攻击。对于爬虫来说,我们用代理就是为了隐藏自身IP,防止自身的P被封锁。
对于爬虫来说,由于爬虫爬取速度过快,在爬取过程中可能遇到同一个P访问过于频繁的问题,此时网站就会让我们输入验证码登录或者直接封锁P,这样会给爬取带来极大的不便使用代理隐藏真实的P,让服务器误以为是代理服务器在请求自己。这样在爬取过程中通过不断更换代理,就不会被封锁,可以达到很好的爬取效果。
代理分类
- 根据协议分类
- FTP代理服务器:主要用于访问FTP服务器,一般有上传、下载以及缓存功能,端口一般为21、2121等。
- HTTP代理服务器:主要用于访问网页,一般有内容过滤和缓存功能,端口一般为808080、3128等
- SSL/TLS代理:主要用于访问加密网站,一般有SSL或TLS加密功能(最高支持128位加密强度),端口一般为443。
- RTSP代理:主要用于访问Real流媒体服务器,一般有缓存功能,端口一般为554
- Telnet代理:主要用于 telnet远程控制(黑客入侵计算机时常用于隐藏身份),端口一般为23。
- POP3/SMTP代理:主要用于POP3SMTP方式收发邮件,一般有缓存功能,端口一般为110/25
- SOCKS代理:只是单纯传递数据包,不关心具体协议和用法,所以速度快很多,一般有缓存功能,端口一般为1080。
S0CKS代理协议又分为SOCKS4和 SOCKS5,前者只支持TCP,而后者支持TCP和UDP,还支持各种身份验证机制、服务器端域名解析等。简单来说, SOCKS4能做到的 SOCKSS都可以做到,但 SOCKS5能做到的 SOCKS4不一定能做到。
- 根据匿名程度
- 高度匿名代理:会将数据包原封不动地转发,在服务端看来就好像真的是一个普通客户端在访问,而记录的IP是代理服务器的IP。
- 普通匿名代理:会在数据包上做一些改动,服务端上有可能发现这是个代理服务器,也有一定几率追查到客户端的真实IP。代理服务器通常会加入的HTTP头有HTTP_ⅥA和 HTTP_X_FORWARDED_FOR
- 透明代理:不但改动了数据包,还会告诉服务器客户端的真实IP。这种代理除了能用缓存技术提高浏览速度,能用内容过滤提高安全性之外,并无其他显著作用,最常见的例子是内网中的硬件防火墙。
- 间谍代理:指组织或个人创建的用于记录用户传输的数据,然后进行研究、监控等目的的代理服务器。
代理设置
- 使用网上的免费代理:最好使用高匿代理,另外可用的代理不多,需要在使用前筛选一下可用代理,也可以进一步维护一个代理池。
- 使用付费代理服务:互联网上存在许多代理商,可以付费使用,质量比免费代理好很多。
- ADSL拨号:拨一次号换一次P,稳定性高,也是一种比较有效的解决方案。