[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】

本文仅供学习使用

Python高级——Ch23爬虫Spider

  • 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.5.2 BS4选择元素
      • 23.5.3 BS4提取信息
      • 23.5.4 find_all方法选择器
      • 23.5.5 CSS选择器
    • 23.6 IXML
      • 23.6.1 节点与属性
      • 23.6.2 Xpath
      • 23.6.3 数据爬取
    • 23.7 小结
    • 23.8 MySQL
      • 23.8.1 MySQL的启动与关闭
      • 23.8.2 MySQL数据库的基础操作
      • 23.8.3 Python操作MySQL
      • 23.8.4 博客园爬取
    • 23.8 Scrapy
      • 23.8.1 异步与非阻塞的区别
      • 23.8.2 爬虫流程
      • 23.8.3 Scrapy 项目文件构成
      • 23.8.4 小结


23. 爬虫Spider

为什么要学习爬虫&爬虫的定义

· 网络爬虫又称网络蜘蛛、网络机器人等,可以自动化浏览网络中的信息,当然浏览信息的时候需要按照所制定的相应规则进行,即网络爬虫算法。
· 原因很简单,我们可以利用爬虫技术,自动地从互联网中获取我们感兴趣的内容,并将这些数据内容爬取回来,作为我们的数据源,从而进行更深层次的数据分析,并获得更多有价值的信息。
· 在大数据时代,这一技能是必不可少的。
· 只要是浏览器能做的事情,原则上,爬虫都能够做。

爬虫的思路
每一个网页都是一份 HTML文档,全称叫hypertext markup language,是一种文本标记语言
[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第1张图片
这是一份很有规则的文档写法,打开一个网页,即是通过了 HTTP协议,对一个资源进行了请求,返还你一份 HTML文档,然后浏览器进行文档的渲染,这样,就形成了一个网页。所以,我们只需要模拟浏览器,发送一份请求,获得这份文档,再抽取出我们需要的内容就好。

通用爬虫和聚焦爬虫工作流程
[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第2张图片

通用搜索引擎的局限性
· 通用搜索引擎所返回的网页里90%的内容无用。
· 图片、音频、视频多媒体的内容通用搜索引擎无能为力。
· 不同用户搜索的目的不全相同,但是返回内容相同

ROBOTS协议
Robots协议:网站通过Robots协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取。
例如:https://www.taobao.com/robots.txt

浏览器发送HTTP请求的过程 ——浏览器渲染出来的页面和爬虫请求的页面并不一样
[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第3张图片

23.1 HTTP基本原理

23.1.1 URI与URL

URI 的全称为Uniform Resource Identifier ,即统一资源标志符
URL 的全称为Universal Resource Locator ,即统一资源定位符

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

· URL是URI的子集
· URI 还包括一个子类叫作URN ,它的全称为Universal Resource Name,即统一资源名称。

URN举例, um:isbn:0451450523
指定了一本书的ISBN,可以唯一标识这本书,但是没有指定到哪里定位这本书。
URN用得非常少,所以几乎所有的URI 都是URL,一般的网页链接我们可称为URI也可称为URL,习惯上称为URL。

URI,URL,URN三者的关系:
[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第4张图片

23.1.2 超文本

· 超文本,其英文名称叫作 hypertext ,我们在浏览器里看到的网页就是超文本解析而成的, 其网页源代码是一系列HTML 代码, 里面包含了一系列标签。

比如 显示图片, 为超链接等。

· 浏览器解析这些标签后,便形成了我们平常看到的网页,而网页的源代码HTML 就可以称作超文本。
· 在Chrome浏览器中打开任意一个页面,右击任意地方并选择“检查”(或者快捷键F12)即可打开开发者工具,Elements选项卡即可看到当前页的源代码,这些源代码都是超文本。

23.1.3 HTTP 和 HTTPS

· 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 。

如果没有SSL加密,会被浏览器提示不安全
比如 http://www.pudong.gov.cn/shpd/department/019022/
有些网站虽然使用了HTTPS协议,还是会被浏览器提示不安全,比如早期的12306网站,它的CA证书是中国铁道部自行签发的,而这个证书不被CA机构信任。
爬取这样的站点,就需要设置忽略证书的选项,否则会提示SSL 链接错误。

23.1.4 HTTP请求过程

[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第5张图片
我们在浏览器中输入一个URL ,回车之后便会在浏览器中观察到页面内容。实际上,这个过程是浏览器向网站所在的服务器发送了一个请求,网站服务器接收到这个请求后进行处理和解析,然后返回对应的响应,接着传回给浏览器。
响应里包含了页面的源代码等内容,浏览器再对其进行解析,便将网页呈现了出来。

比如:https://www.baidu.com/,用Chrome 浏览器的开发者模式下的Network 监听组件来做下演示,它可以显示访问当前请求网页时发生的所有网络请求和响应

打开Chrome 浏览器,输入链接 https://www.baidu.com/
右击并选择“检查”项,即可打开浏览器的开发者工具(F12)

[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第6张图片

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

我们先观察第一个网络请求,即www. baidu.com 点击这个条目,即可看到更详细的信息
[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第7张图片

General 部分
[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第8张图片

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

Response Headers Request Headers ,这分别代表响应头和请求头。

23.1.5 请求

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

  1. 请求方法( 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 方法的补充,用来对已知资源进行局部更新 。
  1. 请求的网址( Request URL )
    请求的网址,即统一资惊定位符URL ,它可以唯一确定我们想请求的资源。

  2. 请求头( 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 识别出是我们自己,并且查出当前状态是登录状态,所以返回结果就是登录之后才能看到的网页内容。

  3. 请求体( Request Body )
    请求体-般承载的内容是 POST 请求中的表单数据,而对于 GET 请求,请求体则为空。
    例如登录猫眼电影https://maoyan.com时捕获到的请求和响应如图所示:
    [深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第9张图片
    登录之前,填写了用户名和密码信息,提交时这些内容就会以表单数据的形式提交给服务器, 此时需要注意Request Headers 中指定Content-Typeapplication, 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数据

23.1.6 响应

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

  1. 响应状态码( Response Status Code )
    在爬虫中,我们可以根据状态码来判断服务器响应状态,如状态码为200 ,则证明成功返回数据, 再进行进一步的处理,否则直接忽略。
状态码 英文名称 中文描述
200 OK 请求成功。一般用于GET与POST请求
404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
500 Internal Server Error 服务器内部错误,无法完成请求

更多状态码可查看:https://www.runoob.com/http/http-status-codes.html

  1. 响应头( 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: 指定响应的过期时间, 可以使代理服务器或浏览器将加载的内容更新到缓存里。如果再次访问时,就可以直接从缓存中加载, 降低服务器负载,缩短加载时间。

  2. 响应体( Response Body )
    响应的正文数据都在响应体中,比如请求网页时,它的响应体就是网页的 HTML 代码; 请求一张图片时, 它的响应体就是图片的二进制数据。我们做爬虫请求网页后, 要解析的内容就是响应体。
    在做爬虫时,我们主要通过响应体得到网页的源代码、JSON 数据等, 然后从中做相应内容的提取[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第10张图片

23.1.7 网页的组成

网页可以分为三大部分一一HTML , CSS 和JavaScript 。如果把网页比作一个人的话, HTML 相当于骨架, JavaScript 相当于肌肉, css 相当于皮肤,三者结合起来才能形成一个完善的网页。

  1. HTML
    HTML 是用来描述网页的一种语言, 其全称叫作 Hyper Text Markup Language ,即超文本标记语言。网页包括文字、按钮、图片和视频等各种复杂的元素,其基础架构就是HTML 。不同类型的元素通过不同类型的标签来表示,如图片用 标签表示, 视频用 标签表示,段落用

    标签表示,它们之间的布局又常通过布局标签

    嵌套组合而成,各种标签通过不同的排列和嵌套才形成了网页的框架。
    在Chrome 浏览器中打开百度, 有击并选择“检查” 项(或按Fl2 键) ,打开开发者模式, 这时在Elements 选项卡巾即可看到网页的源代码。标签定义的节点元素相互嵌套和组合形成了复杂的层次关系,就形成了网页的架构。
    [深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第11张图片
  2. CSS
    CSS ,全称叫作 Cascading Style Sheets ,即层叠样式表。“层叠”是指当在HTML 中引用了数个样式文件,并且样式发生冲突时,浏览器能依据层叠顺序处理。“样式”指网页中文字大小、颜色、元素间距、排列等格式。
    CSS 是目前唯一的网页页面排版样式标准。

大括号前面是一个CSS 选择器,大括号里面是对选择到的节点的样式设置。
[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第12张图片

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

23.1.8 节点数及节点之间的关系

在 HTML 中,所有标签定义的内容都是节点,它们构成了一个HTML DOM 树。
[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第13张图片

23.1.9 选择器

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

选择器 示例 示例说明
.class .intro 选择所有class="intro"的元素
#id #firstname 选择所有id="firstname"的元素
* * 选择所有元素
element p 选择所有

元素

element,element div,p 选择所有
元素和

元素

element>element div>p 选择所有父级是
元素的

元素

[attribute=value] [target=-blank] 选择所有使用target="-blank"的元素

23.1.10 流程回顾

[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第14张图片

23.1.11 能抓怎样的数据

基于HTTP 或HTTPS 协议的,只要是这种数据,爬虫都通过它的URL进行抓取。
· 常规网页,它们对应着HTML 代码,抓取的便是HTML 源代码。
· JSON 字符串(其中API 接口大多采用这样的形式),这种格式的数据方便传输和解析,它们同样可以抓取,而且数据提取更加方便。
· 二进制数据,如图片、视频和音频等。利用爬虫,我们可以将这些二进制数据抓取下来,然后保存成对应的文件名。
· 扩展名的文件,如css 、JavaScript 和配置文件等,这些其实也是最普通的文件,只要在浏览器里面可以访问到,就可以将其抓取下来。

23.1.12 JavaScript渲染页面

有时候,我们在用urllib 或requests“抓取网页时,得到的源代码实际和浏览器中看到的不一样。这是一个很常见的问题。现在网页越来越多地采用Ajax 、前端模块化工具来构建,整个网页可能都是由JavaScript 渲染出来的,也就是说原始的HTML 代码就是一个空壳。

< ! OOCTYPE html>
<html>
<head>
<meta charset="UTF-8>
<title>This is a Oemo</title>
</head>
<body>
<div id=” container” >
</div>
</body>
<script src=” app.js ”></script>
</html>

body 节点里面只有一个id 为container 的节点,但是需要注意在body 节点后引入了app. js,它便负责整个网站的渲染。
在用时lib 或requests 等库请求当前页面时,我们得到的只是这个HTML 代码,它不会帮助我们去继续加载这个JavaScript 文件,这样也就看不到浏览器中的内容了。

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

23.2 会话和Cookies

在浏览网站的过程中,我们经常会遇到需要登录的情况,有些页面只有登录之后才可以访问,而且登录之后可以连续访问很多次网站,但是有时候过一段时间就需要重新登录,还有一些网站,在打开浏览器时就向动登录了,而且很长时间都不会失效,这种情况又是为什么? 其实这里面涉及会话 (Session)和 Cookies 的相关知识,

23.2.1 静态网页和动态网页

  1. 静态网页
<!DOCTYPE html>
<html> <head>
<meta charset=UTF-8">
<title>This is a Demo</title>
</head>
<body>
<div id=” container”>
<div class =”rapper ”〉
<h2 class =”title'’ > Hello World </ h2 >
<p class=”text ”> Hello, this is a paragraph. </ p>
</div>
</div>
</body>
</html>
  1. 动态网页
<!DOCTYPE html>
<html> <head>
<meta charset=UTF-8">
<title>This is a Demo</title>
</head>
<body>
<div id=” container”>
<div class =”rapper ”〉
<h2 class =”title'’ > Hello World! 
<%= request.getParameter("name")%>
</ h2 >
<p class=”text ”> Hello, this is a paragraph. </ p>
</div>
</div>
</body>
</html>

动态解析URL 中参数的变化,关联数据库井动态呈现不同的页面内容。
可能由JSP 、PHP 、Python 等语言编写的

23.2.2 无状态HTTP

HTTP 的无状态是指HTTP 协议对事务处理是没有记忆能力的,也就是说服务器不知道客户端是什么状态。
[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第15张图片
如果后续需要处理前面的信息,则必须重传,这导致需要额外传递一些前面的重复请求,才能获取后续响应。这给需要用户登录的页面来说,非常棘手。
[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第16张图片

  1. Cookies
    Cookies 在客户端,也可以理解为浏览器端,有了Cookies ,浏览器在下次访问网页时会自动附带上它发送给服务器,服务器通过识别Cookies 并鉴定出是哪个用户,然后再判断用户是否是登录状态,然后返回对应的响应。
    我们可以理解为Cookies 里面保存了登录的凭证,有了它,只需要在下次请求携带Cookies 发送请求而不必重新输入用户名、密码等信息重新登录了。
    因此在爬虫中,有时候处理需要登录才能访问的页面时,我们一般会直接将登录成功后获取的Cookies 放在请求头里面直接请求,而不必重新模拟登录。
  2. 会话
    会话在服务端,也就是网站的服务器,用来保存用户的会话信息。会话,其本来的含义是指有始有终的一系列动作/消息。比如,打电话时,从拿起电话拨号到挂断电话这中间的一系列过程可以称为一个会话。而在Web 中,会话对象用来存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的Web 页之间跳转时,存储在会话对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的Web 页时如果该用户还没有会话, 则Web 服务器将自动创建一个会话对象。当会话过期或被放弃后,服务器将终止该会话。

23.2.3 会话维持

[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第17张图片

23.2.4 代理的基本原理

[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第18张图片
反爬虫措施: 检测某个IP 在单位时间内的请求次数,如果超过了这个阔值,就会直接拒绝服务
对于爬虫来说,由于爬虫爬取速度过快,在爬取过程中可能遇到同一个IP 访问过于频繁的问题,此时网站就会让我们输入验证码登录或者直接封锁IP ,这样会给爬取带来极大的不便。
[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第19张图片
使用代理隐藏真实的IP ,让服务器误以为是代理服务器在请求向己。这样在爬取过程中通过不断更换代理,就不会被封锁,可以达到很好的爬取效果。
代理:实现IP 伪装,反反爬虫

23.3 requests

为什么要学习requests,而不是urllib?

  1. requests的底层实现就是urllib。
  2. requests在python2和python3中通用,方法完全一样。
  3. requests简单易用。
  4. requests能够自动帮我们解压(gzip压缩等)网页内容。

作用:发送网络请求,返回响应数据
中文文档API:https://requests-docs-cn.readthedocs.io/zh_CN/latest/_modules/requests/api.html

23.3.1 requests的用法

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方法

get方法请求:
requests.get(url,headers,params,verify)
url:请求链接字符串
headers:请求头部信息,是一个字典,常用字段:‘user-agent’,‘cookie’
params:链接里需要携带的参数,是一个字典,如果url已经设置,则不需要传递
verify:忽略ssl证书警告
response对象:(requests.get())
response.request:查看请求信息,如:response.request.headers
response.headers:查看相应的头部信息
response.status_code:相应的状态码,200表示正确,400,401,404,500,…都不正常
response.headers[‘Content_Type’]:相应内容的类型,text/html;…json
response.text:相应内容,(html源码)
response.json():相应内容,(json数据)
response.content:相应内容,(音频,视频,图片等)

23.3.2 带参数的get请求

  1. 直接将参数放在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)
  1. 先将参数填写在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

  1. 图片:
    [深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第20张图片
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)
  1. 视频:
    [深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第21张图片
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)
  1. 音频
    [深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第22张图片
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

类型:bytes
解码类型:没有指定
如何修改编码方式:response.content.decode("utf8")

23.3.6 为请求添加头信息

[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第23张图片

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的区别

  1. cookie数据存放在客户的浏览器上,session数据放在服务器上。
  2. cookie不是很安全,别人可以分析存放在本地的 COOKIE 并进行 COOKIE 欺骗考虑到安全应当使用session。
  3. session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用COOKIE。
  4. 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

处理cookie和session

  1. 带上cookie和session的好处:能够请求到登陆后的页面
  2. 带上cookie和session的弊端:一套cookie和session往往对应一个用户,请求太快;请求次数太多,容易被识别为爬虫;不需要cookie的时候尽量不去使用cookie
    但是有时为了获取登陆的页面,必须发送带有cookie的请求

requests提供了一个sessiion类,来实现客户端和服务器端的会话保持
使用的方法:

  1. 实例化一个session对象
  2. 让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

结构化数据:json,xml等
处理方法:转化为python数据类型

23.4.2 JSON数据提取

为什么要学习json
由于把json数据转化为python内建数据类型很简单,所以爬虫中,如果我们能够找到返回json数据的URL,就会尽量使用这种URL,而很多地方也都会返回json

什么是json
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,它使得人们很容易的进行阅读和编写。同时也方便了机器进行解析和生成。适用于进行数据交互的场景,比如网站前台与后台之间的数据交互。

哪里能找到返回json的url

  1. 使用chrome切换到手机页面;
  2. 抓包手机app的软件

[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第24张图片

具有read()或者write()方法的对象就是类文件对象:f = open(“a.txt”,”r”) f 就是类文件对象

json在数据交换中起到了一个载体的作用,承载着相互传递的数据
[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第25张图片
json使用注意点
json中的字符串都是用 双引号 引起来的,且必须是双引号,如果不是双引号:

  1. eval:能实现简单的字符串和python类型的转化
  2. replace:把单引号替换为双引号

往一个文件中写入多个json串,不再是一个json串,不能直接读取,这时我们可以一行写一个json串,按照行来读取

23.4.3 正则表达式提取

  1. 知乎爬取
    [深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第26张图片
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'class="css-49pz06">(.*?)', re.S)
titles = re.findall(pattern , response.text)
print(titles)
print(len(titles))
  1. 抓取代理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)
  1. 博客园数据爬取
#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'class="dayTitle">.*?>(.*?)',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)]
  1. 猫眼数据爬取
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:

  1. 安装Selenium:Selenium有很多语言的版本,有java、ruby、python等。我们下载python版本的就可以了。
    pip install selenium
  2. 安装chromedriver:下载完成后,放到不需要权限的纯英文目录下就可以了。

Selenium和chromedriver:

from selenium import webdriver
# chromedriver的绝对路径
driver_path = r'D:\ProgramApp\chromedriver\chromedriver.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给我们提供的查找元素的方法。

23.5 Beautiful Soup

Beautiful Soup是什么?
Beautiful Soup(简称BS4)提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。
Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。
BeautifulSoup已成为和lxml、html6lib一样出色的python解释器,为用户灵活地提供不同的解析策略或强劲的速度。
Beautiful Soup不需要考虑编码方式,除非文档没有指定,重新原始编码方式即可。

23.5.1 BS4引入

from bs4 import BeautifulSoup
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)

如何遍历文档树?

  1. 直接子节点:
    .contents :标签的内容 .content属性可以将tag的子节点以列表的方式输出
    .children:返回一个可迭代对象
  2. 所有子孙节点:
    .descendants属性可以对所有tag的子孙节点进行递归循环,和children类似,我们也需要遍历获取其中内容。
  3. 节点内容:
    .string:返回标签里面的内容
    .text:返回标签的文本
from bs4 import BeautifulSoup

html_str = """


demo


1111111

222222

333333

"""
soup = BeautifulSoup(html_str, 'lxml') #(1)子节点和子孙节点 #直接子节点使用contents属性 print(soup.body.contents) #children属性 print(soup.body.children) for i , child in enumerate(soup.body.children): print(i, child) #所有子孙节点,descendants print(soup.body.descendants) for i, child in enumerate(soup.body.descendants): print(i, child) #(2)父节点和祖先节点 print(soup.title.parent)#直接父节点 print(list(enumerate(soup.title.parents)))#所有的祖先节点 #(3)兄弟节点 print("previous_sibling",soup.p.previous_sibling) print("next_sibling",soup.p.next_sibling.string) print("previous_siblings",list(soup.p.previous_siblings)) print("next_siblings",soup.p.next_siblings)

23.5.2 BS4选择元素

html_str = """


demo


1111111

2222222

"""
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 print(type(soup.title)) print(soup.title.string) print(soup.head) print(soup.p)

23.5.3 BS4提取信息

import requests
from bs4 import BeautifulSoup

html_str = """


demo


1111111

abc

"""
soup = BeautifulSoup(html_str,'lxml') #选择节点 title_tag = soup.title # 重要的数据结构:'bs4.element.Tag' #提取信息 #(1)获取名称 tag_name = title_tag.name print(tag_name) # title #(2)获取属性 #方法1:使用attrs属性 atrr_id = title_tag.attrs['id'] print(atrr_id) # title0 print(title_tag.attrs) # {'id': 'title0'} #方法2:使用方括号[] atrr_id = title_tag['id'] print(atrr_id) # title0 #(3)获取内容 #单个节点 title_text = title_tag.string print(title_text) # demo #嵌套选择 span_tag = soup.p.span print(span_tag) # span节点 print(span_tag.name) # 节点名称 print(span_tag.attrs) # 节点属性 print(span_tag.string) # 节点的文本内容

23.5.4 find_all方法选择器

find_all(name,attrs,recursive,text,**kwargs)

  1. 传入True:找到所有的Tagsoup.find_all('b') # 直接找元素相当于name = 'b'
  2. 传入正则表达式:通过正则找
import re
for tag in soup.find_all(re.compile("^b")):
	print(tag.name)
  1. 传入列表:找a 和 b标签soup.find_all(["a", "b"])
  2. keyword参数(name,attrs)soup.find_all(id='link2')
    如果传入 href 参数,Beautiful Soup会搜索每个tag的”href”属性:soup.find_all(href=re.compile("elsie"))
  3. 通过text参数可以搜索文档中的字符串内容
    soup.find_all(text="Elsie")
    soup.find_all(text=['Tillie','Elsie','Lacie'])
  4. 限定查找个数-limit参数soup.find_all("a",limit=2)
  5. recursive参数:调用tag的 find_all方法,BeautifulSoup会检查当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可是使用参数recursive=Falsesoup.find_all('title',recursive=False)
from bs4 import BeautifulSoup
import re

html_str = """


This is title.
This is title also.


1111111

2222222

"""
soup = BeautifulSoup(html_str,'lxml') print(soup.find(text=re.compile('title'))) # This is title.
from bs4 import BeautifulSoup
import re

html_str = """


This is title.
This is title also.



1111111

2222222

"""
soup = BeautifulSoup(html_str) #find_all(name , attrs , recursive , text , **kwargs) #(1)name 通过标签名定位 print(soup.find_all(name='p')) # 等同于soup.body.children 获取根节点下的所有瓶子为p的节点 for p in soup.find_all(name='p'): print(p.string) for span in p.find_all(name='span'): print(span.string) print(soup.p.span.string) print(soup.p.string) #(2)attrs通过属性定位——找到一个节点 print(soup.find_all(attrs={'id':'title0'})) # [This is title.] print(soup.find_all(attrs={'class':'class0'})) # [

2222222

]
#(3)text:可用来匹配节点的文本,传入的形式可以是字符串,也可以是正则表达式对象 print(soup.find_all(text=re.compile('title'))) # ['This is title.', 'This is title also.']
  1. 结合节点操作print(soup.find(id='head'.div.div.next_sibling.next_sibling)
  2. find_parents() find_parent()
    find_all() 和 find() 只搜索当前节点的所有子节点,孙子节点等.
    find_parents() 和 find_parent() 用来搜索当前节点的父辈节点,搜索方法与普通tag的搜索方法相同,搜索文档搜索文档包含的内容
  3. find_next_siblings() find_next_sibling()
    这2个方法通过 .next_siblings 属性对当 tag 的所有后面解析的兄弟 tag 节点进行迭代,
    find_next_siblings() 方法返回所有符合条件的后面的兄弟节点
    find_next_sibling() 只返回符合条件的后面的第一个tag节点
  4. find_previous_siblings() find_previous_sibling()
    这2个方法通过 .previous_siblings 属性对当前 tag 的前面解析的兄弟 tag 节点进行迭代,
    find_previous_siblings() 方法返回所有符合条件的前面的兄弟节点,
    find_previous_sibling() 方法返回第一个符合条件的前面的兄弟节点
  5. find_all_next() find_next()
    这2个方法通过 .next_elements 属性对当前 tag 的之后的 tag 和字符串进行迭代,
    find_all_next() 方法返回所有符合条件的节点
    find_next() 方法返回第一个符合条件的节点
  6. find_all_previous() 和 find_previous()
    这2个方法通过 .previous_elements 属性对当前节点前面的 tag 和字符串进行迭代,
    find_all_previous() 方法返回所有符合条件的节点,
    find_previous()方法返回第一个符合条件的节点

23.5.5 CSS选择器

这就是另一种与 find_all 方法有异曲同工之妙的查找方法. 写 CSS 时,标签名不加任何修饰,类名前加.,id名前加#在这里我们也可以利用类似的方法来筛选元素,用到的方法是 soup.select(),返回类型是 list

  1. 通过标签名查找 soup.select('a')
  2. 通过类名查找 soup.select('.sister')
  3. 通过 id 名查找 soup.select('#link1')
  4. 组合查找soup.select('p #link1')
  5. 属性查找 soup.select('a[class="sister"]')
  6. 获取内容 get_text()
from bs4 import BeautifulSoup
import re

html_str = """


This is title.
This is title also.


1111111

2222222

  • python
  • PHP
  • Jave
  • javescrip
"""
soup = BeautifulSoup(html_str) print(soup.select('.course')) print(soup.select('#title0')) print(soup.select('ul li')) # 空格表示承接关系 for li in soup.select('ul li'): print(li.string) print("====="*20) for li in soup.select('ul li'): print(li.get_text())

博客园数据爬取

'''
标题,时间,阅读量,评论数,推荐数
url = 'https://www.cnblogs.com/pinard/default.html'
'''
import requests
import re
from bs4 import BeautifulSoup

def get_one_page(url):
    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(url,headers=headers)
    
    if response.status_code == 200:
        return response.text
    return None

def parse_one_page(html):
    html = html.replace('\n','')
    soup = BeautifulSoup(html, 'lxml')
    
    blogs = soup.select('div.day')
    
    for blog in blogs:
        title = [blog.select('div.postTitle > a > span')[0].string.strip()]
        time = blog.find_all(text=re.compile(r'(....年.*月.*日)'))
        reader_str = blog.find_all(text=re.compile(r'阅读\((\d+)'))
        #reader_str = blog.select('div.postDesc > span.post-view-count')[0].string
        comments_num = blog.find_all(text=re.compile(r'评论\((\d+)'))
        recommend_num = blog.find_all(text=re.compile(r'推荐\((\d+)'))
        print(list(zip(title,time,reader_str,comments_num,recommend_num)))
    

def main():
    url = 'https://www.cnblogs.com/pinard/default.html'
    html = get_one_page(url)
    parse_one_page(html)

main()
'''
标题,时间,阅读量,评论数,推荐数
url = 'https://www.cnblogs.com/pinard/default.html'
'''
import requests
import re
from bs4 import BeautifulSoup

def get_one_page(url):
    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(url,headers=headers)
    
    if response.status_code == 200:
        return response.text
    return None

def parse_one_page(html):
    print()
    soup = BeautifulSoup(html, 'lxml')
    
    blogs = soup.select('.day')
    for blog in blogs:
        day = blog.select(".dayTitle a")[0].string
        title = blog.select('a span')[0].string.strip()
        abstr = blog.select('.c_b_p_desc')[0].contents[0]
        desc = blog.select('.postDesc span')
        view,comment,digg = (re.findall(r'\d+',i.string)[0] for i in desc)
        data = '\t'.join((title,day,view,comment,digg)).replace(' ','')
        yield data        
        
    

def main():
    url = 'https://www.cnblogs.com/pinard/default.html'
    html = get_one_page(url)
    parse_one_page(html)

main()

小结

  1. 构建一个soup对象
  2. 定位节点
    2.1 soup.find_all(name),attrs,text
    2.2 soup.tagname
  3. 获取节点的属性信息:tag对象.name(节点名称),tag对象.attrs(属性),tag对象.string(文本)
  4. tag节点对象的用法基本上与我们soup类相同

23.6 IXML

安装:pip install lxml
lxml库:
lxml 是 一个HTML/XML的解析器,主要的功能是如何解析和提取 HTML/XML 数据。
lxml和正则一样,也是用 C 实现的,是一款高性能的 Python HTML/XML 解析器,我们可以利用XPath语法,来快速的定位特定元素以及节点信息。
from lxml import etree

from lxml import etree

# 构建文档树
html_str = """


demo
</head>
<body>
<p>1111111
</body>
</html>
"""</span>
<span class="token comment"># 利用html_str创建一个节点树对象</span>
html_tree <span class="token operator">=</span> etree<span class="token punctuation">.</span>HTML<span class="token punctuation">(</span>html_str<span class="token punctuation">)</span>
<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token builtin">type</span><span class="token punctuation">(</span>html_tree<span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token comment"># 输出结果为:lxml.etree._Element</span>
result <span class="token operator">=</span> etree<span class="token punctuation">.</span>tostring<span class="token punctuation">(</span>html_tree<span class="token punctuation">)</span>  <span class="token comment"># 自动修正html</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>result<span class="token punctuation">.</span>decode<span class="token punctuation">(</span><span class="token string">'utf8'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
</code></pre> 
  <h4>23.6.1 节点与属性</h4> 
  <ol> 
   <li>Element类是lxml的一个基础类,大部分XML都是通过Element存储的。可以通过Element方法创建:</li> 
  </ol> 
  <pre><code class="prism language-python"><span class="token keyword">from</span> lxml <span class="token keyword">import</span> etree

root<span class="token operator">=</span>etree<span class="token punctuation">.</span>Element<span class="token punctuation">(</span><span class="token string">'root'</span><span class="token punctuation">)</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>root<span class="token punctuation">.</span>tag<span class="token punctuation">)</span>  <span class="token comment"># root</span>
</code></pre> 
  <ol start="2"> 
   <li>为root节点添加子节点:</li> 
  </ol> 
  <pre><code class="prism language-python">child1 <span class="token operator">=</span> etree<span class="token punctuation">.</span>SubElement<span class="token punctuation">(</span>root<span class="token punctuation">,</span><span class="token string">'child1'</span><span class="token punctuation">)</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>root<span class="token punctuation">)</span>  <span class="token comment"># <Element root at 0x27fc8a43100></span>
<span class="token keyword">print</span><span class="token punctuation">(</span>etree<span class="token punctuation">.</span>tostring<span class="token punctuation">(</span>root<span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token comment"># b'<root><child1/></root>'</span>
</code></pre> 
  <ol start="3"> 
   <li>XML Element的属性格式为Python的dict。可以通过get/set方法进行设置或获取操作:</li> 
  </ol> 
  <pre><code class="prism language-python">root<span class="token punctuation">.</span><span class="token builtin">set</span><span class="token punctuation">(</span><span class="token string">'id'</span><span class="token punctuation">,</span><span class="token string">'123'</span><span class="token punctuation">)</span>
<span class="token builtin">id</span> <span class="token operator">=</span> root<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">'id'</span><span class="token punctuation">)</span>
<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token builtin">id</span><span class="token punctuation">)</span>  <span class="token comment"># 123</span>
</code></pre> 
  <ol start="4"> 
   <li>遍历全部属性:</li> 
  </ol> 
  <pre><code class="prism language-python"><span class="token keyword">for</span> value<span class="token punctuation">,</span>name <span class="token keyword">in</span> root<span class="token punctuation">.</span>items<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">print</span><span class="token punctuation">(</span>value<span class="token punctuation">,</span><span class="token string">'\t'</span><span class="token punctuation">,</span>name<span class="token punctuation">)</span>  <span class="token comment"># id       123</span>
</code></pre> 
  <h4>23.6.2 Xpath</h4> 
  <p>XPath(XML Path Lauguage)是一门在XML文档中查找信息的语言,可用来在XML文档中对元素和属性进行遍历。</p> 
  <ol> 
   <li>选取节点</li> 
  </ol> 
  <table> 
   <thead> 
    <tr> 
     <th>表达式</th> 
     <th>描述</th> 
    </tr> 
   </thead> 
   <tbody> 
    <tr> 
     <td>nodename</td> 
     <td>选取此节点的所有子节点</td> 
    </tr> 
    <tr> 
     <td>/</td> 
     <td>从根节点选取</td> 
    </tr> 
    <tr> 
     <td>//</td> 
     <td>从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置</td> 
    </tr> 
    <tr> 
     <td>.</td> 
     <td>选取当前节点</td> 
    </tr> 
    <tr> 
     <td>…</td> 
     <td>选取当前节点的父节点</td> 
    </tr> 
    <tr> 
     <td>@</td> 
     <td>选取属性</td> 
    </tr> 
   </tbody> 
  </table> 
  <ol start="2"> 
   <li>谓语<br> 谓语用来查找某个特定的节点或者包含某个指定的值的节点,被嵌在方括号中。在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:</li> 
  </ol> 
  <table> 
   <thead> 
    <tr> 
     <th>路径表达式</th> 
     <th>结果</th> 
    </tr> 
   </thead> 
   <tbody> 
    <tr> 
     <td>/bookstore/book[1]</td> 
     <td>选取属于bookstore子元素的第一个book元素</td> 
    </tr> 
    <tr> 
     <td>/bookstore/book[last()]</td> 
     <td>选取属于bookstore子元素的最后一个book元素</td> 
    </tr> 
    <tr> 
     <td>/bookstore/book[last() - 1]</td> 
     <td>选取属于bookstore子元素的倒数第二个book元素</td> 
    </tr> 
    <tr> 
     <td>/bookstore/book[position < 3]</td> 
     <td>选取最前面的两个属于bookstore元素的子元素的book元素</td> 
    </tr> 
    <tr> 
     <td>//title[@lang]</td> 
     <td>选取所有拥有名为lang的属性title元素</td> 
    </tr> 
    <tr> 
     <td>//title[@lang = ‘eng’]</td> 
     <td>选取所有title元素,且这些元素拥有值为eng的lang属性</td> 
    </tr> 
    <tr> 
     <td>/bookstore/book[price > 35.00]</td> 
     <td>选取bookstore 元素的所有book元素,且其中的price元素的值须大于35.00</td> 
    </tr> 
    <tr> 
     <td>/bookstore/book[price > 35.00]/title</td> 
     <td>选取bookstore元素中的book元素的所有title元素,且其中的price元素的值须大于35.00</td> 
    </tr> 
    <tr> 
     <td>//div[contains(@class,“f1”)]</td> 
     <td>选择div属性包含"f1"的元素</td> 
    </tr> 
   </tbody> 
  </table> 
  <ol start="3"> 
   <li>选取未知节点<br> XPath 通配符可用来选取未知的 XML 元素</li> 
  </ol> 
  <table> 
   <thead> 
    <tr> 
     <th>通配符</th> 
     <th>描述</th> 
    </tr> 
   </thead> 
   <tbody> 
    <tr> 
     <td>*</td> 
     <td>匹配任何元素节点</td> 
    </tr> 
    <tr> 
     <td>@*</td> 
     <td>匹配任何属性节点</td> 
    </tr> 
    <tr> 
     <td>node()</td> 
     <td>匹配任何类型的节点</td> 
    </tr> 
   </tbody> 
  </table> 
  <ol start="4"> 
   <li>Xpath运算符<br> 与运算符基本一致</li> 
  </ol> 
  <table> 
   <thead> 
    <tr> 
     <th>运算符</th> 
     <th>描述</th> 
     <th>实例</th> 
     <th>返回值</th> 
    </tr> 
   </thead> 
   <tbody> 
    <tr> 
     <td>丨</td> 
     <td>计算两个节点集</td> 
     <td><code>//book丨 //cd</code></td> 
     <td>返回所用拥有bookl和cd元素的节点集</td> 
    </tr> 
   </tbody> 
  </table> 
  <pre><code class="prism language-python"><span class="token keyword">from</span> lxml <span class="token keyword">import</span> etree

<span class="token comment">#构建文档树</span>
html_str <span class="token operator">=</span> <span class="token triple-quoted-string string">"""
<html>
<head>
<title id='title0'>demo


1111111

a22

2222222

aaa """
# 利用html_str创建一个节点树对象 html_tree = etree.HTML(html_str) #所有节点 all_e = html_tree.xpath('//*') print(all_e) #指定节点 title_e = html_tree.xpath('//title/text()') print(title_e) # ['demo'] #子节点 child_e = html_tree.xpath('//body/p') print(child_e) #第N个子节点 child_e_2 = html_tree.xpath('//body/p[2]') print(child_e_2) #父节点 parent_e = html_tree.xpath('//title/../*') print(parent_e) #属性匹配 title_id = html_tree.xpath('//title[@id="title0"]') print(title_id) #文本获取 title_text = html_tree.xpath('//title[@id="title0"]/text()') print(title_text) # ['demo'] #属性多值匹配 p1_text = html_tree.xpath('//p[@class="p1"]/text()') print(p1_text) # 没有匹配 p1_text = html_tree.xpath('//p[contains(@class, "p1")]/text()') print(p1_text) # ['1111111', '2222222'] #多属性 p2_text = html_tree.xpath('//p[contains(@class, "p1") and @id="p2"]/text()') print(p2_text) # ['2222222']

23.6.3 数据爬取

#coding=utf-8
'''
标题,时间,阅读量,评论数,推荐数
url = 'https://www.cnblogs.com/pinard/default.html'
'''
import requests
import re
from lxml import etree

def get_one_page(url):
    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(url,headers=headers)
    
    if response.status_code == 200:
        return response.text
    return None

def parse_one_page(html):
    xpath_html = etree.HTML(html)
    
    blogs = xpath_html.xpath('//*[@id="mainContent"]/div/div')
    
    for blog in blogs:
        title = blog.xpath('./div[2]/a/span/text()')
        time = blog.xpath('./div[1]/a/text()')
        reader_num = blog.xpath('./div[5]/span[1]/text()')
        comments_num = blog.xpath('./div[5]/span[2]/text()')
        recommend_num = blog.xpath('./div[5]/span[3]/text()')
        print(title,time,reader_num,comments_num,recommend_num)
    

def main():
    url = 'https://www.cnblogs.com/pinard/default.html'
    html = get_one_page(url)
    parse_one_page(html)

main()

23.7 小结

小结:

  1. re 正则表达式,比较难
  2. lxml xpath表达式:
    一,先实例化一个文档树对象:html=etree.HTML()
    二,调用html.xpath(表达式)
    三,表达式的语法:
    2.1 //div
    2.2 //div/a
    2.3 谓语表达
    //div/a[@name=“a1”] ;
    //div/a[contains(@name, “a1”)] ;
    //div/a[1]
    //div/a[@name=“a1” and contains(@class, “c1”)]
    以上返回的是列表,列表里面是Element对象实例,常用属性:
    Element.text 获取文本内容
    Element.attrib 获取标签的属性字典,比如:Element.attrib[‘href’]
    2.4 //div/a/text() :获取文本内容
  3. bs4
    #1.构建一个soup对象
    #2.定位节点:
    2.1 soup.find_all(name, attrs, text)
    2.2 soup.tagname
    2.3 soup.select(css选择器表达式)
    css选择器表达式:
    2.3.1. .class属性的值
    2.3.2. #id属性的值
    2.3.3. 标签名 子标签名 :div a
    2.3.4.谓语表达式: 标签名[属性名=属性值]
    以上的定位方法返回tag对象/tag对象列表
    #3.获取节点的属性信息:tag对象.name(节点名称), tag对象.attrs(属性字典), tag对象.string(文本))
    #4.tag节点对象的用法基本上与我们soup类相同

23.8 MySQL

MySQL 是一个轻量级的关系型数据库,服务器,没有可视化界面
MySQL 使用标准的sql语言
教程参考:https://www.runoob.com/mysql/mysql-tutorial.html

23.8.1 MySQL的启动与关闭

启动服务:net start mysql
进入mysql操作管理界面:mysql -u root –p
退出mysql操作管理界面:exit;
停止服务:net stop mysql

  1. 系统入口:
    [深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第27张图片

  2. 页面入口:Ctrl + Shift + 右键 于空白文档,打开powershell
    [深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第28张图片

23.8.2 MySQL数据库的基础操作

显示数据库:show databases;
进入一个数据库:use mysql;
显示库里面所有表:show tables;
查询表中所有数据:select * from user;
查询指定字段的数据:select Host,User from user;

创建数据库:create database spider;
创建表: 表名:blog + 字段名:标题,日期,阅读量,评论数,推荐数
create table blog(title VARCHAR(100) COMMENT'标题',date VARCHAR(20) COMMENT'发布时间',read_num int COMMENT'阅读数量',comment_num int COMMENT'评论数量',recomend_num int COMMENT'推荐数量');

varchar相当于字符串,()内为长度

[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第29张图片

往表中插入数据:
#插入数据 insert into XGBoost类库使用小结 2019年7月1日 阅读(32912) 评论(119) 推荐(14)
#XGBoost算法原理小结 2019年6月5日 阅读(37969) 评论(181) 推荐(18)
insert into blog VALUES('XGBoost类库使用小结','2019年7月1日',32912,119,14);
insert into blog VALUES('XGBoost算法原理小结','2019年6月5日',37969,181,18);

查表语句 select from:select title,read_num,comment_num from blog;
修改语句 update:UPDATE blog set comment_num=1 where comment_num=119;
删除数据 DELETE :DELETE from blog where comment_num=1
[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第30张图片

关联查询(多个表查询) left join(左关联) inner join(内关联) right JOIN(右关联)
A inner join B 取交集。
A left join B 取 A 全部,B 没有对应的值为 null。
A right join B 取 B 全部 A 没有对应的值为 null。
select * from student inner join score on student.name = score.name;
select * from student left join score on student.name = score.name;
select * from student right join score on student.name = score.name;

#示例中的数据准备
#创建表
create table student(name VARCHAR(10), age int, weight int);
create table score(name varchar(10), score int, course varchar(10));
#插入数据
insert into student values('susan',10,80);
insert into student values('Tom',10,70);
insert into student values('Tommy',10,70);
insert into score Values('susan',90,'语文');
insert into score Values('susan',90,'英语');
insert into score Values('susan',90,'数学');
insert into score Values('Tom100,'数学');
insert into score Values('Tom',88,'语文');
insert into score Values('Tom',91,'英语');
insert into score Values('Tata',91,'英语');

小结:
1、启动数据库服务:net start mysql
2、连接数据库:mysql -u root -p
3、显示数据库:show databases;
4、使用数据库:use 数据库名称;
5、创建数据库:create database 名称;
6、创建表格:create table 表名(字段名 字段类型);
7、插入数据:insert into 表名 values(字段1的值,字段2的值,…);
8、更新数据:update 表名 set 字段=新值 where 字段名=查询条件;
9、删除数据:delete from 表名;

23.8.3 Python操作MySQL

pip install pymysql

  1. 连接数据库:
    假设当前的 MySQL 运行在本地,用户名为 root ,密码为123456,运行端口为 3306 。这里利用PyMySQL 先连接 MySQL ,然后创建一个新的数据库,名字叫作spider。
#1、创建连接
connect = pymysql.connect(host='localhost',
user='root',
password='123456',
port=3306,
database='spider')
  1. 创建游标:
    调用cursor()方法获得MySQL 的操作游标,利用游标来执行SQL 语句
#2、创建游标
cursor = connect.cursor()
  1. 创建表:
#3、创建表
create_table_sql = '''
create table if not exists blog(title VARCHAR(50),
date VARCHAR(15),
read_num int,
comment_num int,
tuijian int)
'''
cursor.execute(create_table_sql)
  1. 插入数据:
#4、插入数据,方法一
insert_sql = '''insert into blog VALUES('XGBoost类库使用小结','201971',32912,119,14)
'''
cursor.execute(insert_sql)
connect.commit()  # 增,删,改需要commit()

[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第31张图片

  1. 更新数据:
# 5. 更新
update_sqlstr = '''
update blog2 set recomend_num=16 where title like '%999';'''  # like 模糊匹配
cursor.execute(update_sqlstr)
connect.commit()  # 增,删,改需要commit()
connect.close()
  1. 删除数据
# 6 .删除
delete_sqlstr = 'delete from blog2 where recomend_num=16;'
cursor.execute(delete_sqlstr)
connect.commit()
connect.close()
  1. 查询数据
select_sql = '''
select * from blog
'''
cursor.execute(select_sql)
#data = cursor.fetchone() #查询一条数据
#datas = cursor.fetchall() #查询所有数据
datas = cursor.fetchmany(2) #查询多条数据
print(datas)

23.8.4 博客园爬取

#coding=utf-8
'''
标题,时间,阅读量,评论数,推荐数
url = 'https://www.cnblogs.com/pinard/default.html'
'''
import requests
import re
from bs4 import BeautifulSoup
import pymysql

def get_one_page(url):
    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(url,headers=headers)
    
    if response.status_code == 200:
        return response.text
    return None

def parse_one_page(html):
    print()
    soup = BeautifulSoup(html, 'lxml')
    
    blogs = soup.select('.day')
    for blog in blogs:
        day = blog.select(".dayTitle a")[0].string
        title = blog.select('a span')[0].string.strip()
        abstr = blog.select('.c_b_p_desc')[0].contents[0]
        desc = blog.select('.postDesc span')
        view,comment,digg = (re.findall(r'\d+',i.string)[0] for i in desc)
        # data = '\t'.join((title,day,view,comment,digg)).replace(' ','')
        yield title,day,view,comment,digg        
def save2mysql(data):
    
    #1、创建连接
    connect = pymysql.connect(host='localhost',
                            user='root',
                            password='123456',
                            port=3306,
                            database='spider')
    
    #2、创建游标 
    cursor = connect.cursor()
    insert_sqlstr3 = "insert into blog2 values(%s,%s,%s,%s,%s)"
    
    cursor.execute(insert_sqlstr3, data)
    connect.commit()
    connect.close()    
    

def main():
    url = 'https://www.cnblogs.com/pinard/default.html'
    html = get_one_page(url)
    parse = parse_one_page(html)
    for data in parse:
        save2mysql(data)

main()

23.8 Scrapy

Scrapy 是一个为了爬取网站数据,提取结构性数据而编写的应用框架,我们只需要实现少量代码,就能够快速的抓取到数据内容。
Scrapy 使用了 Twisted ['twɪstɪd] (其主要对手是Tornado)异步网络框架来处理网络通讯,可以加快我们的下载速度,不用自己去实现异步框架,并且包含了各种中间件接口,可以灵活的完成各种需求。

安装(anaconda):使用anaconda直接 conda install scrapy

启动时需改变路径

23.8.1 异步与非阻塞的区别

[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第32张图片
异步: 调用在发出之后,这个调用就直接返回,不管有无结果
非阻塞: 关注的是程序在等待调用结果(消息,返回值)时的状态,指在不能立刻得到结果之前,该调用不会阻塞当前线程。

23.8.2 爬虫流程

前面的爬虫流程

[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第33张图片

另一种形式爬虫流程

[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第34张图片

Scrapy的爬虫流程

[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第35张图片
[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第36张图片

1.创建一个scrapy项目scrapy startproject mySpider
2.生成一个爬虫scrapy genspider xxx “xxx.com”
3.提取数据完善spider,使用xpath等方法
4.保存数据pipeline中保存数据

23.8.3 Scrapy 项目文件构成

当创建好一个项目后,会生成几个项目文件

|-- spider. py ---- 爬虫文件夹
|---- _init_. py ---- 默认的爬虫代码文件
|---- myspider. py ---- 具体爬虫代码文件
|-- _init_. py ---- 爬虫项目的初始化文件,用来对项目做初始化工作。
|-- items. py ---- 爬虫项目的数据容器文件,用来定义要获取的数据。
|-- pipelines. py ---- 爬虫项目的管道文件,用来对items中的数据进行进一步的加工处理
|-- middlewares.py ---- 爬虫中间件文件,处理request和reponse等相关配置
|-- setting. py ---- 爬虫项目的设置文件,包含了爬虫项目的设置信息
|-- scrapy. cfg ---- 爬虫项目的配置文件,定义了项目的配置文件路径、部署相关信息等内容

[深度学习]Part1 Python学习进阶Ch23爬虫Spider——【DeepBlue学习笔记】_第37张图片

23.8.4 小结

代码流程:

  1. 使用指令创建一个项目
    指令:scrapy startproject mySpider
    1.1方式1 cmd/powershell 命令窗口 输入指令执行(推荐)
    1.2方式2 创建一个main.py文件,使用excute函数执行
    from scrapy.cmdline import execute
    execute([‘scrapy’, ‘startproject’, ‘mySpider’])

  2. 使用指令创建一只spider
    指令:scrapy genspider cnblogs www.cnblogs.com
    2.1方式1 cmd/powershell 命令窗口 输入指令执行(推荐)
    先 cd D:\deepblue\course\NO.202202\code\spider\mySpider\mySpider
    然后再输入指令执行
    2.2方式2 创建一个spidermain.py文件,使用excute函数执行
    在D:\deepblue\course\NO.202202\code\spider\mySpider\mySpider下创建一个spidermain.py文件,
    使用excute函数执行

  3. 修改相关文件:
    3.1 修改items.py,按照模板定义提取的字段
    3.2 修改piplines.py, 按照模板定义数据处理类,需要注意,
    该类要实现process_item方法,如果有多个处理类,就需要return item,
    3.3 修改settings.py文件,放开piplines的注释,设置每个数据处理类的执行优先级,数字越小优先级越高
    3.4 修改spiders文件夹下的蜘蛛文件,比如本项目的cnblogs.py
    按照模板定义一个解析类,实现parse方法,数据传递使用yield item

  4. 启动爬虫:
    指令:scrapy crawl 蜘蛛名
    1.1方式1 cmd/powershell 命令窗口 输入指令执行
    1.2方式2 创建一个main.py文件(跟item.py同级目录),使用excute函数执行(推荐),因为可以打断点检查

你可能感兴趣的:(python,深度学习,机器学习,python,学习,爬虫)