1. 什么是爬虫?
我们首先看一下客户端访问服务器时候的流程
简单来说,就是客户端向服务器发起请求,服务器响应请求,把相应的资源返回给客户端。
还不理解?举个通俗易懂的例子
当我们在我们的浏览器上输入www.baidu.com这个url后按下回车后,就向百度的服务器端发起请求,请求百度搜索的主页面资源,此时百度的服务器端收到请求,处理请求,然后把百度搜索的主页面以html文档的形式返回去,我们客户端收到了以下html文档。
我们客户端的浏览器对该html文档进行渲染,然后就出现了我们搜索的主页面界面。
html文档中包含文字、图片、URL链接,甚至音乐、程序等元素,通过浏览器的渲染,显示在我们的浏览器上。
所以,整个流程就是:
浏览器提交请求->下载html网页代码->解析/渲染成页面。
下图是互联网页面的划分,可以看出来一个网页它有可能还会链接到另外一个或多个网页,而另外一个网页也有可能会链接到另外一个或多个网页,因此,网页之间是通过链接建立起关系的。
我们根据这种关系,就可以一步步地根据页面关系找到初始的一个网页与它有直接或间接的所有网页链接。
因此,爬虫就是模拟浏览器请求初始的一条URL(链接),下载html,再从html文档中找所有URL放进URL队列,然后从URL(链接)队列中取出一条URL(链接)继续模拟浏览器请求该URL(链接),下载html,再从该html文档中找到所有URL(链接)再放进队列.....反复执行到URL(链接)队列为空为止。这样就找到了初始URL(链接)的所有与其有直接或间接关系的所有网页链接。
爬虫流程如下:
2. 下面开始进入我们的正题!
那就是简单暴力爬一些妹子图!
1. 我们需要requests和re两个模块。
import requests
import re
2. 首先我们要找到一个初始的URL链接作为爬虫的入口地址,我们选择了该网站,里面有大量的妹子图。
https://www.mzitu.com/all/
3. 然后我们python程序得先伪装成浏览器进行爬虫,不然对方服务器会认为我们是非法软件操作,然后把我们的请求封了。所以,伪装成浏览器的方法就是模仿浏览器定义一个请求头。
#伪装请求头
headers={
"user-agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWe"
"bKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36", #伪装成浏览器访问
"referer": "https://www.mzitu.com/all/" #破解网站的防盗链
}
其实伪装成浏览器的行为只要第3行定义user-agent字段名即可,但是此时还有第5行代码定义一个referer字段名,那它的主要作用是干嘛的呢?
很多网站都设置了一种叫做防盗链的功能。
防盗链的作用是,我们网站的图片,只能通过我们自己的网站去访问,其他网站借用不行。
图片防盗链原理:
http标准协议中有专门的字段记录referer
一来可以追溯上一个入站地址是什么
二来对于资源文件,可以跟踪到包含显示他的网页地址是什么
因此所有防盗链方法都是基于这个Referer字段
因为我们是要直接访问其网站上的妹子图片资源然后进行下载的,如果没有referer字段,对方服务器会禁止我们访问其网站上的图片资源,他们会认为我们是属于其他网站的借用行为,所以我们得加上referer字段,表明了我们是通过referer字段里的那个网站去访问的,这样才能绕过对方服务器的防盗链访问到对方网站上的妹子资源,然后进行下载到我们的电脑上进行保存。
4. 通过分析html页面,我们知道html文档中图片资源路径的标签是这样写的
<li>
.....
data-original='https://i.meizitu.net/thumbs/2019/06/181419_23b31_236.jpg'
....
</li>
//data-original跟的地址即为妹子图片的路径。
html文档中跳转URL链接的标签是这样写的
设置两个正则表达式,对html中的内容进行查找。
pic = "<li>.*?<img.*?data-original='(.*?)'.*?</li>"
links = '<a href="(.*?)".*?'
第1行,目的匹配html页面中的图片链接,返回(.*?)括号内的内容,即匹配到的html页面中的图片链接。
第2行,目的匹配html页面中的跳转链接,返回(.*?)括号内的内容,即匹配到的html页面中的跳转URL链接。
5. 然后下面就是开始爬虫的核心代码了,完全是根据上面的爬虫原理及爬虫流程图进行Code的。
index = 0 #图片命名,自增,1.jpg 2.jpg ....
hashmap = {} #存储已经访问过的html链接{url1:1,url2:1,....,url_n:1}
#开始爬虫
def start_pa(url):
global index #引用外部index变量
global hashmap #引用外部hashma爬变量
response = requests.get(url, headers=headers) #请求url
html = response.text #返回结果,一个html
hashmap.update(url=1) #记录该链接,表示已经访问过了
pic_src = re.findall(pic, html) #找到html中所有的图片链接
html_link = re.findall(links, html) #找到html中所有的跳转链接
#遍历html中的图片链接
for src in pic_src:
index += 1
r = requests.get(src, headers=headers) #请求图片链接,返回结果
with open(str(index) + '.jpg', 'wb') as f:
f.write(r.content) #二进制方式写图片二进制码,存储图片
print("download---"+str(index)+".jpg")
#遍历当前html中的跳转链接
for link in html_link:
if not(link in hashmap.keys()): #若没访问过
start_pa(link) #就继续爬该没访问过的链接
看到上面代码,有个要注意的点,为什么我需要用一个hashmap来存储已经访问过的链接呢。因为会出现如下这种情况。
a.html中会链接到b.html,b.html会链接到a.html,这样在爬虫的时候,爬a.html完后会爬b.html,爬b.html完后又会爬a.html,这样就形成了个死循环,我们要杜绝这种情况,所以我们得有一个hashmap存储已经访问过的链接,访问过的,就不能再访问!
这样,就能爬下https://www.mzitu.com/all/页面所有直接或间接的链接,然后在爬下每个链接的时候,得到所有的图片资源路径并下载到我们当前目录下。
然后在电脑上跑一跑
然后就得到了许许多多的meizi图片了,由于图片少儿不宜,我打上了马赛克,如果你想揭开马赛克的面纱,那就去你的电脑试着跑一下吧。
跑完整个程序需要两天两夜,想想有多少张meizi图吧!嘻嘻
当然,上面所讲的是最基本而且最简单粗暴的爬虫,其实上面爬虫的代码效率很低而且是单线程的,网络操作、写磁盘操作这些都是很耗时的操作,当我们的程序是单线程的话,那我们在网络操作、写磁盘这些操作的时候,CPU就静静地等在那里,等这些耗时的操作完成后才继续往下执行其他代码。所以,我们可以实现一个多线程版本的爬虫。在多线程版本的爬虫基础上,我们又可以实现分布式的爬虫,当然,有兴趣的读者可以试着玩一下写一下,欢迎交流和指点!或者如果你有什么不明白的地方,欢迎后台留言、评论,我们必回!期待你的参与!