爬虫的重要性和广泛性无需赘述。本篇教程面向对爬虫感兴趣的小白同学们,有范君将奉上一篇清新脱俗,内容充实,可以引导爬虫初学者迅速入门的指南文章。在进行实践之前,我们先共同弄清楚几个事情:何为爬虫、爬什么和怎么爬?
01
何为爬虫?
提到爬虫,第一时间你想到的是互联网?是滑动的代码块?是源源不断地数据流?得了吧,难道不是一只有着些许科技感的蜘蛛,或是一只略显卡通的"七星瓢虫"。无论你想到的是什么,那么恭喜你,都是对的。其实无需纠结爬虫的详细定义,即按照一定规则去目标网页获取指定的信息。所以,其核心跃然纸上,即目标网页和爬取规则。
02
爬虫目标-网页
爬虫的基本对象是网页,那么我们有必要对网页的基本框架进行了解,正如庖丁解牛首先需要对牛的结构了然于心。
1
URL:连接网页
我们知道登陆某一网页第一步是输入其网址,然后回车键跳转。那么这个网址,统称为URL(Universal Resource Locator):统一资源定位符(更详细为URI,我们这里暂时不涉及)。
2
H-C-J:网页生成
HTML(hyper Text Markup Language):超文本标记语言,其构建网页的主要框架。CSS(Cascading Style Sheets):层叠样式表,用于网页的排版。JavaScript:用于在网页中提供交互选项和动画效果。三者协作,生成我们日常见到的各种各样的网页形态 。庆幸的是,虽然网页千变万化,但其内在结构大致统一,其主要内容填充在下图网页结构中。
对于网页的了解远远不止于此,但是作为入门级教程,熟悉以上这些内容,足以开始你的爬虫创作。
03
爬虫规则-模拟通信
熟悉目标之后,我们必须知道目标的行动规则,这样才能按照它的规则获取我们需要的信息。首先来看看当你输入url,然后点击回车后,跳转到指定网页的这个过程发生了什么事情。
HTML请求过程图
第一步,由客户端向服务器端发送请求,其中请求包含请求方法、请求头、请求体。请求方法包含GET和POST两种,其中POST一般用于需要输入用户密码的时候。其各自特点如下:
GET:参数包含在url里面,数据可见,最多1024字节;
POST:数据不包含在url中,通过表达方式传输,包含在请求体中,没有大小限制。
请求头内包客户端的一些信息,包括什么浏览器,接受什么语言等等,类似身份证。所以在爬虫经常构造请求头既是为了模拟一个真实的身份。
请求体一般包含一些请求数据表,如果是GET方法,请求体为空。
第二步,服务器端对请求信息进行解析,然后做出相应的响应。响应也分为三部分:响应码、响应头、响应体。
响应码用来表示服务器的相应状态,其中200表示成功。如果客户端的信息没问题,那么好,返回给客户端想要的网页信息,响应码为200;
响应头类似请求头,包含服务器端的一些信息;
响应体是网页的源码,HTML码,既是客户端需要的信息,也是爬虫的主要目标文档。
第三步,客户端进行对响应源码解析,并以网页的形式呈现出来。
弄清楚以上这个过程,爬虫的实现过程就已经很清晰了。首先我们拿到目标url,然后向其服务器端发送请求,有时还需要伪装一下身份。最后拿到目标的HTML源码,我们提取源码中有效信息即可。那么,具体如何实现呢。我们怎么发请求?怎么伪装身份?怎么提取有效信息呢?继续解决这三个核心问题,我们就完成了本次任务。
Python中提供了很多模块来解决相关问题,本篇我们介绍笔者比较熟悉的模块,感兴趣的同学可以进一步进行探索。其中requests模块用于发送请求和构造身份、BeautifulSoup和PyQuery模块均可用于提取有效信息。为了使爬虫任务变得异常简单,我们通过一个实例将整任务分解成几个步骤,以达到迅速入门的目的。
实例:爬取当当畅销书信息(书名、价格、推荐指数)
代码块-加载模块:
import requests
from bs4 import BeautifulSoup
如果加载不了模块,请提前安装,于命令行输入:
pip install 模块名即可。
第一步:获取目标网站url,发送请求,获取响应。
代码块-发送请求:
url = 'http://bang.dangdang.com/books/'
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36'
}
html = requests.get(url,headers = headers).text
requests的get方法可以发送请求,其中headers参数可以构造特定的浏览器类型,这里无需修改,固定这段代码。什么,你要灵活运用?不急不急,先走过核心步骤。requests的get方法返回对象的text属性为html源码构成的字符串。这样,我们完成第一步发送请求并获取了服务器端返回的响应。
第二步:解析网页,获取目标信息。
代码块-解析网页:
BeauSoupHtml = BeautifulSoup(html,'html.parser')
将响应的html源码字符串作为参数传给BeautifulSoup方法,第二个参数为Python自带的网页解析方法,固定不必改变,此方法返回sp对象,以便锁定信息。接下来获取目标信息是所有初学者最头疼的事情,如果你去查阅书籍,或者观看视频。发现他们会建议你先学习各种各样的选择器用于锁定目标信息,往往面对大量的规则让初学者感到反感。下面介绍一种相对简单且能应对大多数情况的方法。
锁定指定信息,必须从原网页入手,作者使用的谷歌浏览器。鼠标移至目标信息处,右键选择检查选项,如上图所示,页面右侧弹出目标信息的html源码位置。
定位html源码
<p class="bang_index_intro">
<a href="http://product.dangdang.com/23761145.html" target="_blank" title="人间失格(日本小说家太宰治的自传体小说)">人间失格(日本小说家太</a>
</p>
锁定上面这段源代码,我们寻找包含目标信息的标签和类,至于书名这个信息,我们可以发现其标签是p,类是bang_index_intro,有同学问书名的标签难道不是a吗?这里a标签没有类,难以精确确定位置,所以使用上级标签。那么一旦我们锁定了标签和类,即可对信息进行锁定。
代码块-获取目标信息块
title = BeauSoupHtml.find('p',{"class":"bang_index_intro"})
print(type(title))
print(title.children)
print(list(title.children)[0])
print(title.attrs)
print(title.contents)
sp对象BeauSoupHtml的find方法接收标签和类参数,以获取满足条件第一个信息块,返回对象是bs元素。我们需要记住的只是这个bs元素的几个属性:
title.children:下级标签内容;
list(title.children)[0]:下级标签第一个bs元素;
title.attrs:其包含的属性;
title.contents:其包含的内容,返回一个列表。还记得书名信息在我们锁定位置的下级标签内,那么list(title.children)[0]可以获取书名信息的a标签内容,于a标签内,我们可以看见书名是title属性,那我们可以进一步锁定书名信息。
定位html源码
<a href="http://product.dangdang.com/23761145.html" target="_blank" title="人间失格(日本小说家太宰治的自传体小说)">人间失格(日本小说家太</a>
代码块-锁定书名:
print(list(title.children)[0].attrs['title'])
list(title.children)[0]返回依然为bs元素对象,它依然有上面介绍的三个属性,调用attrs属性的title,即可获取目标信息:人间失格(日本小说家太宰治的自传体小说)。同样道理,我们练习锁定价格和推荐指数信息,依然使用网页检查的方法。
价格
推荐指数
定位html源码:
<p class="bang_index_price_e">电子书:
<i>4.99</i></p>
根据以上规则,通过find结合contents属性获取价格和推荐指数。
代码块-获取价格和推荐指数:
price = BeauSoupHtml.find('p',{"class":"bang_index_price"})
print(price.contents[0].strip())
#11.5
recommend = BeauSoupHtml.find('p',{"class":"bang_index_rec"})
print(recommend.contents[0].strip())
#100%推荐
通过标签和类来锁定信息,然后通过内容属性获取价格和推荐指数,最后使用strip()是去除字符串前后的空格。通过BeautifulSoup的find方法我么可以快捷的锁定所需信息,然而它只返回满足条件的第一个对象,接下来我么使用find_all方法获取所有满足条件的信息块,组成一个元素集,最后通过for方法可以顺序调用出来。
第三步:保存目标信息,搞定任务。
代码块-保存信息:
title_text = BeauSoupHtml.find_all('p',{"class":"bang_index_intro"})
price_text = BeauSoupHtml.find_all('p',{"class":"bang_index_price"})
recommend_text = BeauSoupHtml.find_all('p',{"class":"bang_index_rec"})
file = open('Books.txt','a',encoding = 'utf-8')
file.write('\t'.join(["title_name","price_now","recommend_rate"]) + '\n')
for title, price, recommend in zip(title_text,price_text,recommend_text):
title_name = list(title.children)[0].attrs['title']
price_now = price.contents[0].strip()
recommend_rate = recommend.contents[0].strip()
file.write('\t'.join([title_name,price_now,recommend_rate]) + '\n')
#file.write('\n' + '=' * 50 + '\n')
file.close()
调用find_all方法,获取所有满足条件的信息集;然后打开一个Books.txt文件来保存信息,先写入首行标题;最后依次写入爬取信息,完成保存文件。
写在最后的一段话
不知道看完本篇介绍,你是否已经跃跃欲试?爬虫的流程通俗易懂,基本过程的实现也简单明了。但是实践过程中,你可能会遇到各种各样的疑惑。不要担心,当你一步步的去实现自己的第一个爬虫程序,并且逐渐破解阻挡你爬取的难题的时候,就能体会到这项充满创造性工作的意义与乐趣。最后衷心希望这篇初级的Python爬虫指导,可以为读者打开通往爬虫世界的一扇门。