爬虫
-
爬虫原理与数据抓取
-
通用爬虫和聚焦爬虫
-
背景
现在是"大数据时代",那数据从何而来?
-
什么是网络爬虫
网络爬虫,又称为网页蜘蛛、网络机器人,是一种按照一定的规则,自动请求万维网网站并提取网络数据的程序或脚本。如果说网络像一张网,那么爬虫就是网上的一只小虫子,在网上爬行的过程中遇到了数据,就把它抓取下来。这里的数据是指互联网上公开的并且可以访问到的网页信息,而不是网站的后台信息(没有权限访问),更不是用户注册的信息(非公开的)。
根据使用场景,网络爬虫可分为 通用爬虫 和 聚焦爬虫 两种:
-
通用爬虫
通用网络爬虫 是 捜索引擎抓取系统(Baidu、Google、Yahoo等)的重要组成部分。主要目的是将互联网上的网页下载到本地,形成一个互联网内容的镜像备份
-
聚焦爬虫
聚焦爬虫(Focused Crawler),又称主题网络爬虫(Topical Crawler),是指选择性地爬行那些与预先定义好的主题相关的页面的网络爬虫。和通用爬虫相比,聚焦爬虫只需要爬行与主题相关的页面,从而极大地节省了硬件和网络资源,保存的页面也由于数量少而更新快,还可以很好地满足一些特定人群对特定领域信息的需求。
-
-
爬虫及网页请求原理
-
爬虫实现原理
-
通用爬虫工作原理
通用爬虫是一个自动提取网页的程序,它为搜索引擎从Internet网上下载网页,是搜索引擎的重要组成部分。通用爬虫从一个或若干初始网页的URL开始,获得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件。
通用网络爬虫从互联网中搜集网页,采集信息,这些网页信息用于为搜索引擎建立索引提供支持,它决定着整个引擎系统的内容是否丰富,信息是否及时,因此其性能的优劣直接影响着搜索引擎的效果。但是,用于搜索引擎的通用爬虫其爬行行为需要符合一定的规则,遵从一些命令或文件的内容,如标注为nofollow的链接,或者是Robots协议。
Robots协议(也叫爬虫协议、机器人协议等),全称是“网络爬虫排除标准”(Robots Exclusion Protocol),网站通过Robots协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取,例如:
淘宝网:https://www.taobao.com/robots.txt
腾讯网: http://www.qq.com/robots.txt
-
聚焦爬虫工作原理
与通用爬虫相比,聚焦爬虫的工作流程较为复杂,需要根据一定的网页分析算法过滤与主题无关的链接,保留有用的链接,并将其放入等待抓取的URL队列。然后,它将根据一定的搜索策略从队列中选择下一步要抓取的网页URL,并重复上述过程,直到达到系统的某一条件时停止。聚焦爬虫即是我们需要学习的。
-
爬虫抓取网页流程
爬虫抓取网页数据的详细流程,它的主要步骤包括:
(1) 首先选取一些网页,将这些网页的链接地址作为种子URL;
(2) 将这些种子URL放入到待抓取URL队列中;
(3) 爬虫从待抓取URL队列(队列先进先出)中依次读取URL,并通过DNS解析URL,把链接地址转换为网站服务器所对应的IP地址;
(4) 将IP地址和网页相对路径名称交给网页下载器,网页下载器负责页面内容的下载;
(5) 网页下载器将相应网页的内容下载到本地;
(6) 将下载到本地的网页存储到页面库中,等待建立索引等后续处理;与此同时将下载过网页的URL放入到已抓取URL队列中,这个队列记载了爬虫系统已经下载过的网页URL,以避免网页的重复抓取;
(7) 对于刚刚下载的网页,从中抽取出所包含的所有链接信息,并在已抓取URL中检查其是否被抓取过,如果还未被抓取过,则将这个URL放入到待抓取URL队列中;
(8) 下载被放入待抓取URL队列中的URL对应的网页,如此重复3-7,形成循环,直到待抓取URL队列为空。
-
-
网页请求原理
-
浏览器加载网页过程
网络爬虫抓取数据的过程可以理解为模拟浏览器操作的过程,我们有必要了解浏览器浏览网页的基本过程。例如,在浏览器的地址栏输入网址“
http://www.baidu.com
”,按下回车键后会在浏览器中显示百度的首页。那么,这段网络访问过程中到底发生了什么?简单来说,浏览网页的过程可分为以下四个步骤:
(1) 浏览器通过DNS服务器查找域名对应的IP地址;
(2) 向IP地址对应的Web服务器发送请求;
(3) Web服务器响应请求,发回HTML页面;
(4) 浏览器解析HTML内容,并显示出来。
-
统一资源定位符url
URL(Uniform Resource Locator,统一资源定位符)是互联网上标准资源的地址,互联网上每个文件(即资源)都有一个唯一的URL,它包含了文件的位置以及浏览器处理方式等信息。
URL地址由协议头、服务器地址、文件路径三部分组成。比如,一个典型的URL地址
http://127.0.0.1:8080/subject/pythonzly/index.shtml
1.协议头(Protocol Head)
协议头指定使用的传输协议,用于告诉浏览器如何处理将要打开的文件。不同的协议表示不同的资源查找以及传输方式。网络上常用的协议如表1所示。
表1 URL常见的协议| 常见协议 | 代表类型 | 示例 |
| ------------ | --------------------------------------- | -------------------------------------------------------- |
| file | 访问本地计算机的资源 | file:///Users/itcast/Desktop/basic.html |
| ftp | 访问共享主机的文件资源 | ftp://ftp.baidu.com/movies |
| http | 超文本传输协议, 访问远程网络资源 | http://image.baidu.com/channel/wallpaper |
| https | 安全的ssl加密传输协议,访问远程网络资源 | https://image.baidu.com/channel/wallpaper |
| mailto | 访问电子邮件地址 | mailto:null@itcast.cn |其中最常用的是HTTP协议和HTTPS协议,分别由协议头http和https指定。
2.服务器地址(Hostname或IP)和端口(Port)
服务器地址指存放资源的服务器的主机名或者IP地址,其目的在于标识互联网上的唯一一台计算机,通过这个地址来找到这台计算机。
端口是在地址和冒号后面的数字,用于标识在一台计算机上运行的不同程序。每个网络程序,都对应一个或多个特定的端口号,例如HTTP程序的默认端口号为80,HTTPS程序的默认端口号为443。
IP地址被用来给Internet上的每台电脑一个编号,但是IP地址不容易记忆,而且服务器的物理IP地址是有可能发生改变的。为此,人们又发明了域名来替代IP地址访问服务器的网站。例如,使用百度公司所在的IP地址“
http://180.97.33.107
”可以打开百度的首页,但是这个地址不易记忆,不如使用域名网址http://www.baidu.com
访问来的方便。3.路径(Path)
路径是由0或者多个“/”符号隔开的字符串,一般用于指定本次请求的资源在服务器中的位置。
-
计算机域名系统DNS
DNS 是计算机域名系统(Domain Name System或Domain Name Service)的缩写,由解析器和域名服务器组成。
域名服务器保存有该网络中所有主机的域名和对应IP地址,并具有将域名转换为IP地址的功能。
在之前介绍URL时提到过,人们习惯用域名来访问网站。此时,浏览器需要首先访问域名服务器,从域名服务器查找该域名对应的服务器IP地址,然后再向该IP地址对应的Web服务器发送资源请求。一般一个域名的DNS解析时间在10~60毫秒之间。
要注意的是,一个域名必须对应一个IP地址,而一个IP地址可能对应零到多个域名。即一个IP地址可能没有申请域名,也可能同时对应多个域名
-
HTTP&HTTPS网络请求原理
当用户在浏览器的地址栏中输入一个URL地址并按回车键之后,浏览器会向HTTP服务器发送HTTP请求。常用的HTTP请求包括 Get 和 Post 两种方式。例如,当我们在浏览器输入URL :
http://www.baidu.com
的时候,浏览器发送一个Request请求去获取http://www.baidu.com
的html文件,服务器把包含了该文件内容的Response发送回给浏览器。浏览器分析Response对象中的html文件内容,发现其中引用了很多其他文件,包括Images文件,CSS文件,JS文件等。 浏览器会自动再次发送Request去获取这些图片,CSS文件,或者JS文件。当所有的文件都下载成功后,浏览器会根据HTML语法结构,将网页完整的显示出来。
-
客户端http请求格式
在网络传输中HTTP协议非常重要,该协议规定了客户端和服务器端请求和应答的标准。HTTP协议能保证计算机正确快速地传输超文本文档,还确定了传输文档中的哪一部分,以及哪部分内容首先显示(如文本先于图形)等。根据HTTP协议的规定,客户端发送一个HTTP请求到服务器的请求消息,由请求行、请求头部、空行、以及请求数据这四个部分组成。
接下来,我们结合一个典型的HTTP请求的示例,为大家详细地介绍HTTP请求信息的各个组成部分。示例内容如下所示。
-
-
GET https://www.baidu.com/ HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8
Referer: http://www.baidu.com/
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: BAIDUID=04E4001F34EA74AD4601512DD3C41A7B:FG=1; BIDUPSID=04E4001F34EA74AD4601512DD3C41A7B; PSTM=1470329258; MCITY=-343%3A340%3A; H_PS_PSSID=1447_18240_21105_21386_21454_21409_21554; BD_UPN=12314753; sug=3; sugstore=0; ORIGIN=0; bdime=0; H_PS_645EC=7e2ad3QHl181NSPbFbd7PRUCE1LlufzxrcFmwYin0E6b%2BW8bbTMKHZbDP0g; BDSVRTM=0
```**一、请求行** 上例中第1行为请求行,包含了请求方法、URL地址、和协议版本,代码如下: ```python
GET https://www.baidu.com/ HTTP/1.1
```其中GET是请求方法,`https://www.baidu.com/`是URL地址,HTTP/1.1指定了协议版本。 表1 请求方法 | **序号** | **方法** | **描述** |
| -------- | -------- | ------------------------------------------------------------ |
| 1 | GET | 请求指定的页面信息,并返回实体主体。 |
| 2 | POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件),数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。 |
| 3 | HEAD | 类似于GET请求,只不过返回的响应中没有具体的内容,用于获取报头。 |
| 4 | PUT | 这种请求方式下,从客户端向服务器传送的数据取代指定的文档的内容。 |
| 5 | DELETE | 请求服务器删除指定的页面。 |
| 6 | CONNECT | HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。 |
| 7 | OPTIONS | 允许客户端查看服务器的性能。 |
| 8 | TRACE | 回显服务器收到的请求,主要用于测试或诊断。 |其中,最常用的请求方法是GET和POST,两者的区别在于: - GET是从服务器上获取指定页面信息,POST是向服务器提交数据并获取页面信息。
-
GET请求参数都显示在URL上,服务器根据该请求所包含URL中的参数来产生响应内容。 由于请求参数都暴露在外,所以安全性不高。
- POST请求参数在请求体当中,消息长度没有限制而且采取隐式发送,通常用来向HTTP服务器提交量比较大的数据(比如请求中包含许多参数或者文件上传操作等)。 POST请求的参数不在URL中,而在请求体中,所以安全性也高,使用场合也比GET多。**二、请求报头** 在请求行下是若干个请求报头,接下来为大家介绍常用的请求报头及其含义。 **1. Host** **(主机和端口号)** 指定被请求资源的Internet主机和端口号,对应网址URL中的Web名称和端口号,通常属于URL的Host部分。 **2. Connection(连接类型)** 表示客户端与服务器的连接类型,通常情况下,连接类型的对话流程如下: (1)Client 发起一个包含 Connection:keep-alive 的请求(HTTP/1.1使用 keep-alive 为默认值)。 (2)Server收到请求后: - 如果 Server 支持 keep-alive,回复一个包含 Connection:keep-alive 的响应,不关闭连接;
-
如果 Server 不支持 keep-alive,回复一个包含 Connection:close 的响应,关闭连接。
(3)如果Client收到包含 Connection:keep-alive 的响应,则向同一个连接发送下一个请求,直到一方主动关闭连接。
要注意的是,Connection:keep-alive在很多情况下能够重用连接,减少资源消耗,缩短响应时间。比如当浏览器需要多个文件时(比如一个HTML文件和多个Image文件),不需要每次都去请求建立连接。 **3. Upgrade-Insecure-Requests(升级为HTTPS请求)** 表示升级不安全的请求,意思是会在加载 HTTP 资源时自动替换成HTTPS请求,让浏览器不再显示HTTPS页面中的HTTP请求警报。 HTTPS 是以安全为目标的HTTP通道,所以在HTTPS承载的页面上不允许出现HTTP请求,一旦出现就是提示或报错。 **4. User-Agent(浏览器名称)** 标识客户端身份的名称,通常页面会根据不同的User-Agent信息自动做出适配,甚至返回不同的响应内容。 **5. Accept** **(传输文件类型)** 指浏览器或其他客户端可以接受的MIME(Multipurpose Internet Mail Extensions,多用途互联网邮件扩展)文件类型,服务器可以根据它判断并返回适当的文件格式。 Accept报头的示例如下所示: ```python
Accept: /:表示什么都可以接收。
Accept:image/gif:表明客户端希望接受GIF图像格式的资源。
Accept:text/html:表明客户端希望接受html文本。
Accept: text/html, application/xhtml+xml;q=0.9, image/*;q=0.8:表示浏览器支持的 MIME 类型分别是 html文本、xhtml和xml文档、所有的图像格式资源。
```在上述示例中,q表示权重系数,范围是0 =< q <= 1。q值越大,请求越倾向于获得其“;”之前的类型表示的内容。若没有指定q值,则默认为1,按从左到右排序顺序;若被赋值为0,则表示浏览器不接受此内容类型。text用于标准化地表示文本信息,文本信息可以是多种字符集和多种格式的。Application用于传输应用程序数据或者二进制数据。 **6. Referer (页面跳转来源)** 表明产生请求的网页来自于哪个URL,用户是从该 Referer页面访问到当前请求的页面。这个属性可以用来跟踪Web请求来自哪个页面,是从什么网站来的等。 防盗链:有时候在下载某网站图片时,需要对应的referer,否则无法下载图片,那是因为人家做了防盗链。原理就是根据referer去判断是否是本网站的地址,如果不是,则拒绝,如果是,就可以下载。 **7. Accept-Encoding(文件编解码格式)** 指出浏览器可以接受的编码方式。编码方式不同于文件格式,它的作用是压缩文件并加速文件传递速度。浏览器在接收到Web响应之后先解码,然后再检查文件格式,许多情形下这可以减少大量的下载时间。例如: ```python
Accept-Encoding:gzip;q=1.0, identity; q=0.5, *;q=0
```如果有多个Encoding同时匹配, 按照q值顺序排列,本例中按顺序支持 gzip, identity压缩编码,支持gzip的浏览器会返回经过gzip编码的HTML页面。 如果请求消息中没有设置这个报头,通常服务器假定客户端不支持压缩,直接返回文本。 **8. Accept-Language(语言种类)** 指出浏览器可以接受的语言种类,如en或en-us指英语,zh或者zh-cn指中文,当服务器能够提供一种以上的语言版本时要用到。 如果目标网站支持多个语种的话,可以使用这个信息来决定返回什么语言的网页。 **9. Accept-Charset(字符编码)** 指出浏览器可以接受的字符编码。示例如下: ```python
Accept-Charset:iso-8859-1,gb2312,utf-8
```常用的字符编码包括: - ISO-8859-1:通常叫做Latin-1。Latin-1包括了书写所有西方欧洲语言不可缺少的附加字符,英文浏览器的默认值是ISO-8859-1。
-
gb2312:标准简体中文字符集。
- utf-8:UNICODE 的一种变长字符编码,可以解决多种语言文本显示问题,从而实现应用国际化和本地化。如果在HTTP请求消息中没有设置这个域,默认客户端是任何字符集都可以接受,则返回网页charset指定的编码。 **10. Cookie(Cookie)** 浏览器用这个属性向服务器发送Cookie。Cookie是在浏览器中寄存的小型数据体,它可以记载和服务器相关的用户信息,也可以用来实现模拟登陆。 **11. Content-Type(POST数据类型)** 指定POST请求里用来表示的内容类型。示例如下: ```python
Content-Type = Text/XML; charset=gb2312:
```上述示例指明了该请求的消息体中包含的是纯文本的XML类型的数据,字符编码采用“gb2312”。 - 服务端http响应格式 HTTP响应由四个部分组成,分别是:状态行、响应报头、空行、以及响应正文。一个HTTP响应的典型示例如下所示: ```python
HTTP/1.1 200 OK
Server: Tengine
Connection: keep-alive
Date: Wed, 30 Nov 2016 07:58:21 GMT
Cache-Control: no-cache
Content-Type: text/html;charset=UTF-8
Keep-Alive: timeout=20
Vary: Accept-Encoding
Pragma: no-cache
X-NWS-LOG-UUID: bd27210a-24e5-4740-8f6c-25dbafa9c395
Content-Length: 180945<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" .... ``` 响应状态代码由三位数字组成,其中第1位数字定义了响应的类别,有五种可能取值。 常用的响应状态码如下所示。 - 100~199:表示服务器成功接收部分请求,要求客户端继续提交其余请求才能完成整个处理过程。
- 200~299:表示服务器成功接收请求并已完成整个处理过程。常用状态码为200(表示OK,请求成功)。
- 300~399:为完成请求,客户需进一步细化请求。例如:请求的资源已经移动到一个新地址。常用状态码包括302(表示所请求的页面已经临时转移至新的URL)、307和304(表示使用缓存资源)。 - 400~499:客户端的请求有错误,常用状态码包括404(表示服务器无法找到被请求的页面)和403(表示服务器拒绝访问,权限不够)。
- 500~599:服务器端出现错误,常用状态码为500(表示请求未完成,服务器遇到不可预知的情况)。
-
-
-
数据抓取
-
requests库
requests库自称“HTTP for Humans”,直译过来的意思是专门为人类设计的HTTP库,能够被开发人员安全地使用。
requests是基于Python开发的HTTP 库,它不仅使用方便,而且能节约大量的工作。实际上,requests是在urllib的基础上进行了高度的封装,它不仅继承了urllib的所有特性,而且还支持一些其它的特性,比如使用Cookie保持会话、自动确定响应内容的编码等,可以轻而易举地完成浏览器的任何操作。
requests库中提供了如下常用的类:
- requests.Request:表示请求对象,用于准备一个请求发送到服务器。
- requests.Response:表示响应对象,其中包含服务器对HTTP请求的响应。
- requests.Session:表示请求会话,提供Cookie持久性、连接池和配置。
其中,Request类的对象表示一个请求,它的生命周期针对一个客户端请求,一旦请求发送完毕,该请求包含的内容就会被释放掉。而Session类的对象可以跨越多个页面,它的生命周期同样针对的是一个客户端。当关闭这个客户端的浏览器时,只要是在预先设置的会话周期内(一般是20~30分钟),这个会话包含的内容会一直存在,不会被马上释放掉。例如,用户登陆某个网站时,可以在多个IE窗口发出多个请求。
-
发送请求
requests库中提供了很多发送HTTP请求的函数,具体如下表所示。
函数 功能说明 requests.request() 构造一个请求,支撑以下各方法的基础方法 requests.get() 获取HTML网页的主要方法,对应于HTTP的GET请求方式 requests.head() 获取HTML网页头信息的方法,对应于HTTP的HEAD请求方式 requests.post() 向HTML网页提交POST请求的方法,对应于HTTP的POST请求方式 requests.put() 向HTML网页提交PUT请求的方法,对应于HTTP的PUT请求方式 requests.patch() 向HTML网页提交局部修改请求,对应于HTTP的PATCH请求方式 requests.delete() 向HTML网页提交删除请求,对应于HTTP的DELETE请求方式 上表列举了一些常用于HTTP请求的函数,这些函数都会做两件事情,一件是构建一个Request类型的对象,该对象将被发送到某个服务器上请求或者查询一些资源;另一件是一旦得到服务器返回的响应,就会产生一个Response对象,该对象包含了服务器返回的所有信息,也包括原来创建的Request对象。
-
返回响应
Response类用于动态地响应客户端的请求,控制发送给用户的信息,并且将动态地生成响应,包括状态码、网页的内容等。接下来,通过一张表来列举Response类包含的信息,如下表所示。
下表Response类的常用属性
属性 说明 status_code HTTP请求的返回状态,200表示连接成功,404表示失败 text HTTP响应内容的字符串形式,即URL对应的页面内容 encoding 从HTTP请求头中猜测的响应内容编码方式 apparent_encoding 从内容中分析出的响应编码的方式(备选编码方式) content HTTP响应内容的二进制形式 Response类会自动解码来自服务器的内容,并且大多数的Unicode字符集都可以被无缝地解码。
-
使用
# 导入requests库 import requests # 请求的URL路径和查询参数 url = "http://www.baidu.com/" param = {"wd": "爬虫"} # 请求报头 headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/51.0.2704.103 Safari/537.36"} # 发送GET请求,返回一个响应对象 response = requests.get(url, params=param, headers=headers) # 查看响应的内容 print(response.text)
-
-
非结构化数据与结构化数据提取
对于服务器端来说,它返回给客户端的数据格式可分为非结构化和结构化两种。那么,什么是非结构化数据?什么是结构化数据呢?非结构化数据是指数据结构不规则或不完整,没有预定义的数据模型,不方便使用数据库二维逻辑来表现的数据,包括所有格式的办公文档、文本、图像等。结构化数据就是能够用数据或统一的结构加以表示,具有模式的数据,包括HTML、XML和JSON等。
想要了解一个网页的结构,可以直接在浏览器的右键菜单中点击“查看源代码”实现。例如,使用Google Chrome浏览器打开百度首页,在“新闻”选项上面右击选择“检查”,浏览器底部打开一个窗口,并显示选中元素周围的HTML层次结构。
选中的带有底色的行就是刚刚选择的“新闻”标签。可以清楚地看到,选中的标签< a>位于id属性值为’u1’的标签< div>中,并且与其它标签< a>属于并列关系,只是每个标签内部的属性值不同而已。例如,要提取点击“新闻”后跳转的网页,可以获取href属性的值。
了解了网页的数据和结构以后,我们可以借助网页解析器(用于解析网页的工具)从网页中解析和提取出有价值的数据,或者是新的URL列表。为此,Python支持一些解析网页的技术,分别为正则表达式、XPath、JSONPath。
-
正则表达式re模块
Python提供了对正则表达式的支持,在其内置的re模块中包含一些函数接口和类,开发人员可以使用这些函数和类,对正则表达式与匹配结果进行操作。
re模块的一般使用步骤如下:
(1)使用compile()函数将正则表达式以字符串形式编译为一个Pattern类型的对象。
(2)通过Pattern对象提供的一系列方法对文本进行匹配查找,得到一个表示匹配结果的Match对象。
(3)使用Match对象提供的属性和方法获得信息,如匹配到的字符串。
大多数情况下,从网站上爬取下来的网页源码中都有汉字,如果要匹配这些汉字,那么就需要知道其对应的正则表达式。通常情况下,中文对应的Unicode 编码范围为[u4e00-u9fa5],这个范围并不是很完整,比如没有包括全角(中文)标点,但是大多数情况下是可以使用的。
例如,把“你好,hello,世界”中的汉字提取出来,可以通过如下代码实现:
import re
待匹配的字符串
title = “你好,hello,世界”
创建正则表达式,用于只匹配中文
pattern = re.compile(r"[\u4e00-\u9fa5]+")
检索整个字符串,将匹配的中文放到列表中
result = pattern.findall(title)
print(result)上述示例中,首先定义了一个字符串“你好,hello,世界”,然后创建一个正则表达式对象pattern,用于匹配该字符串中的中文,接着调用findall方法将“你好”和“世界”提取为子串后保存到列表result中。 该示例的执行结果如下: ```python ['你好', '世界']
-
Xpath模块
XPath,可以将 HTML文件 转换成 XML文档,然后用 XPath 查找 HTML 节点或元素。
-
什么是XML
- XML 指可扩展标记语言(EXtensible Markup Language)
-
XML 是一种标记语言,很类似 HTML
- XML 的设计宗旨是传输数据,而非显示数据
-
XML 的标签需要我们自行定义。
- XML 被设计为具有自我描述性。
- XML 是 W3C 的推荐标准
W3School官方文档:http://www.w3school.com.cn/xml/index.asp
| 数据格式 | 描述 | 设计目标 |
| -------- | :---------------------------------------------: | ------------------------------------------------------------ |
| XML | Extensible Markup Language(可扩展标记语言)
| 被设计为传输和存储数据,其焦点是数据的内容。 |
| HTML | HyperText Markup Language(超文本标记语言)
| 显示数据以及如何更好显示数据。 |
| HTML DOM | Document Object Model for HTML(文档对象模型)
| 通过 HTML DOM,可以访问所有的 HTML 元素,连同它们所包含的文本和属性。可以对其中的内容进行修改和删除,同时也可以创建新的元素。 |<?xml version="1.0" encoding="utf-8"?>
``` <?xml version="1.0" encoding="utf-8"?><book category="cooking"> <title lang="en">Everyday Italian</title> <author>Giada De Laurentiis</author> <year>2005</year> <price>30.00</price> </book> <book category="children"> <title lang="en">Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book> <book category="web"> <title lang="en">XQuery Kick Start</title> <author>James McGovern</author> <author>Per Bothner</author> <author>Kurt Cagle</author> <author>James Linn</author> <author>Vaidyanathan Nagarajan</author> <year>2003</year> <price>49.99</price> </book> <book category="web" cover="paperback"> <title lang="en">Learning XML</title> <author>Erik T. Ray</author> <year>2003</year> <price>39.95</price> </book>
Harry Potter J K. Rowling 2005 29.99 ```2. 子(Children)
元素节点可有零个、一个或多个子。
在下面的例子中,title、author、year 以及 price 元素都是 book 元素的子:
<?xml version="1.0" encoding="utf-8"?> <book> <title>Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book>
3. 同胞(Sibling)
拥有相同的父的节点
在下面的例子中,title、author、year 以及 price 元素都是同胞:
<?xml version="1.0" encoding="utf-8"?> <book> <title>Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book>
4. 先辈(Ancestor)
某节点的父、父的父,等等。
在下面的例子中,title 元素的先辈是 book 元素和 bookstore 元素:
<?xml version="1.0" encoding="utf-8"?> <bookstore> <book> <title>Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book> </bookstore>
5. 后代(Descendant)
某个节点的子,子的子,等等。
在下面的例子中,bookstore 的后代是 book、title、author、year 以及 price 元素:
<?xml version="1.0" encoding="utf-8"?> <bookstore> <book> <title>Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book> </bookstore>
-
什么是xpath(W3School官方文档:http://www.w3school.com.cn/xpath/index.asp)
为了能够在XML文档树中准确地找到某个节点,引入了XPath的概念。XPath(XML Path Language的简写)即为XML路径语言,用于确定XML树结构中某一部分的位置。XPath技术基于XML的树结构,能够在树结构中遍历节点(元素、属性等)。那么,XPath 是如何查找信息呢?XPath使用路径表达式选取XML文档中的节点或者节点集,这些路径表达式与常规的电脑文件系统中看到的路径非常相似,代表着从一个节点到另一个或者一组节点的顺序,并以“/”字符进行分隔。
-
xpath语法
在Python中,XPath使用路径表达式在文档中进行导航。这个表达式是从某个节点开始,之后顺着文档树结构的节点进一步查找。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。由于查询路径的多样性,可以将XPath的语法按照如下情况进行划分:
1. 选取节点
节点是沿着路径选取的,既可以从根节点开始,也可以从任意位置开始。表1列举了XPath中用于选取节点的表达式。
表1 选取节点的表达式
| 表达式 | 说明 |
| ---------- | ---------------------------------------------------------- |
| nodename | 选取此节点的所有子节点 |
| / | 从根节点选取 |
| // | 从匹配选择的当前节点选取文档中的节点,而不用考虑它们的位置 |
| . | 选取当前节点 |
| … | 选取当前节点的父节点 |
| @ | 选取属性 |接下来是一些选取节点的示例,具体如下:
- 选取节点bookstore的所有子节点,表达式如下:
bookstore
2) 选取根节点bookstore,表达式如下: ```python /bookstore
需要注意的是,如果路径以“/”开始,那么该路径就代表着到达某个节点的绝对路径。
- 从根节点bookstore开始,向下选取属于它的所有book子节点,表达式如下:
bookstore/book
- 从任意位置开始,选取名称为book的所有节点,表达式如下:
//book
与上一个表达式相比,该表达式不用再说明符合要求的这些节点在文档树中的具体位置。
- 在节点bookstore的后代中,选取所有名称为book的所有节点,而且不用管这些节点位于bookstore之下的什么位置,表达式如下:
bookstore//book
- 使用“@”选取名称为lang的所有属性节点,表达式如下:
//@lang
2. 谓语(补充说明节点)
谓语是对指路径表达式的附加条件,这些条件都写在方括号中,表示对节点进行进一步筛选,用于查找某个特定节点或者包含某个指定值的节点,具体格式如下。
元素[表达式]
接下来,通过一张表来列举一些常用的带有谓语的路径表达式,以及对这些表达式功能的说明,具体如表2所示。
表2 使用谓语的表达式
表达式 说明 /bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素。 /bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素。 /bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素。 /bookstore/book[position()❤️] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。 //title[@lang] 选取所有的title元素,且这些元素拥有名称为lang的属性。 //title[@lang=’eng’] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。 /bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。 /bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。 3. 选取未知节点
XPath可以使用通配符(*)来选取未知的节点。例如,使用“ *”可以匹配任何元素节点。接下来,通过一张表来列举带有通配符的表达式,具体如表3所示。
表3 带有通配符的表达式
通配符 说明 * 匹配任何元素节点。 @* 匹配任何属性节点。 node() 匹配任何类型的节点。 下面是一些使用通配符的示例,具体如下:
- 选取bookstore元素的所有子元素,表达式如下:
/bookstore/*
- 选取文档中的所有元素,表达式如下:
//*
- 选取所有带有属性的title 元素,表达式如下:
//title[@*]
4. 选取若干路径
在路径表达式中可以使用“|”运算符,以选取若干个路径。下面是一些在路径表达式中使用“|”运算符的示例,具体如下:
- 选取book元素中包含的所有title和price子元素,表达式如下:
//book/title | //book/price
- 选取文档中的所有title和price元素,表达式如下:
//title | //price
- 选取位于/bookstore/book/路径下的所有title元素,以及文档中所有的price元素,表达式如下:
/bookstore/book/title | //price
-
xpath开发工具
对于编写网络爬虫或做网页分析的人而言,会在定位和获取XPath路径上花费大量的时间,当爬虫框架成熟以后,又会花费大量的时间来解析网页。针对这些情况,Python提供了一些好用的插件或工具,主要包含如下几种:
- XMLQuire:开源的XPath表达式编辑工具。
-
XPath Helper:适用于Chrome浏览器。
- XPath Checker:使用于Firefox浏览器。
下面以XPath Helper为例,介绍一下如何使用XPath Helper插件,查找与XPath表达式相匹配的结果,具体步骤如下:
(1)在Chrome浏览器打开当当网网站,在左侧的商品分类中选择“图书、童书”,并点击右侧的“排行榜->图书畅销榜”,跳转到图书畅销榜的网页。
(2)上下滚动上述网页,可以看到当前页共推荐了20本图书。在图1中的链接文字“人间失格”上右击“检查”,该窗口的底部显示了网页源码的弹窗,并且定位到“少年读史记”所对应的元素位置。
(3)点击地址栏右侧的“
”图标,打开XPath Helper工具。
左侧的编辑区域用于输入路径表达式,右侧用于显示匹配到的结果,并且将总数显示到上面标签的括号中,如“RESULTS(20)”,默认为0。
(4)分析上述的源码信息,如果要获取所有书籍的地址,那么需要在编辑区域输入如下表达式:
-
-
//div[@class=“pic”]/a/@href
```
(5)此时,右侧显示了所有匹配到的书籍标题,如图3所示。
```xml
http://product.dangdang.com/23778791.html
http://product.dangdang.com/29132118.html
http://product.dangdang.com/23504059.html
http://product.dangdang.com/27909879.html
http://product.dangdang.com/25252408.html
http://product.dangdang.com/28973600.html
http://product.dangdang.com/25083136.html
http://product.dangdang.com/25261327.html
http://product.dangdang.com/26446066.html
http://product.dangdang.com/29148110.html
http://product.dangdang.com/23444350.html
http://product.dangdang.com/28541697.html
http://product.dangdang.com/25575797.html
http://product.dangdang.com/25272786.html
http://product.dangdang.com/29131992.html
http://product.dangdang.com/29122655.html
http://product.dangdang.com/28533668.html
http://product.dangdang.com/23851466.html
http://product.dangdang.com/25309478.html
http://product.dangdang.com/29177871.html
```
在测试表达式时,要查询的路径既可以从根节点开始,也可以从任何位置的节点开始,这个表示路径的语句并不是唯一的。
-
Json模块
json模块提供了四个方法:dumps、dump、loads、load,用于字符串和Python数据类型间进行转换。其中loads和load方法用于Python对象的反序列化,dumps和dump方法用于Python对象的序列化,接下来对这些方法依次进行介绍。
1. json.loads()
把JSON格式字符串解码转换成Python对象。从JSON类型向Python原始类型转化的对照如表1所示。
表1 JSON向Python转化的类型对照
| JSON | Python |
| -------------- | -------- |
| object | dict |
| array | list |
| string | unicode |
| number(int) | int,long |
| number(real) | float |
| true | True |
| false | False |
| null | None |
以下示例演示了loads方法的使用:
```python
>>> import json
>>> str_list = '[1, 2, 3, 4]'
>>> str_dict = '{"city": "北京", "name": "小明"}'
>>> json.loads(str_list)
[1, 2, 3, 4]
>>> json.loads(str_dict)
{'city': '北京', 'name': '小明'}
```
**2. json.dumps()**
实现将Python类型编码为JSON字符串,返回一个str对象。从Python原始类型向JSON类型转化的对照如表2所示。
表2 Python向JSON转化的类型对照
| **Python** | **JSON** |
| ---------------- | -------- |
| dict | object |
| list,tuple | array |
| str, unicode | string |
| int,long,float | number |
| True | true |
| False | false |
| None | null |
以下示例演示了dumps方法的使用:
```python
# json_dumps.py
import json
demo_list = [1, 2, 3, 4]
demo_tuple = (1, 2, 3, 4)
demo_dict = {"city": "北京", "name": "小明"}
json.dumps(demo_list)
# [1, 2, 3, 4]
json.dumps(demo_tuple)
[1, 2, 3, 4]
# 注意:json.dumps() 处理中文时默认使用的ascii编码,会导致中文无法正常显示
print(json.dumps(demo_dict))
# {“city”: “\u5317\u4eac”, “name”: “\u5c0f\u660e”}
# 记住:处理中文时,添加参数 ensure_ascii=False 来禁用ascii编码
print(json.dumps(demo_dict, ensure_ascii=False))
# {“city”: “北京”, “name”: “小明”}
```
**3.json.load()**
读取文件中JSON形式的字符串元素,转化成Python类型。它与json.loads()方法的区别在于,一个读取的是字符串,一个读取的是文件。
以下的示例读取一个名为listStr.json和dictStr.json的文件内容,代码如下:
```python
# json_load.py
import json
str_list = json.load(open("listStr.json"))
print(str_list)
[{u’city’: u’\u5317\u4eac’}, {u’name’: u’\u5c0f\u660e’}]
str_dict = json.load(open("dictStr.json"))
print(str_dict)
# {u’city’: u’\u5317\u4eac’, u’name’: u’\u5c0f\u660e’}
**4.json.dump()**
将Python内置类型序列化为json对象后写入文件。它与json.dumps()方法的区别在于写入的是文件还是字符串。
以下示例演示了dump方法的使用:
```python
# json_dump.py
import json
str_list = [{"city": "北京"}, {"name": "小明"}]
json.dump(str_list, open("listStr.json", "w"), ensure_ascii=False)
str_dict = {"city": "北京", "name": "小明"}
json.dump(str_dict, open("dictStr.json","w"), ensure_ascii=False)
-
动态html处理
-
动态网页介绍
本章所指的动态网页是指在网页中依赖JavaScript脚本动态加载数据的网页。与传统单页面表单事件不同,使用了JavaScript脚本的网页能够在URL不变的情况下改变网页的内容。动态网页上使用的技术主要包括以下几种:
1. JavaScript
JavaScript 是网络上最常用的、支持者最多的客户端脚本语言,它可以收集 用户的跟踪数据,不需要重载页面直接提交表单,在页面嵌入多媒体文件,甚至运行网页游戏。
我们可以在网页源代码的< scripy>标签里看到,比如:
<script type="text/javascript" src="https://statics.huxiu.com/w/mini/static_2015/js/sea.js?v=201601150944"></script>
2. jQuery
jQuery 是一个十分常见的优秀的JavaScript库,也是一个快速、简洁的JavaScript框架,它封装了JavaScript常用的功能代码,提供一种简便的JavaScript设计模式,优化HTML文档操作、事件处理等。
70% 最流行的网站(约200万)和约30%的其他网站(约2亿)都在使用jQuery。一个网站使用 jQuery 的特征,就是源代码里包含了jQuery入口,比如:
<script type="text/javascript" src="https://statics.huxiu.com/w/mini/static_2015/js/jquery-1.11.1.min.js?v=201512181512"></script>
如果你在一个网站上看到了 jQuery,那么采集这个网站数据的时候要格外小心。JQuery 可以动态地创建 HTML 内容,只有在JavaScript代码执行之后才会显示,如果你用传统的方法采集页面内容,就只能获得JavaScript代码执行之前页面上的内容。
3. AJAX
我们与网站服务器通信的唯一方式,就是发出 HTTP 请求获取新页面。如果提交表单之后,或者从服务器获取信息之后,网站的页面不需要重新刷新,那么当前访问的网站就在用AJAX 技术。
AJAX其实并不是一门语言,而是用来完成网络任务(可以认为它与网络数据采集差不多)的一系列技术。AJAX全称是Asynchronous JavaScript and XML(异步 JavaScript 和 XML),网站不需要使用单独的页面请求就可以和网络服务器进行交互 (收发信息)。
4. DHTML
与AJAX一样,动态HTML(Dynamic HTML,DHTML)也是一系列用于解决网络问题的技术集合。DHTML是用客户端语言改变页面的HTML元素(HTML、CSS,或者二者皆被改变),比如页面上的按钮只有当用户移动鼠标之后才出现,背景色可能每次点击都会改变,或者用一个AJAX请求触发页面加载一段新内容,网页是否属于DHTML,关键要看有没有用JavaScript控制HTML和CSS元素。
那些使用了AJAX或DHTML技术改变或加载内容的页面,可能有一些采集手段。但是用Python解决这个问题只有两种途径:
(1)直接从JavaScript代码里采集内容(费时费力)。
(2)用Python的第三方库运行JavaScript,直接采集在浏览器里看到的页面(比较符合)。
-
-
Selenium与PhantomJS
-
selenium和phantomjs概述
要想抓取动态网页,需要结合如下两种技术:
1. Selenium
Selenium是一个Web的自动化测试工具,最初是为网站自动化测试而开发的,类型像我们玩游戏用的按键精灵,可以按指定的命令自动操作,不同是Selenium可以直接运行在浏览器上,它支持所有主流的浏览器(包括PhantomJS这些无界面的浏览器)。
Selenium可以根据我们的指令,让浏览器自动加载页面,获取需要的数据,甚至页面截屏,或者判断网站上某些动作是否发生。
Selenium自己不带浏览器,不支持浏览器的功能,它需要与第三方浏览器结合在一起才能使用。但是我们有时候需要让它内嵌在代码中运行,所以我们可以用一个叫 PhantomJS 的工具代替真实的浏览器。
Selenium 官方参考文档地址是
http://selenium-python.readthedocs.io/index.html
。2. PhantomJS
PhantomJS 是一个基于Webkit的“无界面”浏览器,它会把网站加载到内存并执行页面上的JavaScript,因为不会展示图形界面,所以运行起来比完整的浏览器要高效。
如果我们把Selenium和PhantomJS结合在一起,就可以运行一个非常强大的网络爬虫了,这个爬虫可以处理JavaScript、Cookie、headers,以及任何我们真实用户需要做的事情。
PhantomJS是一个功能完善(虽然无界面)的浏览器,而不是一个Python 库,因此它不需要像Python的其他库一样安装,但我们可以通过Selenium调用PhantomJS来直接使用。
PhantomJS官方参考文档地址是
http://phantomjs.org/documentation
。 -
selenium和phantomjs安装配置
要想使用selenium和PhantomJS,前提是需要在电脑上进行安装配置,具体如下。
1. Selenium下载和安装
Selenium的下载和安装有两种方式。
第一种方式是手动从PyPI网站下载Selenium库然后安装。下载地址是:
https://pypi.python.org/simple/selenium/
。下载最新版的selenium-2.21.2.tar.gz安装包到本地,然后解压缩(在Windows系统下,假设解压缩到E盘)。
打开终端来到解压后的setup.py文件所在目录(例如Windows系统下,E:\selenium-2.21.2),使用如下命令安装即可。
-
python setup.py install
```
第二种方式是直接使用第三方管理器pip命令自动安装。例如,在Windows终端输入以下命令即可:
```python
pip install selenium
```
**2.** **PhantomJS下载和配置**
输入网址`https://bitbucket.org/ariya/phantomjs/downloads/`,可以看到PhantomJS的官网下载页面,选择自己电脑对应的版本下载即可。点击对应Windows系统的phantomjs-2.1.1-windows.zip并进行下载。下载到本地后,解压缩即可。
然后对PhantomJS进行配置,只需要将文件夹目录放入系统环境变量中。如果不对PhantomJS进行配置,不将它的目录添加到系统路径,其实也可以在代码里使用,只需要显式地指定phantomjs.exe文件所在的目录即可。但是这种方式使用相对麻烦,我们推荐使用第一种方法,即添加到环境变量中,这样在代码里就不用理会phantomjs.exe文件在哪个位置了。
-
selenium和phantomjs使用
Selenium库里有个叫WebDriver的API。WebDriver有点儿像可以加载网站的浏览器,但是它也可以像BeautifulSoup或者其他Selector对象一样用来查找页面元素,与页面上的元素进行交互 (发送文本、点击等),以及执行其他动作来运行网络爬虫。
我们以访问百度网页为例,逐步介绍Selenium和PhantomJS的一些基本操作。
(1) 导入webdriver,代码如下。
from selenium import webdriver
```
(2)调用环境变量指定的PhantomJS浏览器创建浏览器对象,代码如下。
```python
driver = webdriver.PhantomJS()
```
如果没有在环境变量指定PhantomJS位置,则需要传入phantomjs.exe文件所在的路径,代码如下。
```python
driver = webdriver.PhantomJS(executable_path=
‘D:\phantomjs-2.1.1-windows\bin\phantomjs.exe’)
(3)获取页面内容,代码如下。
```python
>>> driver.get("http://www.baidu.com/")
使用get方法将页面的内容加载到浏览器对象driver中,get方法会一直等到页面被完全加载,然后才继续执行程序。
(4)获取页面名为wrapper的id标签的文本内容,代码如下。
>>> data = driver.find_element_by_id("wrapper").text
>>> data
'新闻\nhao123\n地图\n视频\n贴吧\n学术\n登录\n设置\n更多产品\n手机百度\n把百度设为主页关于百度About Baidu百度推广\n©2017 Baidu 使用百度前必读 意见反馈 京ICP证030173号 京公网安备11000002000001号 '
```
浏览器对象通过find_element_by_id方法定位页面元素,后面我们还会介绍其他定位元素的方法。
(5)打印页面标题,代码如下。
```python
>>> print(driver.title)
百度一下,你就知道
```
通过浏览器对象的title属性可以获取当前页面的标题信息。
(6)生成当前页面快照并保存,代码如下。
```python
>>> driver.save_screenshot("baidu.png")
True
```
PhantomJS浏览器虽然不显示页面,但是可以生成页面快照,并通过save_screenshot方法将页面快照保存成图片。此时,在python.exe文件的同目录下生成了一个名为baidu.png的图片文件,打开baidu.png文件,可以看到它保存了百度搜索页面在浏览器上的显示效果。
(7)打印页面源码,代码如下。
```python
>>> print(driver.page_source)
```
此时,会打印出页面的整个源码。
(8)往页面的输入框中添加内容。
下面代码中,通过id="kw"定位百度搜索输入框,往输入框里添加字符串"长城",然后将页面快照保存。
```python
>>> driver.find_element_by_id("kw").send_keys(u"长城")
>>> driver.save_screenshot("baidu.png")
True
```
send_keys方法的作用就是往页面元素上添加内容。此时,再次打开baidu.png文件,可以看到字符串“长城”已经添加到百度页面的搜索框中。
(9)模拟点击页面上的按钮。
下面的示例代码中,通过id="su"定位百度搜索按钮,然后通过click()方法模拟点击页面上的按钮。
```python
>>> driver.find_element_by_id("su").click()
>>> driver.save_screenshot("changcheng.png")
True
```
此时,在Python.exe文件同目录下生成了名为changcheng.png的图片。
(10)调用键盘按键操作,首先引入Keys包。示例代码如下。
```python
>>> from selenium.webdriver.common.keys import Keys
```
(11)通过模拟Control + a 键全选输入框内容,代码如下。
```python
>>> driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'a')
```
(12)通过模拟Control + x 键剪切输入框内容,代码如下。
```python
>>> driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'x')
```
往输入框重新输入搜索关键字“itcast”,代码如下。
```python
>>> driver.find_element_by_id("kw").send_keys("itcast")
```
(13)模拟点击“Enter”回车键,代码如下。
```python
>>> driver.find_element_by_id("kw").send_keys(Keys.RETURN)
```
等待2秒,等页面响应完毕,然后生成新的页面快照,代码如下。
```python
>>> time.sleep(2)
>>> driver.save_screenshot("itcast.png")
True
```
此时,在Python.exe文件同目录下生成了名为itcast.png的图片。
(14)清除输入框内容。
清除输入框内容,使用clear方法,示例代码如下。
```python
>>> driver.find_element_by_id("kw").clear()
```
(15)获取当前页面Cookie。
使用get_cookies方法获取当前页面的Cookie,示例代码如下。
```python
>>> print(driver.get_cookies())
```
(16)获取当前URL。
使用current_url属性获取当前页面的URL,示例代码如下。
```python
>>> print(driver.current_url)
```
(17)关闭当前页面。
使用close方法关闭当前页面,如果只有一个页面,会关闭浏览器。示例代码如下。
```python
>>> driver.close()
```
(18)关闭浏览器。
当浏览器使用完毕时,应使用quit方法关闭浏览器,示例代码如下。
```python
>>> driver.quit()
```
- ##### Scrapy框架
Scrapy是用纯Python实现的一个开源爬虫框架,是为了高效地爬取网站数据、提取结构性数据而编写的应用框架,用途非常广泛,可用于爬虫开发、数据挖掘、数据监测、自动化测试等领域。Scrapy使用了Twisted(其主要对手是Tornado)异步网络框架来处理网络通讯,该网络框架可以加快我们的下载速度,并且包含了各种中间件接口,可以灵活的完成各种需求。
Scrapy功能很强大,它支持自定义Item和pipeline数据管道;支持在Spider中指定domain(网页域范围),以及相应的Rule(爬取规则);支持XPath对DOM的解析等。而且Scrapy还有自己的shell,可以在上面方便地调试爬虫项目和查看爬虫运行结果。
- ###### scrapy框架介绍
Scrapy框架主要包含以下组件:
(1)Scrapy Engine(引擎):负责Spider、Item Pipeline、Downloader、Scheduler之间的通讯,包括信号和数据的传递等。
(2)Scheduler(调度器):负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列和入队,当引擎需要时,交还给引擎。
(3)Downloader(下载器):负责下载Scrapy Engine(引擎)发送的所有Requests(请求),并将其获取到的Responses(响应)交还给Scrapy Engine(引擎),由引擎交给Spider来处理。
(4)Spiders(爬虫):负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler(调度器)。
(5)Item Pipeline(管道):负责处理Spiders中获取到的Item数据,并进行后期处理(详细分析、过滤、存储等)。
(6)Downloader Middlewares(下载中间件):是一个可以自定义扩展下载功能的组件。
(7)Spider Middlewares(Spider中间件):是一个可以自定义扩展Scrapy Engine和Spiders中间通信的功能组件(比如进入Spiders的Responses和从Spiders出去的Requests)。
Scrapy的这些组件通力合作,共同完成整个爬取任务。架构图中的箭头是数据的流动方向,首先从初始URL开始,Scheduler会将其交给Downloader进行下载,下载之后会交给Spider进行分析,Spider分析出来的结果有两种:一种是需要进一步抓取的链接,例如之前分析的“下一页”的链接,这些东西会被传回Scheduler;另一种是需要保存的数据,它们则被送到Item Pipeline那里,那是对数据进行后期处理(详细分析、过滤、存储等)的地方。另外,在数据流动的通道里还可以安装各种中间件,进行必要的处理。
- ###### scrapy框架运作流程
Scrapy的运作流程由引擎控制,其过程如下所示:
(1)引擎向Spider请求第一个要爬取的URL(s)。
(2)引擎从Spider中获取到第一个要爬取的URL,封装成Request并交给调度器。
(3)引擎向调度器请求下一个要爬取的Request。
(4)调度器返回下一个要爬取的Request给引擎,引擎将Request通过下载中间件转发给下载器。
(5)一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过下载中间件发送给引擎。
(6)引擎从下载器中接收到Response并通过Spider中间件发送给Spider处理。
(7)Spider处理Response并返回爬取到的Item及新的Request给引擎。
(8)引擎将爬取到的Item给Item Pipeline,将Request给调度器。
(9)从(2)开始重复,直到调度器中没有更多的Request。
- ###### 配置安装
```python
pip install scrapy
```
- ###### 使用
- 创建一个Scrapy项目
- 定义提取的结构化数据(Item)
- 编写爬取网站的 Spider 并提取出结构化数据(Item)
- 编写 Item Pipelines 来存储提取到的Item(即结构化数据)
##### 一. 新建项目(scrapy startproject)
- 在开始爬取之前,必须创建一个新的Scrapy项目。进入自定义的项目目录中,运行下列命令:
scrapy startproject mySpider
- 其中, mySpider 为项目名称,可以看到将会创建一个 mySpider 文件夹,目录结构大致如下:

下面来简单介绍一下各个主要文件的作用:
> scrapy.cfg :项目的配置文件
>
> mySpider/ :项目的Python模块,将会从这里引用代码
>
> mySpider/items.py :项目的目标文件
>
> mySpider/pipelines.py :项目的管道文件
>
> mySpider/settings.py :项目的设置文件
>
> mySpider/spiders/ :存储爬虫代码目录
##### 二、明确目标(mySpider/items.py)
我们打算抓取:http://www.itcast.cn/channel/teacher.shtml 网站里的所有讲师的姓名、职称和个人信息。
1. 打开mySpider目录下的items.py
2. Item 定义结构化数据字段,用来保存爬取到的数据,有点像Python中的dict,但是提供了一些额外的保护减少错误。
3. 可以通过创建一个 scrapy.Item 类, 并且定义类型为 scrapy.Field的类属性来定义一个Item。
4. 接下来,创建一个ItcastItem 类,和构建item模型(model)。
```python
import scrapy
class ItcastItem(scrapy.Item):
name = scrapy.Field()
level = scrapy.Field()
info = scrapy.Field()
三、制作爬虫 (spiders/itcastSpider.py)
爬虫功能要分两步:
1. 爬数据
- 在当前目录下输入命令,将在
mySpider/spider
目录下创建一个名为itcast
的爬虫,并指定爬取域的范围:
scrapy genspider itcast "itcast.cn"
- 打开 mySpider/spider目录里的 itcast.py,默认增加了下列代码:
import scrapy
class ItcastSpider(scrapy.Spider):
name = "itcast"
allowed_domains = ["itcast.cn"]
start_urls = (
'http://www.itcast.cn/',
)
def parse(self, response):
pass
其实也可以由我们自行创建itcast.py并编写上面的代码,只不过使用命令可以免去编写固定代码的麻烦
要建立一个Spider, 你必须用scrapy.Spider类创建一个子类,并确定了三个强制的属性 和 一个方法。
name = ""
:这个爬虫的识别名称,必须是唯一的,在不同的爬虫必须定义不同的名字。allow_domains = []
是搜索的域名范围,也就是爬虫的约束区域,规定爬虫只爬取这个域名下的网页,不存在的URL会被忽略。start_urls = ()
:爬取的URL元祖/列表。爬虫从这里开始抓取数据,所以,第一次下载的数据将会从这些urls开始。其他子URL将会从这些起始URL中继承性生成。parse(self, response)
:解析的方法,每个初始URL完成下载后将被调用,调用的时候传入从每一个URL传回的Response对象来作为唯一参数,主要作用如下:- 负责解析返回的网页数据(response.body),提取结构化数据(生成item)
- 生成需要下一页的URL请求。
将start_urls的值修改为需要爬取的第一个url
start_urls = ("http://www.itcast.cn/channel/teacher.shtml",)
修改parse()方法
def parse(self, response):
with open("teacher.html", "w") as f:
f.write(response.text)
然后运行一下看看,在mySpider目录下执行:
scrapy crawl itcast
是的,就是 itcast,看上面代码,它是 ItcastSpider 类的 name 属性,也就是使用 scrapy genspider
命令的爬虫名。
一个Scrapy爬虫项目里,可以存在多个爬虫。各个爬虫在执行时,就是按照 name 属性来区分。
运行之后,如果打印的日志出现 [scrapy] INFO: Spider closed (finished)
,代表执行完成。 之后当前文件夹中就出现了一个 teacher.html 文件,里面就是我们刚刚要爬取的网页的全部源代码信息。
2. 取数据
- 爬取整个网页完毕,接下来的就是的取过程了,首先观察页面源码:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fowFI4hd-1632390744252)(…/images/teacher_html.png)]
<div class="li_txt">
<h3> xxx </h3>
<h4> xxxxx </h4>
<p> xxxxxxxx </p>
是不是一目了然?直接上XPath开始提取数据吧。
- 我们之前在mySpider/items.py 里定义了一个ItcastItem类。 这里引入进来
from mySpider.items import ItcastItem
- 然后将我们得到的数据封装到一个
ItcastItem
对象中,可以保存每个老师的属性:
from mySpider.items import ItcastItem
def parse(self, response):
#open("teacher.html","wb").write(response.body).close()
# 存放老师信息的集合
items = []
for each in response.xpath("//div[@class='li_txt']"):
# 将我们得到的数据封装到一个 `ItcastItem` 对象
item = ItcastItem()
#extract()方法返回的都是字符串
name = each.xpath("h3/text()").extract()
title = each.xpath("h4/text()").extract()
info = each.xpath("p/text()").extract()
#xpath返回的是包含一个元素的列表
item['name'] = name[0]
item['title'] = title[0]
item['info'] = info[0]
items.append(item)
# 直接返回最后数据
return items
- 我们暂时先不处理管道,后面会详细介绍。
保存数据
scrapy保存信息的最简单的方法主要有四种,-o 输出指定格式的文件,,命令如下:
# json格式,默认为Unicode编码
scrapy crawl itcast -o teachers.json
# json lines格式,默认为Unicode编码
scrapy crawl itcast -o teachers.jsonl
# csv 逗号表达式,可用Excel打开
scrapy crawl itcast -o teachers.csv
# xml格式
scrapy crawl itcast -o teachers.xml
<div class="li_txt">
<h3> xxx </h3>
<h4> xxxxx </h4>
<p> xxxxxxxx </p>
是不是一目了然?直接上XPath开始提取数据吧。
- 我们之前在mySpider/items.py 里定义了一个ItcastItem类。 这里引入进来
from mySpider.items import ItcastItem
- 然后将我们得到的数据封装到一个
ItcastItem
对象中,可以保存每个老师的属性:
from mySpider.items import ItcastItem
def parse(self, response):
#open("teacher.html","wb").write(response.body).close()
# 存放老师信息的集合
items = []
for each in response.xpath("//div[@class='li_txt']"):
# 将我们得到的数据封装到一个 `ItcastItem` 对象
item = ItcastItem()
#extract()方法返回的都是字符串
name = each.xpath("h3/text()").extract()
title = each.xpath("h4/text()").extract()
info = each.xpath("p/text()").extract()
#xpath返回的是包含一个元素的列表
item['name'] = name[0]
item['title'] = title[0]
item['info'] = info[0]
items.append(item)
# 直接返回最后数据
return items
- 我们暂时先不处理管道,后面会详细介绍。
保存数据
scrapy保存信息的最简单的方法主要有四种,-o 输出指定格式的文件,,命令如下:
# json格式,默认为Unicode编码
scrapy crawl itcast -o teachers.json
# json lines格式,默认为Unicode编码
scrapy crawl itcast -o teachers.jsonl
# csv 逗号表达式,可用Excel打开
scrapy crawl itcast -o teachers.csv
# xml格式
scrapy crawl itcast -o teachers.xml