Python高级——Ch23~Ch2本文仅供学习使用
- 23. 爬虫Spider
- 23.1 HTTP基本原理
- 23.1.1 URI与URL
- 23.1.2 超文本
- 23.1.3 HTTP 和 HTTPS
- 23.1.4 HTTP请求过程
- 23.1.5 请求
- 23.1.6 响应
- 23.1.7 网页的组成
- 23.1.8 节点数及节点之间的关系
- 23.1.9 选择器
- 23.1.10 流程回顾
- 23.1.11 能抓怎样的数据
- 23.1.12 JavaScript渲染页面
- 23.2 会话和Cookies
- 23.2.1 静态网页和动态网页
- 23.2.2 无状态HTTP
- 23.2.3 会话维持
- 23.2.4 代理的基本原理
- 23.3 requests
- 23.3.1 requests的用法
- 23.3.2 带参数的get请求
- 23.3.3 JSON解析
- 23.3.4 简单保存一个二进制文件
- 23.3.5 response.text和response.content的区别
- 23.3.6 为请求添加头信息
- 23.3.7 基本代理
- 23.3.8 带参数的get请求
- 23.2.9 cookie和session
- 23.3.9 证书验证设置
- 23.3.10 异常处理
- 23.4 数据提取
- 23.4.1 数据分类
- 23.4.2 JSON数据提取
- 23.4.3 正则表达式提取
- 23.4.4 动态网站数据抓取
- 23.5 Beautiful Soup
- 23.5.1 BS4引入
23. 爬虫Spider
为什么要学习爬虫&爬虫的定义
· 网络爬虫又称网络蜘蛛、网络机器人等,可以自动化浏览网络中的信息,当然浏览信息的时候需要按照所制定的相应规则进行,即网络爬虫算法。
· 原因很简单,我们可以利用爬虫技术,自动地从互联网中获取我们感兴趣的内容,并将这些数据内容爬取回来,作为我们的数据源,从而进行更深层次的数据分析,并获得更多有价值的信息。
· 在大数据时代,这一技能是必不可少的。
· 只要是浏览器能做的事情,原则上,爬虫都能够做。
爬虫的思路
每一个网页都是一份 HTML文档,全称叫hypertext markup language,是一种文本标记语言
这是一份很有规则的文档写法,打开一个网页,即是通过了 HTTP协议,对一个资源进行了请求,返还你一份 HTML文档,然后浏览器进行文档的渲染,这样,就形成了一个网页。所以,我们只需要模拟浏览器,发送一份请求,获得这份文档,再抽取出我们需要的内容就好。
通用爬虫和聚焦爬虫工作流程
通用搜索引擎的局限性
· 通用搜索引擎所返回的网页里90%的内容无用。
· 图片、音频、视频多媒体的内容通用搜索引擎无能为力。
· 不同用户搜索的目的不全相同,但是返回内容相同
ROBOTS协议
Robots协议:网站通过Robots协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取。
例如:https://www.taobao.com/robots.txt
浏览器发送HTTP请求的过程 ——浏览器渲染出来的页面和爬虫请求的页面并不一样
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的子集
· URI 还包括一个子类叫作URN ,它的全称为Universal Resource Name,即统一资源名称。URN举例, um:isbn:0451450523
指定了一本书的ISBN,可以唯一标识这本书,但是没有指定到哪里定位这本书。
URN用得非常少,所以几乎所有的URI 都是URL,一般的网页链接我们可称为URI也可称为URL,习惯上称为URL。
URI,URL,URN三者的关系:
· 超文本,其英文名称叫作 hypertext ,我们在浏览器里看到的网页就是超文本解析而成的, 其网页源代码是一系列HTML 代码, 里面包含了一系列标签。
比如 显示图片, 为超链接等。
· 浏览器解析这些标签后,便形成了我们平常看到的网页,而网页的源代码HTML 就可以称作超文本。
· 在Chrome浏览器中打开任意一个页面,右击任意地方并选择“检查”(或者快捷键F12)即可打开开发者工具,Elements选项卡即可看到当前页的源代码,这些源代码都是超文本。
· URL 的开头会有http 或https ,这就是访问资源需要的协议类型。
· 其他协议类型:ftp, sftp, smb。在爬虫中,我们抓取的页面通常就是http 或https协议的。
· HTTP 的全称是 Hyper Text Transfer Protocol ,中文名叫作超文本传输协议。HTTP 协议是用于从网络传输超文本数据到本地浏览器的传送协议,它能保证高效而准确地传送超文本文档。
· HTTPS 的全称是 Hyper Text Transfer Protocol over Secure Socket Layer ,是以安全为目标的HTTP通道,简单讲是HTTP 的安全版, 即 HTTP 下加入 SSL 层,简称为 HTTPS 。
23.1.4 HTTP请求过程如果没有SSL加密,会被浏览器提示不安全
比如 http://www.pudong.gov.cn/shpd/department/019022/
有些网站虽然使用了HTTPS协议,还是会被浏览器提示不安全,比如早期的12306网站,它的CA证书是中国铁道部自行签发的,而这个证书不被CA机构信任。
爬取这样的站点,就需要设置忽略证书的选项,否则会提示SSL 链接错误。
我们在浏览器中输入一个URL ,回车之后便会在浏览器中观察到页面内容。实际上,这个过程是浏览器向网站所在的服务器发送了一个请求,网站服务器接收到这个请求后进行处理和解析,然后返回对应的响应,接着传回给浏览器。
响应里包含了页面的源代码等内容,浏览器再对其进行解析,便将网页呈现了出来。
比如:https://www.baidu.com/,用Chrome 浏览器的开发者模式下的Network 监听组件来做下演示,它可以显示访问当前请求网页时发生的所有网络请求和响应
打开Chrome 浏览器,输入链接 https://www.baidu.com/
右击并选择“检查”项,即可打开浏览器的开发者工具(F12)
第一列 Name :请求的名称,一般会将URL 的最后一部分内容当作名称。
第二 Status :响应的状态码,这里显示为200 , 代表响应是正常的。通过状态码,我们可以判断发送了请求之后是否得到了正常的响应。
第三列 Type : 请求的文梢类型。这里为document ,代表我们这次请求的是一个HTML 文档,内容就是一些HTML 代码。
第四列 Initiator : 请求源。用来标记请求是由哪个对象或进程发起的。
第五列 Size : 从服务器下载的文件和请求的资源大小。如果是从缓存中取得的资源,则该列会显示from cache 。
第六列 Time : 发起请求到获取响应所用的总时间。
第七列 Waterfall:网络请求的可视化瀑布流
我们先观察第一个网络请求,即www. baidu.com 点击这个条目,即可看到更详细的信息
23.1.5 请求General 部分
Request URL 为请求的URL,
Request Method 为请求的方法,
Status Code 为响应状态码,
Remote Address 为远程服务器的地址和端口,
Referrer Policy 为Referrer 判别策略。Response Headers 和 Request Headers ,这分别代表响应头和请求头。
请求,由客户端向服务端发出,可以分为4 部分内容:请求方法Request Method 、请求的网址Request URL、请求头 Request Headers 、请求体Request Body。
- 请求方法( Request Method )
常见的请求方法有两种: GET 和POST
· 在浏览器中直接输入URL 并回车,这便发起了一个GET 请求,请求的参数会直接包含到URL里。例如,在百度中搜索Python ,这就是一个GET 请求,链接为 https://www.baidu.corn/s?wd= Python ,其中 URL 中问号后面为请求的参数信息,这里参数wd表示要搜寻的关键字。
· POST 请求大多在表单提交时发起。比如,对于一个登录表单,输入用户名和密码后,点击“ 登录”按钮,这通常会发起一个POST请求,其数据通常以表单的形式传输,而不会体现在URL 中。
GET 和POST 请求方法有如下区别:
· GET 请求中的参数包含在URL 里面,数据可以在URL 中看到,而POST 请求的URL 不会包含这些数据,数据都是通过表单形式传输的,会包含在请求体中。
· 一般来说,登录时,需要提交用户名和密码,其中包含了敏感信息,使用GET 方式请求的话,密码就会暴露在URL 里面,造成密码泄露,所以这里最好以POST 方式发送。上传文件时,由于文件内容比较大,也会选用POST 方式。
我们平常遇到的绝大部分请求都是GET 或POST 请求,另外还有一些请求方法:
| 方法 | 描述 |
|---|---|
| HEAD | 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头 |
| PUT | 从客户端向服务器传送的数据取代指定的文档的内容。 |
| DELETe | 请求服务器删除指定的页面。 |
| CONNECT | HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。 |
| OPTIONS | 允许客户端查看服务器的性能。 |
| TRACE | 回显服务器收到的请求,主要用于测试或诊断。 |
| PATCH | 是对 PUT 方法的补充,用来对已知资源进行局部更新 。 |
-
请求的网址( Request URL )
请求的网址,即统一资惊定位符URL ,它可以唯一确定我们想请求的资源。 -
请求头( Request Headers )
请求头,用来说明服务器要使用的附加信息,比较重要的信息有Cookie 、Referer 、User-Agent 等。
一些常用的头信息:
Accept :请求报头域,用于指定客户端可接受哪些类型的信息。
Accept-Language :指定客户端可接受的语言类型。
Accept-Encoding :指定客户端可接受的内容编码。
Host :用于指定请求资源的主机IP 和端口号,其内容为请求URL 的原始服务器或网关的位置。从HTTP 1. l 版本开始,请求必须包含此内容。
Referer :此内容用来标识这个请求是从哪个页面发过来的,服务器可以拿到这一信息并做相应的处理,如做来源统计、防盗链处理等。
User-Agent :简称UA ,它是一个特殊的字符串头,可以使服务器识别客户使用的操作系统及版本、浏览器及版本等信息。在做爬虫时加上此信息,可以伪装为浏览器;如果不加,很可能会被识别为爬虫。
Content-Type:也叫互联网媒体类型( Internet Media Type )或者MIME 类型,在HTTP 协议消息头中,它用来表示具体请求中的媒体类型信息。例如,text/html 代表HTML 格式,image/gif代表GIF 图片, app lication/json 代表JSON 类型,更多对应关系可以查看此对照表:http://tool.oschina.neνcommons 。
Cookie :也常用复数形式Cookies ,这是网站为了辨别用户进行会话跟踪而存储在用户本地的数据。它的主要功能是维持当前访问会话。
请求头是请求的重要组成部分,在写爬虫时,大部分情况下都需要设定请求头。
我们输入用户名和密码成功登录某个网站后,服务器会用会话保存登录状态信息,后面我们每次刷新或请求该站点的其他页面时,会发现都是登录状态,这就是Cookies 的功劳。Cookies 里有信息标识了我们所对应的服务器的会话,每次浏览器在请求该站点的页面时,都会在请求头中加上Cookies 并将其发送给服务器,服务器通过Cookies 识别出是我们自己,并且查出当前状态是登录状态,所以返回结果就是登录之后才能看到的网页内容。 -
请求体( Request Body )
请求体-般承载的内容是 POST 请求中的表单数据,而对于 GET 请求,请求体则为空。
例如登录猫眼电影https://maoyan.com时捕获到的请求和响应如图所示:
登录之前,填写了用户名和密码信息,提交时这些内容就会以表单数据的形式提交给服务器, 此时需要注意Request Headers 中指定Content-Type 为application, x-www-form-urlencoded
在爬虫中,如果要构造POST 请求,需要使用正确的 Content-Type ,并了解各种请求库的各个参数设置时使用的是哪种 Content-Type , 不然可能会导致 POST 提交后无法正常响应。
| Content-Type | 提交数据方式 |
|---|---|
| application/x-www-form-urlencoded | 表单数据 |
| multipart/dorm-data | 表单文件上传 |
| application/json | 序列化JSON数据 |
| text/xml | XML数据 |
响应,由服务端返回给客户端,可以分为三部分:响应状态码Response Status Code、响应头Response Headers和响应体Response Body
- 响应状态码( Response Status Code )
在爬虫中,我们可以根据状态码来判断服务器响应状态,如状态码为200 ,则证明成功返回数据, 再进行进一步的处理,否则直接忽略。
| 状态码 | 英文名称 | 中文描述 |
|---|---|---|
| 200 | OK | 请求成功。一般用于GET与POST请求 |
| 404 | Not Found | 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面 |
| 500 | Internal Server Error | 服务器内部错误,无法完成请求 |
更多状态码可查看:https://www.runoob.com/http/http-status-codes.html
-
响应头( Response Headers )
响应头包含了服务器对请求的应答信息,如 Content-Type 、Server 、Set- Cookie 等。
一些常用的头信息:
Date : 标识响应产生的时间。
Last-Modified : 指定资源的最后修改时间。
Content-Encoding : 指定响应内容的编码。
Server : 包含服务器的信息,比如名称、版本号等。
Content-Type : 文档类型,指定返回的数据类型是什么,如tex t/htm l 代表返回HTML 文档,application/x-javascript 代表返回JavaScript 文件, image/jpeg 则代表返回图片。
Set-Cookie : 设置Cookies 。响应头中的Set- Cookie 告诉浏览器需要将此内容放在Cookies中, 下次请求携带Cookies 请求。
Expires: 指定响应的过期时间, 可以使代理服务器或浏览器将加载的内容更新到缓存里。如果再次访问时,就可以直接从缓存中加载, 降低服务器负载,缩短加载时间。 -
响应体( Response Body )
响应的正文数据都在响应体中,比如请求网页时,它的响应体就是网页的 HTML 代码; 请求一张图片时, 它的响应体就是图片的二进制数据。我们做爬虫请求网页后, 要解析的内容就是响应体。
在做爬虫时,我们主要通过响应体得到网页的源代码、JSON 数据等, 然后从中做相应内容的提取
网页可以分为三大部分一一HTML , CSS 和JavaScript 。如果把网页比作一个人的话, HTML 相当于骨架, JavaScript 相当于肌肉, css 相当于皮肤,三者结合起来才能形成一个完善的网页。
- HTML
HTML 是用来描述网页的一种语言, 其全称叫作 Hyper Text Markup Language ,即超文本标记语言。网页包括文字、按钮、图片和视频等各种复杂的元素,其基础架构就是HTML 。不同类型的元素通过不同类型的标签来表示,如图片用 标签表示, 视频用 - CSS
CSS ,全称叫作 Cascading Style Sheets ,即层叠样式表。“层叠”是指当在HTML 中引用了数个样式文件,并且样式发生冲突时,浏览器能依据层叠顺序处理。“样式”指网页中文字大小、颜色、元素间距、排列等格式。
CSS 是目前唯一的网页页面排版样式标准。
大括号前面是一个CSS 选择器,大括号里面是对选择到的节点的样式设置。
- JavaScript
JavaScript ,简称JS ,是一种脚本语言。它的出现使得用户与信息之间不只是一种浏览与显示的关系,而是实现了一种实时、动态、交互的页面功能。HTML 和CSS 配合使用, 提供给用户的只是一种静态信息,缺乏交互性。我们在网页里可能会看到一些交互和动画效果,如下载进度条、提示框、轮播图等,这通常就是JavaScript 的功劳。
在 HTML 中,所有标签定义的内容都是节点,它们构成了一个HTML DOM 树。
我们知道网页由一个个节点组成, CSS 选择器会根据不同的节点设置不同的样式规则,那么怎样来定位节点呢?
| 选择器 | 示例 | 示例说明 |
|---|---|---|
| .class | .intro | 选择所有的元素 |
| #id | #firstname | 选择所有id="firstname"的元素 |
| * | * | 选择所有元素 |
| element | p | 选择所有 元素 |
| element,element | div,p | 选择所有元素和 元素 |
| element>element | div>p | 选择所有父级是元素的 元素 |
| [attribute=value] | [target=-blank] | 选择所有使用target="-blank"的元素 |
基于HTTP 或HTTPS 协议的,只要是这种数据,爬虫都通过它的URL进行抓取。
· 常规网页,它们对应着HTML 代码,抓取的便是HTML 源代码。
· JSON 字符串(其中API 接口大多采用这样的形式),这种格式的数据方便传输和解析,它们同样可以抓取,而且数据提取更加方便。
· 二进制数据,如图片、视频和音频等。利用爬虫,我们可以将这些二进制数据抓取下来,然后保存成对应的文件名。
· 扩展名的文件,如css 、JavaScript 和配置文件等,这些其实也是最普通的文件,只要在浏览器里面可以访问到,就可以将其抓取下来。
有时候,我们在用urllib 或requests“抓取网页时,得到的源代码实际和浏览器中看到的不一样。这是一个很常见的问题。现在网页越来越多地采用Ajax 、前端模块化工具来构建,整个网页可能都是由JavaScript 渲染出来的,也就是说原始的HTML 代码就是一个空壳。
< ! OOCTYPE html>This is a Demo Hello World h2 >Hello, this is a paragraph. p>
- 动态网页
This is a Demo Hello World! <%= request.getParameter("name")%> h2 >Hello, this is a paragraph. p>
23.2.2 无状态HTTP动态解析URL 中参数的变化,关联数据库井动态呈现不同的页面内容。
可能由JSP 、PHP 、Python 等语言编写的
HTTP 的无状态是指HTTP 协议对事务处理是没有记忆能力的,也就是说服务器不知道客户端是什么状态。
如果后续需要处理前面的信息,则必须重传,这导致需要额外传递一些前面的重复请求,才能获取后续响应。这给需要用户登录的页面来说,非常棘手。
- Cookies
Cookies 在客户端,也可以理解为浏览器端,有了Cookies ,浏览器在下次访问网页时会自动附带上它发送给服务器,服务器通过识别Cookies 并鉴定出是哪个用户,然后再判断用户是否是登录状态,然后返回对应的响应。
我们可以理解为Cookies 里面保存了登录的凭证,有了它,只需要在下次请求携带Cookies 发送请求而不必重新输入用户名、密码等信息重新登录了。
因此在爬虫中,有时候处理需要登录才能访问的页面时,我们一般会直接将登录成功后获取的Cookies 放在请求头里面直接请求,而不必重新模拟登录。 - 会话
会话在服务端,也就是网站的服务器,用来保存用户的会话信息。会话,其本来的含义是指有始有终的一系列动作/消息。比如,打电话时,从拿起电话拨号到挂断电话这中间的一系列过程可以称为一个会话。而在Web 中,会话对象用来存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的Web 页之间跳转时,存储在会话对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的Web 页时如果该用户还没有会话, 则Web 服务器将自动创建一个会话对象。当会话过期或被放弃后,服务器将终止该会话。
反爬虫措施: 检测某个IP 在单位时间内的请求次数,如果超过了这个阔值,就会直接拒绝服务
对于爬虫来说,由于爬虫爬取速度过快,在爬取过程中可能遇到同一个IP 访问过于频繁的问题,此时网站就会让我们输入验证码登录或者直接封锁IP ,这样会给爬取带来极大的不便。
使用代理隐藏真实的IP ,让服务器误以为是代理服务器在请求向己。这样在爬取过程中通过不断更换代理,就不会被封锁,可以达到很好的爬取效果。
代理:实现IP 伪装,反反爬虫
为什么要学习requests,而不是urllib?
- requests的底层实现就是urllib。
- requests在python2和python3中通用,方法完全一样。
- requests简单易用。
- requests能够自动帮我们解压(gzip压缩等)网页内容。
作用:发送网络请求,返回响应数据
中文文档API:https://requests-docs-cn.readthedocs.io/zh_CN/latest/_modules/requests/api.html
requests是python实现的简单易用的HTTP库,使用起来比urllib简洁很多因为是第三方库,所以使用前需要cmd安装pip install requests
基本用法: requests.get()用于请求目标网站,类型是一个HTTPresponse类型
get请求示例:
import requests
response = requests.get('http://www.baidu.com')
print(response.status_code) # 打印状态码
print(response.url) # 打印请求url
print(response.headers) # 打印头信息
print(response.cookies) # 打印cookie信息
print(response.text) # 以文本形式打印网页源码
print(response.content) # 以字节流形式打印
'''
调用get()方法得到一个Response对象,然后分别输出了Response的类型、状态码、响应体的类型、内容以及Cookies 。
'''
r = requests.get('https://www.baidu.com/') #请求方法为GET方法
print(type(r)) #Response类型
print(r.status_code) #状态码
print (type(r.text)) #响应体的类型
print(r.text) #响应体的内容
print(r.cookies) #Cookies
print(r.request.headers) #请求头部信息
其他请求示例:
import requests
r = requests.get('http://httpbin.org/get') #GET方法
r = requests.post('http://httpbin.org/post') #POST方法
r = requests.put('http://httpbin.org/put') #PUT方法
r = requests.delete('http://httpbin.org/delete') #DELETE方法
r = requests.head('http://httpbin.org/get') #HEAD方法
r = requests.options('http://httpbin.org/get') #OPTIONS方法
23.3.2 带参数的get请求
- 直接将参数放在url内:
import requests
#方式一
r = requests.get('https://www.baidu.com/s?wd=python')
print(r.text)
print(r.request.headers)
response = requests.get('http://httpbin.org/get?name=gemey&age=22')
print(response.text)
- 先将参数填写在dict中,发起请求时params参数指定为dict
import requests
#方式二
data = { 'wd': 'python'}
r = requests.get('https://www.baidu.com/s', params=data)
print(r.text)
print(r.request.headers)
data = { 'name': 'tom', 'age': 20}
response = requests.get('http://httpbin.org/get', params=data)
print(response.text)
中文乱码问题:response.encoding = 'utf8'
import requests
data = { 'wd': '人工智能'}
response = requests.get('https://www.runoob.com/jsp/jsp-form-processing.html')
print(response.text) #可能会遇到中文乱码问题
response.encoding = 'utf8' #设置响应编码为utf8
print(response.text)
23.3.3 JSON解析
import requests
response = requests.get('http://httpbin.org/get')
print(response.text)
print(response.json()) # response.json()方法同json.loads(response.text)
print(type(response.json()))
#encoding:utf8
import requests
#解析json数据
def parse_json(url, data):
response = requests.get(url, params=data)
print(response.text)
print(response.json()) #如果不是json数据,会报错
#增加返回类型的判断
def parse_json2(url, data):
response = requests.get(url, params=data)
#获取响应头的content_type
content_type = response.headers.get('Content-Type')
if content_type.endswith('json'):
print(response.json())
elif content_type.startswith('text'):
print(response.text)
if __name__ == '__main__':
url1 = 'http://httpbin.org/get'
data1 = {'name': 'tom', 'age': 20}
url2 = 'https://www.baidu.com/s'
data2 = {'wd': 'python'}
parse_json(url1, data1)
parse_json2(url2, data2)
23.3.4 简单保存一个二进制文件
二进制内容为response.content
- 图片:
import requests
response = requests.get('https://pic4.zhimg.com/80/v2-e3438d2fc003329e1fa3ae4ee94903bf_1440w.jpg?source=1940ef5c')
b = response.content
with open('fengjing.jpg','wb') as f:
f.write(b)
- 视频:
import requests
response = requests.get('https://vdn.vzuu.com/HD/d3e878bc-e62f-11ec-8f32-2ea37be35c9e-v4_t121-wghOYfkVKm.mp4?disable_local_cache=1&bu=http-1513c7c2&c=avc.4.0&f=mp4&expiration=1654871876&auth_key=1654871876-0-0-06c7423155449d5ae0b5c7c17cd0baec&v=ali&pu=1513c7c2')
b = response.content
with open('fengjing.mp4','wb') as f:
f.write(b)
- 音频
import requests
response = requests.get('https://gg-sycdn.kuwo.cn/cff8afeee3d82fca6d2776717df44e7f/62a34c6d/resource/n3/12/69/4182461584.mp3')
b = response.content
with open('fengjing.mp3','wb') as f:
f.write(b)
23.3.5 response.text和response.content的区别
response.text
类型:str
解码类型:根据HTTP头部对响应的编码做出有根据的推测,推测的文本编码
如何修改编码方式:response.encoding="gbk"
response.content
23.3.6 为请求添加头信息类型:bytes
解码类型:没有指定
如何修改编码方式:response.content.decode("utf8")
import requests
def hasetheaders():
headers = {}
headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36'
response = requests.get('http://www.baidu.com',headers=headers)
print(response.request.headers.get('User-Agent'))
def hasnoheaders():
response = requests.get('http://www.baidu.com')
print(response.request.headers.get('User-Agent'))
if __name__ == '__main__':
#请求头中的'User-Agent'没有设置
hasnoheaders()
#请求头中的'User-Agent'设置了操作系统和浏览器的相关信息
hasetheaders()
23.3.7 基本代理
23.3.8 带参数的get请求
import requests
headers = {
'Cookie':'',
'Host':'www.zhihu.com',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36'
}
r = requests.get('https://www.zhihu.com/people/jackde-jie-jie',headers=headers)
with open('zhihu.txt', 'w', encoding='utf8') as fi:
fi.write(r.text)
# print(r.text)
获取cookie
import requests
1
response = requests.get('http://www.baidu.com')
print(response.cookies)
print(type(response.cookies))
for k,v in response.cookies.items():
print(k+':'+v)
23.2.9 cookie和session
cookie和session的区别
- cookie数据存放在客户的浏览器上,session数据放在服务器上。
- cookie不是很安全,别人可以分析存放在本地的 COOKIE 并进行 COOKIE 欺骗考虑到安全应当使用session。
- session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用COOKIE。
- 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
处理cookie和session
- 带上cookie和session的好处:能够请求到登陆后的页面
- 带上cookie和session的弊端:一套cookie和session往往对应一个用户,请求太快;请求次数太多,容易被识别为爬虫;不需要cookie的时候尽量不去使用cookie
但是有时为了获取登陆的页面,必须发送带有cookie的请求
requests提供了一个sessiion类,来实现客户端和服务器端的会话保持
使用的方法:
- 实例化一个session对象
- 让session来发送get或post请求
session=requests.session()
response=session.get(url,headers)
会话维持
import requests
requests.get('http://httpbin.org/cookies/set/number/12345') # 设置cookies
r = requests.get('http://httpbin.org/cookies') # 获取cookies
print(r.text) # 没有内容
session = requests.Session() # 创建一个会话
session.get('http://httpbin.org/cookies/set/number/12345') # 设置cookies
response = session.get('http://httpbin.org/cookies') # 获取cookies
print(response.text) # 有内容
23.3.9 证书验证设置
import requests
from requests.packages import urllib3
urllib3.disable_warnings() # 从urllib3中消除警告
response = requests.get('https://www.12306.cn',verify=False) # 证书验证设为FALSE
print(response.status_code)
打印结果:200
23.3.10 异常处理
在你不确定会发生什么错误时,尽量使用try…except来捕获异常
所有的requests exception:
import requests
from requests.exceptions import ReadTimeout,HTTPError,RequestException
try:
response = requests.get('http://www.baidu.com',timeout=0.5)
print(response.status_code)
except ReadTimeout:
print('timeout')
except HTTPError:
print('httperror')
except RequestException:
print('reqerror')
超时异常捕获
import requests
from requests.exceptions import ReadTimeout
from requests.exceptions import ConnectTimeout
try:
res = requests.get('http://httpbin.org', timeout=0.1)
print(res.status_code)
except (ReadTimeout,ConnectTimeout) as timeout:
print(timeout)
23.4 数据提取
简单的来说,数据提取就是从响应中获取我们想要的数据的过程。
23.4.1 数据分类非结构化的数据:html等
处理方法:正则表达式、xpath
23.4.2 JSON数据提取结构化数据:json,xml等
处理方法:转化为python数据类型
为什么要学习json
由于把json数据转化为python内建数据类型很简单,所以爬虫中,如果我们能够找到返回json数据的URL,就会尽量使用这种URL,而很多地方也都会返回json
什么是json
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,它使得人们很容易的进行阅读和编写。同时也方便了机器进行解析和生成。适用于进行数据交互的场景,比如网站前台与后台之间的数据交互。
哪里能找到返回json的url
- 使用chrome切换到手机页面;
- 抓包手机app的软件
具有read()或者write()方法的对象就是类文件对象:f = open(“a.txt”,”r”) f 就是类文件对象
json在数据交换中起到了一个载体的作用,承载着相互传递的数据
json使用注意点
json中的字符串都是用 双引号 引起来的,且必须是双引号,如果不是双引号:
- eval:能实现简单的字符串和python类型的转化
- replace:把单引号替换为双引号
往一个文件中写入多个json串,不再是一个json串,不能直接读取,这时我们可以一行写一个json串,按照行来读取
23.4.3 正则表达式提取- 知乎爬取
import requests
import re
#如果不设置这个headers,知乎会拒绝我们访问
headers = {}
headers['User-Agent'] = 'Mozilla/5.0 '
'(Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 '
'(KHTML, like Gecko) Version/5.1 Safari/534.50'
response = requests.get('https://www.zhihu.com/special/1394398803970056192',headers=headers)
#print(response.text)
pattern = re.compile(r'>(.*?)', re.S)
titles = re.findall(pattern , response.text)
print(titles)
print(len(titles))
- 抓取代理ip
import requests
import re
def get_html(url):
heads = {}
heads['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0'
req = requests.get(url, headers=heads)
html = req.text
return html
def get_ipport(html):
regex = r'(.+) '
iplist = re.findall(regex, html)
regex2 = '(.+) '
portlist = re.findall(regex2, html)
regex3 = r'(.+) '
typelist = re.findall(regex3, html)
sumray = []
for i in iplist:
for p in portlist:
for t in typelist:
pass
pass
a = t+','+i + ':' + p
sumray.append(a)
print('高匿代理')
print(sumray)
return sumray
if __name__ == '__main__':
free_proxy_url = 'http://www.kuaidaili.com/free/'
urls = ['https://www.kuaidaili.com/free/inha/{}/'.format(page) for page in range(1,50)]
proxys = []
for url in urls:
res = get_ipport(get_html(url))
proxys.append(res)
print(proxys)
- 博客园数据爬取
#coding=utf-8
'''
标题,时间,阅读量,评论数,推荐数
'''
import requests
import re
def get_one_page(url):
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36'
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
return response.text
return None
def parse_one_page(html):
str_text = html.replace("n","")
blogs = re.findall(r',str_text)
for blog in blogs:
title = re.findall(r'(.+?)', blog)
date = re.findall(r'>.*?>(.*?)',blog)
read = re.findall(r'阅读((.*?))',blog)
comment = re.findall(r'评论((.*?))',blog)
recommend = re.findall(r'推荐((.*?))',blog)
print(list(zip(title,date,read,comment,recommend)))
strs = 't'.join(list(zip(title,date,read,comment,recommend))[0]).replace(' ','')
with open('博客园数据.txt','a',encoding='utf-8') as f:
f.write(strs+'n')
def main():
url = 'https://www.cnblogs.com/pinard/default.html'
for page in range(1,15):
urls = f"{url}?page={page}"
html = get_one_page(urls)
# print(html)
parse_one_page(html)
main()
urls = ['https://www.cnblogs.com/pinard/default.html']
#urls = ['https://www.cnblogs.com/pinard/default.html?page={}'.format(page) for page in range(1,15)]
- 猫眼数据爬取
import requests
import re
def get_one_page(url):
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36'
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
return response.text
return None
def parse_one_page(html):
index = re.findall(r',html)
print(index)
def main():
url = 'https://www.maoyan.com/BOARD'
html = get_one_page(url)
# print(html)
parse_one_page(html)
main()
23.4.4 动态网站数据抓取
AJAX(Asynchronouse JavaScript And XML)异步JavaScript和XML。过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。传统的网页(不使用Ajax)如果需要更新内容,必须重载整个网页页面。因为传统的在传输数据格式方面,使用的是XML语法。因此叫做AJAX,其实现在数据交互基本上都是使用JSON。使用AJAX加载的数据,即使使用了JS,将数据渲染到了浏览器中,在右键->查看网页源代码还是不能看到通过ajax加载的数据,只能看到使用这个url加载的html代码。
获取ajax数据的方式:
| 方式 | 优点 | 缺点 |
|---|---|---|
| 分析接口 | 直接可以请求到数据。不需要做一些解析工作,代码量少,性能高。 | 分析接口比较复杂,特别是一些通过js混淆的接口,要有一定的js功底。容易发现是爬虫 |
| sel eni um | 直接模拟浏览器的行为。浏览器能请求到的,使用selenium也能请求到。爬虫更稳定。 | 代码量多,性能低。 |
Selenium+chromedriver获取动态数据:
Selenium相当于是一个机器人。可以模拟人类在浏览器上的一些行为,自动处理浏览器上的一些行为,比如点击,填充数据,删除cookie等。chromedriver是一个驱动Chrome浏览器的驱动程序,使用他才可以驱动浏览器。当然针对不同的浏览器有不同的driver。以下列出了不同浏览器及其对应的driver:
Chrome:https://sites.google.com/a/chromium.org/chromedriver/downloads
Firefox:https://github.com/mozilla/geckodriver/releases
Edge:https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
Safari:https://webkit.org/blog/6900/webdriver-support-in-safari-10/
安装Selenium和chromedriver:
- 安装Selenium:Selenium有很多语言的版本,有java、ruby、python等。我们下载python版本的就可以了。
pip install selenium - 安装chromedriver:下载完成后,放到不需要权限的纯英文目录下就可以了。
Selenium和chromedriver:
from selenium import webdriver
# chromedriver的绝对路径
driver_path = r'D:ProgramAppchromedriverchromedriver.exe'
# 初始化一个driver,并且指定chromedriver的路径
driver = webdriver.Chrome(executable_path=driver_path)
# 请求网页
driver.get("https://www.baidu.com/")
# 通过page_source获取网页源代码
print(driver.page_source)
Notes:
如果只是想要解析网页中的数据,那么推荐将网页源代码扔给lxml来解析。因为lxml底层使用的是C语言,所以解析效率会更高。
如果是想要对元素进行一些操作,比如给一个文本框输入值,或者是点击某个按钮,那么就必须使用selenium给我们提供的查找元素的方法。
Beautiful Soup是什么?
Beautiful Soup(简称BS4)提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。
Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。
BeautifulSoup已成为和lxml、html6lib一样出色的python解释器,为用户灵活地提供不同的解析策略或强劲的速度。
Beautiful Soup不需要考虑编码方式,除非文档没有指定,重新原始编码方式即可。
soup = BeautifulSoup(html_str, 'lxml')
初始化Beautifol Soup 实例化一个bf4对象,相当于一个根节点对象,它的子节点会作为他的实例属性保存起来
这个实例属性是一个tag实例对象:
tag实例对象的主要属性:
string(可以是该节点的文本内容,相当于xpath中的text())
attrs:该节点/标签的属性,是个字典。比如字典里有{‘id’:‘title0’}
该节点的子节点会作为它的实例属性保存起来,如果有同名的子节点,则只会将第一个作为实例属性保存起来
import requests from bs4 import BeautifulSoup html_str = """demo 1111111 """ soup = BeautifulSoup(html_str, 'lxml') #初始化Beautifol Soup 实例化一个bf4对象 print(type(soup)) print(soup.prettify()) # 这个方法可以把要解析的字符串以标准的缩进格式输出 print(soup.title.string) print(soup.head) print(soup.p.string) print(soup.name) print(soup.body.name)
如何遍历文档树?
- 直接子节点:
.contents :标签的内容 .content属性可以将tag的子节点以列表的方式输出
.children:返回一个可迭代对象 - 所有子孙节点:
.descendants属性可以对所有tag的子孙节点进行递归循环,和children类似,我们也需要遍历获取其中内容。 - 节点内容:
.string:返回标签里面的内容
.text:返回标签的文本
import requests from bs4 import BeautifulSoup html_str = """demo 1111111 """ soup = BeautifulSoup(html_str, 'lxml') #初始化Beautifol Soup 实例化一个bf4对象 print(type(soup)) #
# print(soup.prettify()) # 这个方法可以把要解析的字符串以标准的缩进格式输出 print(soup.title.string) # demo print(soup.head) ''' demo ''' print(soup.p.string) # 1111111 /n print(soup.name) # [document] print(soup.body.name) # body


![[深度学习]Part1 Python学习进阶Ch23~Ch2——【DeepBlue学习笔记】 [深度学习]Part1 Python学习进阶Ch23~Ch2——【DeepBlue学习笔记】](http://www.mshxw.com/aiimages/31/961190.png)
