本文已参与「新人创作礼」活动,一起开启掘金创作之路。
引言
本文将从零开始,为各位掘友讲述如何实现一个简单的爬虫,话不多说,这就开始。
What
首先,什么是爬虫呢?爬虫就是利用程序帮我们获取有价值的信息,爬虫可以避免重复劳动,并且创造价值。 爬虫能做很多事,对于个人而言,它能做生活助手,比如:分析上海近两年二手房成交均价是多少?深圳的软件开发岗平均薪资是多少?等等。而对于公司,可以利用爬虫来创造巨大的商业价值,比如百度的搜索引擎,其核心技术之一就是爬虫。
百度会源源不断地把千千万万个网站爬取下来,存储在自己的服务器上。客户使用百度搜索的本质就是在它的服务器上搜索信息,搜索到一些超链接,在点击超链接跳转之后就可以访问其它网站。
爬虫伦理
恶意消耗别人的服务器资源,是一件不道德的事,网站的服务器被爬虫爬得多了,会受到较大的压力,它会拒绝频率很高,给其带来极大的压力或伤害的大型爬虫和恶意爬虫。
技术无罪,罪在人为,掘友可能听过这么一句话“爬虫写得好,牢饭吃到饱”,互联网不是法外之地,一定要知法守法。已经进入职场的掘友更要注意,不要因为老板让爬取什么数据就爬取什么,非法获取计算机信息系统数据罪了解一下。
另外还要提一嘴robots协议,即“网络爬虫排除标准”(robots exclusion protocol),这个协议用来告诉爬虫,哪些页面是可以抓取的,哪些不可以。并非所有网站都存在robots.txt,如果存在,爬虫应按照该文件中的内容来确定访问的范围;如果该文件不存在,任何爬虫将能够访问网站上所有没有被口令保护的页面。当爬取网站数据的时候,不要忘了看看网站的robots协议是否允许。在爬取网络中的信息时,我们应该有意识地去遵守这个协议。
我们以豆瓣的robots协议为例
User-agent: * #所有的爬虫
Disallow: /subject_search # 禁止爬取subject_search的整个目录
Disallow: /amazon_search
Disallow: /search #禁止爬取search的整个目录
Disallow: /group/search
Disallow: /event/search
Disallow: /celebrities/search
Disallow: /location/drama/search
Disallow: /forum/ # 禁止访问forum目录下面的目录
Disallow: /new_subject
Disallow: /service/iframe
Disallow: /j/
Disallow: /link2/
Disallow: /recommend/
Disallow: /doubanapp/card
Disallow: /update/topic/
Disallow: /share/ #禁止爬取share目录下面的目录
Allow: /ads.txt
Sitemap: https://www.douban.com/sitemap_index.xml #通知爬虫这个页面是网站地图
Sitemap: https://www.douban.com/sitemap_updated_index.xml
# Crawl-delay: 5
User-agent: Wandoujia Spider #豌豆荚爬虫
Disallow: / # 禁止访问所有页面
复制代码
上面每个段落都含有以下两种字段:一种是User-agent:
另一种是Allow:
或Disallow:
。
User-agent表示的是爬虫类型,上面的示例代码注释了“豌豆荚”,我们自己写的爬虫一般要看User-Agent: *,*指向所有未被明确提及的爬虫。
Allow代表允许被访问,Disallow代表禁止被访问。字段对应的值都含有路径分隔符/,限制了哪些或哪一层目录的内容是允许或者禁止被访问的。
爬虫原理
首先,看看我们是如何使用浏览器下载文件的, 而对于爬虫而言, 将最开始的【请求——响应】封装为一个步骤,即获取数据。
就得到以下四个步骤:
- 获取数据
- 解析数据
- 提取数据
- 储存数据
首先,爬虫会根据网址,模拟成浏览器向服务器发起请求,然后返回数据;
其次,爬虫能代替浏览器帮我们把服务器响应的数据解析成我们能读懂的格式;
接着,爬虫可以根据我们设定的规则批量提取相关数据;
最后,爬虫可以批量地把数据存储到本地,便于日后的使用和分析。
How
获取数据
即封装的请求与响应
我们可以利用一个库——Requests
来获取数据,它可以帮我们下载网页源代码、文本、图片、甚至音频。Requests使用文档掘友们可以自行翻阅。
库的安装
Mac电脑里打开终端软件,输入pip3 install requests
,然后点击enter Windows电脑里使用命令提示符,输入pip install requests
举一个简单的例子:
# 引入requests库
import requests
# 调用requests库中的get()方法,向服务器发送一个请求,括号里的参数是爬取的数据所在的网址,然后服务器会对请求作出了响应。服务器响应的结果是个Response对象,将结果存储到定义的变量res中。
res = requests.get('URL')
复制代码
Response对象
常用属性
属性 | 作用 |
---|---|
response.status_code | HTTP协议的响应状态码,通常用于检查请求是否成功(值是否为200) |
response.content | 把response对象转换为二进制数据(适用于图片、音频、视频的下载) |
response.text | 把response对象转换为字符串数据(适用于文本) |
response.encoding | 定义response对象的编码(解决文本的乱码问题) |
这里,使用requests.get()发送请求后,能得到一个Response对象,requests库会对数据的编码类型做出自己的判断。这个判断有可能准确,也可能不准确。如果判断不准确,就会出现一堆乱码,那我们可以去查看目标数据的编码,然后再用res.encoding把编码定义成和目标数据一致的类型即可。
数据的解析与提取
我们可以把解析和提取的任务交给第三方网页解析库BeautifulSoup
。使用文档在此,掘友们可以点个收藏,方便日后查阅。
由于它是第三方库,需要单独安装它,windows系统输入pip install BeautifulSoup4
。(Mac电脑需要输入pip3 install BeautifulSoup4
)
利用beautifulSoup解析
# 导入requests库
import requests
# 引入BS库,下面的bs4就是beautifulsoup4
from bs4 import BeautifulSoup
# 返回一个response对象
res = requests.get('URL')
# 把网页解析为BeautifulSoup对象
#第1个参数是要被解析的文本,它必须是字符串。第2个参数用来标识解析器,这里用的是Python内置库:html.parser。(它不是唯一的解析器,却是简单的那个)
soup = BeautifulSoup(res.text,'html.parser')
复制代码
这里soup
的数据类型是<class 'bs4.BeautifulSoup'>
,即BeautifulSoup
对象。
利用beautifulSoup提取
对于HTML和css有一定了解的掘友,这部分内容理解起来一定没什么问题。
find()和find_all()的用法
这两个方法可以匹配html的标签和属性,把BeautifulSoup对象里符合要求的数据都提取出来。
如表格所示
方法 | 作用 | 用法 | 示例 |
---|---|---|---|
find() | 提取满足要求的首个数据 | BeautifulSoup对象.find(标签,属性) | bs.find('div',class_='books') |
find_all() | 提取满足要求的所有数据 | BeautifulSoup对象.find_all(标签,属性) | bs.find_all('div',class_='books') |
示例中的class_
有一个下划线,这是为了和python语法中的类 class
区分,避免程序冲突。当然,除了用class属性去匹配,还可以使用其它属性。其次,括号中的参数:标签和属性可以任选其一,也可以两个一起使用。
另外,find()返回的数据类型是<class 'bs4.element.Tag'>
,是一个Tag
类标签对象。
find_all()返回的数据类型是<class 'bs4.element.ResultSet'>
,可以把它当做列表list
来看待。
Tag对象的常用属性和方法
属性/方法 | 作用 |
---|---|
Tag.find()和Tag.find_all() | 提取Tag中的Tag |
Tag.text | 提取Tag中的文字 |
Tag['属性名'] | 输入参数:属性名,可以提取Tag中这个属性的值 |
应当注意的是,Tag
对象可以使用find()
与find_all()
来继续检索。
存储数据
常用的存储数据的方式有两种——存储成csv格式文件或Excel文件。
这里我们只讲述前者,它的语法就是在字符串之间加分隔符,行与行之间是加换行符,同行字符之间是加逗号分隔。
它可以用任意的文本编辑器打开(如记事本),也可以用Excel打开,还可以通过Excel把文件另存为csv格式(因为Excel支持csv格式文件)。用csv格式存储数据,读写比较方便,易于实现,文件也会比Excel文件小。
使用之前,需要引用csv模块。csv模块是Python自带的,所以我们不需要安装就能引用它,这是csv模块官方文档
csv的写入
步骤
- 创建文件 调用open()
- 创建对象 借助writer()
- 写入内容 调用writer对象的writerow()方法
- 关闭文件 调用close()
open()函数的使用
b(bytes,字节) | + | b+ | ||
---|---|---|---|---|
r(read,读) | r只读,指针在开头文件不存在则报错 | rb二进制只读,其余同左 | r+读写,其余同左 | rb+二进制读写,其余同左 |
w(write,写) | w只写,文件不存在则新建,存在则覆盖 | wb二进制只写,其余同左 | W+读写,其余同左 | wb+二进制读写,其余同左 |
a(append,追加) | a追加,文件存在指针放在末尾,文件不存在则新建 | ab二进制追加,其余同左 | a+追加且可读,其余同左 | ab+二进制追加,且可读,其余同左 |
例如:
# 引用csv模块。
import csv
# 创建csv文件,我们要先调用open()函数,传入参数:文件名“demo.csv”、写入模式“w”、newline=''、encoding='utf-8'
#加 newline=' ' ,可以避免表格的行与行之间出现空白行,加 encoding='utf-8' ,避免编码问题导致的报错或乱码。
csv_file = open('demo.csv','w',newline='',encoding='utf-8')
# 用csv.writer()函数创建一个writer对象。
writer = csv.writer(csv_file)
# 调用writer对象的writerow()方法,可以往csv文件里写入新的内容,同时需把要写入的内容写成列表作为参数
# 在csv文件里写入一行文字
writer.writerow(['电影','豆瓣评分'])
writer.writerow(['闻香识女人','9.1'])
writer.writerow(['死亡诗社','9.1'])
# 写入完成后,关闭文件
csv_file.close()
复制代码
csv的读取
步骤
- 打开文件(调用open()函数)
- 创建对象(借助reader()函数)
- 读取内容(遍历reader对象)
- print内容
例如:
#导入csv模块
import csv
#用open()打开“demo.csv”文件,'r'是read读取模式,newline=''是避免出现两倍行距,encoding='utf-8'能避免编码问题导致的报错或乱码。
csv_file = open('demo.csv','r',newline='',encoding='utf-8')
#用csv.reader()函数创建一个reader对象。
reader = csv.reader(csv_file)
#遍历reader对象的每一行。打印row,读取出“demo.csv”文件里的内容。
for row in reader:
print(row)
复制代码
END
我们能够通过爬虫减少重复性工作,提高效率。而限制好爬虫的速度,对提供数据的服务器心存感谢,避免给它造成太大压力,同样也是我们该做的事。
文章只是讲述了爬虫技术的冰山一角,希望屏幕前的掘友看完之后会有所收获,这也是这篇文章存在的意义。(后续我会继续写有关爬虫的文章,希望能得到大家的认可)
最后,致伟大的开源精神!!!