什么是爬虫?
什么是爬虫?
到底什么是爬虫呢?
爬虫,从本质上来说,就是利用程序在网上拿到对我们有价值的数据。
爬虫能做很多事,能做商业分析,也能做生活助手,比如:分析北京近两年二手房成交均价是多少?深圳的Python工程师平均薪资是多少?北京哪家餐厅粤菜最好吃?等等。
这是个人利用爬虫所做到的事情,而公司,同样可以利用爬虫来实现巨大的商业价值。比如你所熟悉的搜索引擎——百度和谷歌,它们的核心技术之一也是爬虫,而且是超级爬虫。
以百度为例,你在搜索的时候仔细看,会发现每个搜索结果下面都有一个百度快照。
点击百度快照,你会发现网址的开头有baidu这个词,也就是说这个网页属于百度。
这是因为,百度这家公司会源源不断地把千千万万个网站爬取下来,存储在自己的服务器上。你在百度搜索的本质就是在它的服务器上搜索信息,你搜索到的结果是一些超链接,在超链接跳转之后你就可以访问其它网站了。
爬虫还让这些搜索巨头有机会朝着人工智能的未来迈进,因为人工智能的发展离不开海量的数据。而每天使用这些搜索网站的用户都是数以亿计的,产生的数据自然也是难以计量的。
从搜索巨头到人工智能巨头,这是一条波澜壮阔的路。而我们应该看到,事情的源头,却是我们今日所书写的“爬虫”。
爬虫的工作原理
第0步:获取数据。爬虫程序会根据我们提供的网址,向服务器发起请求,然后返回数据。
第1步:解析数据。爬虫程序会把服务器返回的数据解析成我们能读懂的格式。
第2步:提取数据。爬虫程序再从中提取出我们需要的数据。
第3步:储存数据。爬虫程序把这些有用的数据保存起来,便于你日后的使用和分析。
这就是爬虫的工作原理啦,无论之后的学习内容怎样变化,其核心都是爬虫原理。
体验爬虫
在进行爬虫时,我们会用到一个强大的库-requests库来获取数据
安装方法:在Mac电脑里打开终端软件(terminal),输入pip3 install requests,然后点击enter即可;Windows电脑里叫命令提示符(cmd),输入pip install requests 即可。
requests库可以帮我们下载网页源代码、文本、图片,甚至是音频。其实,“下载”本质上是向服务器发送请求并得到响应。
先来看requests.get()方法。
requests.get()
requests.get()的具体用法如下,请仔细阅读注释噢:
# 引入requests库
import requests
# requests.get是在调用requests库中的get()方法,它向服务器发送了一个请求,括号里的参数是你需要的数据所在的网址,然后服务器对请求作出了响应。
# 我们把这个响应返回的结果赋值给变量res
res = requests.get('URL')
第2行代码,是引用requests库。
第6行代码,requests.get()发送了请求,然后得到了服务器的响应。服务器返回的结果是个Response对象,现在存储到了我们定义的变量res中。
URL的功能从它的常用别名“网址”以及它所在的位置“地址栏”联想一下就很好理解了:它指示了一个网页在网络上的地址,就像我们住的房子在地球上的详细到某某街道某某号的具体地址。
Response对象的常用属性
Python是一门面向对象编程的语言,而在爬虫中,理解数据是什么对象是非常、特别、以及极其重要的一件事。因为只有知道了数据是什么对象,我们才知道对象有什么属性和方法可供我们操作。
所以,我们现在来打印看看刚刚用requests.get()获取到的数据是什么,请点击运行下面的程序:
import requests
res = requests.get('https://res.pandateacher.com/2018-12-18-10-43-07.png')
# 打印变量res的数据类型
print(type(res))
-----------------------------
<class 'requests.models.Response'>
终端显示:
这代表着:res是一个对象,属于requests.models.Response类。好,既然已经知道res是一个Response对象了,我们也就可以去了解它的相应属性和方法了。
我们主要讲Response对象常用的四个属性:
response.status_code:检查请求是否成功
response.content:把response对象转换为二进制对象
response.text:把response对象转为字符串数据
response.encoding:定义response对象的编码
response.status_code是一个很常用的属性,在我们之后的爬虫代码中也将多次出现。
接着的属性是response.content,它能把Response对象的内容以二进制数据的形式返回,适用于图片、音频、视频的下载,看个例子你就懂了。
假如我们想下载这张图片,它的URL是:https://img0.baidu.com/it/u=2921493010,3508718203&fm=253&fmt=auto&app=138&f=JPEG?w=505&h=500
那么代码可以这样写:
# 引入requests库
import requests
# 发出请求,并把返回的结果放在变量res中
res = requests.get('https://img0.baidu.com/it/u=2921493010,3508718203&fm=253&fmt=auto&app=138&f=JPEG?w=505&h=500')
# 把Reponse对象的内容以二进制数据的形式返回
pic = res.content
# 新建了一个文件picture.jpg,这里的文件没加路径,它会被保存在程序运行的当前目录下。
# 图片内容需要以二进制wb读写。你在学习open()函数时接触过它。
photo = open('picture.jpg','wb')
# 获取pic的二进制内容
photo.write(pic)
# 关闭文件
photo.close()
这样,我们的图片就下载成功啦~你也可以在本地运行这个程序。
讲完了response.content,继续看response.text,这个属性可以把Response对象的内容以字符串的形式返回,适用于文字、网页源代码的下载。
举个例子,我们还是来下载小说《三国演义》的第一回。
代码如下:
# 引用requests库
import requests
# 下载《三国演义》第一回,我们得到一个对象,它被命名为res
res = requests.get('https://localprod.pandateacher.com/python-manuscript/crawler-html/sanguo.md')
# 把Response对象的内容以字符串的形式返回
novel=res.text
# 现在,可以打印小说了,但考虑到整章太长,只输出800字看看就好。在关于列表的知识那里,你学过[:800]的用法。
print(novel[:800])
--------------------------
## 三国演义
作者:罗贯中
### 第一回 宴桃园豪杰三结义 斩黄巾英雄首立功
> 滚滚长江东逝水,浪花淘尽英雄。
> 是非成败转头空。
> 青山依旧在,几度夕阳红。
> 白发渔樵江渚上,惯看秋月春风。
> 一壶浊酒喜相逢。
> 古今多少事,都付笑谈中。
> ——调寄《临江仙》
话说天下大势,分久必合,合久必分。周末七国分争,并入于秦。及秦灭之后,楚、汉分争,又并入于汉。汉朝自高祖斩白蛇而起义,一统天下,后来光武中兴,传至献帝,遂分为三国。推其致乱之由,殆始于桓、灵二帝。桓帝禁锢善类,崇信宦官。及桓帝崩,灵帝即位,大将军窦武、太傅陈蕃共相辅佐。时有宦官曹节等弄权,窦武、陈蕃谋诛之,机事不密,反为所害,中涓自此愈横。
建宁二年四月望日,帝御温德殿。方升座,殿角狂风骤起。åª见一条大青蛇,从梁上飞将下来,蟠于椅上。帝惊倒,左右急救入宫,百官俱奔避。须臾,蛇不见了。忽然大雷大雨,加以冰雹,落到半夜方止,坏却房屋无数。建宁四年二月,洛阳地震;又海水泛溢,沿海居民,尽被大浪卷入海中。光和元年,雌鸡化雄。六月朔,黑气十余丈,飞入温德殿中。秋七月,有虹现于玉堂;五原山岸,尽皆崩裂。种种不祥,非止一端。帝下诏问群臣以灾异之由,议郎蔡邕上疏,以为蜺堕鸡化,乃妇寺干政之所致,言颇切直。帝览奏叹息,因起更衣。曹节在后窃视,悉宣告左右;遂以他事陷邕于罪,放归田里。后张让、赵忠、封谞、段珪、曹节、侯览、蹇硕、程旷、夏恽、郭胜十人朋比为奸,号为“十常侍”。帝尊信张让,呼为“阿父”。朝政日非,以致天下人心思乱,盗贼蜂起。
时巨鹿郡有兄弟三人,一名张角,一名张宝,一名张梁。那张角本是个不第秀才,因入山采药,遇一老人,碧眼童颜,手执藜杖,唤角至一洞中,以天书三卷授之,曰:“此名《太平要术》,汝得之,当代天宣化,普救世人;若萌异心,必获恶
之后,我们就可以用通过读写文件把小说保存到本地了。
接下来,我们看最后一个属性:response.encoding,它能帮我们定义Response对象的编码。
我们仍然以三国演义的小说来做示范,请看下面的代码(第7行为新增代码),然后点击运行:
# 引用requests库
import requests
# 下载《三国演义》第一回,我们得到一个对象,它被命名为res
res = requests.get('https://localprod.pandateacher.com/python-manuscript/crawler-html/sanguo.md')
# 定义Response对象的编码为gbk
res.encoding='gbk'
# 把Response对象的内容以字符串的形式返回
novel=res.text
# 打印小说的前800个字
print(novel[:800])
-----------------
## 涓夊浗婕斾箟
浣滆�咃細缃楄疮涓�
### 绗�涓�鍥� 瀹存�冨洯璞�鏉颁笁缁撲箟 鏂╅粍宸捐嫳闆勯�栫珛鍔�
> 婊氭粴闀挎睙涓滈�濇按锛屾氮鑺辨窐灏借嫳闆勩��
> 鏄�闈炴垚璐ヨ浆澶寸┖銆�
> 闈掑北渚濇棫鍦�锛屽嚑搴﹀�曢槼绾�銆傘��銆�
> 鐧藉彂娓旀ǖ姹熸笟涓婏紝鎯�鐪嬬�嬫湀鏄ラ�庛��
> 涓�澹舵祳閰掑枩鐩搁��銆�
> 鍙や粖澶氬皯浜嬶紝閮戒粯绗戣皥涓�銆�
> 銆�銆�銆�銆�銆�銆�銆�銆�銆�銆�鈥斺�旇皟瀵勩�婁复姹熶粰銆�
璇濊�村ぉ涓嬪ぇ鍔匡紝鍒嗕箙蹇呭悎锛屽悎涔呭繀鍒嗐�傚懆鏈�涓冨浗鍒嗕簤锛屽苟鍏ヤ簬绉︺�傚強绉︾伃涔嬪悗锛屾�氥�佹眽鍒嗕簤锛屽張骞跺叆浜庢眽銆傛眽鏈濊嚜楂樼�栨柀鐧借泧鑰岃捣涔夛紝涓�缁熷ぉ涓嬶紝鍚庢潵鍏夋�︿腑鍏达紝浼犺嚦鐚�甯濓紝閬傚垎涓轰笁鍥姐�傛帹鍏惰嚧涔变箣鐢憋紝娈嗗�嬩簬妗撱�佺伒浜屽笣銆傛�撳笣绂侀敘鍠勭被锛屽磭淇″�﹀畼銆傚強妗撳笣宕╋紝鐏靛笣鍗充綅锛屽ぇ灏嗗啗绐︽�︺�佸お鍌呴檲钑冨叡鐩歌緟浣愩�傛椂鏈夊�﹀畼鏇硅妭绛夊紕鏉冿紝绐︽�︺�侀檲钑冭皨璇涗箣锛屾満浜嬩笉瀵嗭紝鍙嶄负鎵�瀹筹紝涓�娑撹嚜姝ゆ剤妯�銆�
寤哄畞浜屽勾鍥涙湀鏈涙棩锛屽笣寰℃俯寰锋�裤�傛柟鍗囧骇锛屾�胯�掔媯椋庨�よ捣銆傚彧瑙佷竴鏉″ぇ闈掕泧锛屼粠姊佷笂椋炲皢涓嬫潵锛岃煚浜庢�呬笂銆傚笣鎯婂�掞紝宸﹀彸鎬ユ晳鍏ュ��锛岀櫨瀹樹勘濂旈伩銆傞』鑷撅紝铔囦笉瑙佷簡銆傚拷鐒跺ぇ闆峰ぇ闆�锛屽姞浠ュ啺闆癸紝钀藉埌鍗婂�滄柟姝�锛屽潖鍗存埧灞嬫棤鏁般�傚缓瀹佸洓骞翠簩鏈堬紝娲涢槼鍦伴渿锛涘張娴锋按娉涙孩锛屾部娴峰眳姘戯紝ç借��澶ф氮鍗峰叆娴蜂腑銆傚厜鍜屽厓骞达紝闆岄浮鍖栭泟銆傚叚鏈堟湐锛岄粦姘斿崄浣欎笀锛岄�炲叆娓╁痉娈夸腑銆傜�嬩竷鏈堬紝鏈夎櫣鐜颁簬鐜夊爞锛涗簲鍘熷北宀革紝灏界殕宕╄�
诶,为什么会出现一段乱码呢?
事情是这样的:首先,目标数据本身有它的编码类型,这个《三国演义》URL中的数据类型是’utf-8。获取目标数据后要知道相应的编码类型才能正确解码。
编解码要共享同一种编码类型,就像你给我传纸条用的编码方式如果是“拼音”,我收到后就要拼“拼音”来理解语意——若我以为是“英语”,去查英语字典,那必然看不懂你说了什么。
那么,回看之前的【练习:下载文章《三国演义》第一回并打印】,终端是怎么“知道”编码类型并将文字正确打印显示的呢?
事实上,requests.get()发送请求后,我们得到一个Response对象,其中,requests模块会对数据的编码类型做出自己的判断。
但是,第7行的代码不管原来的判断是什么,直接定义了Response对象的编码类型是’gbk’。这样一来,跟数据本身的编码’utf-8’就不一致了,所以打印出来,就是一堆乱码。就像我虽然看出这是个“拼音”字条,但有人一定要我按照“英文”解释一下,不乱码还能怎样呢,我太难了。
如果我们把第7行代码换成utf-8,打印出来就没问题了。
这只是个示范,是为了让大家理解res.encoding的意义,也就是它能定义Response对象的编码类型。
那在真实的情况中,我们该在什么时候用res.encoding呢?
首先,目标数据本身的编码方式是未知的。用requests.get()发送请求后,我们会取得一个Response对象,其中,requests库会对数据的编码类型做出自己的判断。但是!这个判断有可能准确,也可能不准确。比如你发给我一张“法语”字条,我看不出来是什么语言,猜测可能是“俄语”,“德语”等。
如果它判断准确的话,我们打印出来的response.text的内容就是正常的、没有乱码的,那就用不到res.encoding;如果判断不准确,就会出现一堆乱码,那我们就可以去查看目标数据的编码,然后再用res.encoding把编码定义成和目标数据一致的类型即可。
总的来说,就是遇上文本的乱码问题,才考虑用res.encoding。
爬取音频
#引入reques库
import requests
#请求数据,并返回给目标对象
res=requests.get('https://static.pandateacher.com/Over%20The%20Rainbow.mp3')
#进行返回数据的解析,解析为二进制文件
pic=res.content
#新建一个文件,用来存储二进制文件图片
a=open('音乐.mp3','wb')
#进行读写进入文件
a.write(pic)
a.close()
以上的爬取案例均为最简单的,未经过加密的,可以直接进行爬取。其他更复杂的就需要自己更深层次的学习了。