来,让我们写一个网络爬虫,下载页面上所有的照片吧!

什么是网络爬虫?

网络爬虫是一种非常有意思的程序。偌大的Internet,就像是一只蜘蛛织成的大网:一个个超级链接就是蛛丝,将无数页面连接起来,而网络爬虫,则会沿着一根根蛛丝,爬遍每一个节点……


网络爬虫能干嘛?

蜘蛛在网上爬来爬去,当然不是为了健身。它会在网上寻觅猎物,捕捉它们,并拖回自己的窝里。

举一个例子:某天某日的清晨,老板突然让你将雪球网上所有的A股行情信息全部保存到一个Excel文件里,以便他浏览。你点开了网址雪球(A股行情),惊喜地发现有三十四页,一条一条复制,显然又累又笨,但是老板要看,你又不得不从,惆怅啊……

很多时候,我们会需要做这样的工作:将某一类型的文档全部下载保存下来,然后进行分析。一个一个点击鼠标,显然不现实。那么,就做一个网络爬虫吧,自动化地把需要的东西保存下来。

网络爬虫是怎么干的?

一个简单的网络爬虫的逻辑并不复杂,就是模仿人类浏览网页的动作:输入网址,进入页面,浏览网页,点击链接,进入下一页面……周而复始。

当然,嘴上说是这样,还得要将步骤细化一下。所以先来看看浏览网页的时候,我们做了些什么。

当我们浏览网页的时候,浏览器在做些什么?

蒂姆·伯纳斯-李在发明互联网的时候,为了解决页面之间相互连接的问题,让网络浏览更加方便,发明了超文本标记语言HTML。它是一个文本文档,但是文档中会有一些特殊的标记——标签,它可以标记出哪些位置的信息是标题,哪些位置的信息是文本,哪些地方是图像,哪些地方是超级链接。它就长这样。
<html>
<body>

<h1>My First Heading</h1>

<p>My first paragraph.</p>

</body>
</html>
尖括号中间的就是标签,里面的字就是标签的名字,带斜杠的表示标记结束了。比如,<p>的意思是段落, </p>的意思就是这一段结束了。
浏览器可以根据标签,将网页依照代码的意思表现出来,用点儿术语的话,就是浏览器是一个HTML的解释器。当我们浏览网页的时候,浏览器会先将网页的HTML文件,以及所需要的其它元素都下载到你的电脑里,然后根据HTML文件的内容,将网页展现在你的面前。
所以网络爬虫也需要干这件事情:下载HTML文件,然后分析它,根据分析的结果将需要的文本或者图像什么的下载下来,找到超级链接,继续……

让我们写一个网络爬虫,下载页面上所有的照片吧!

知乎上有很多提问,下面会有很多照片,比如这个 怎么用手机拍出精彩的照片?,图片都挺好看的,我想全部下载下来,怎么办呢?打开页面以后,右键点击页面,选择【查看源文件】,就可以看到HTML文件的内容了。简单分析一下,会发现照片其实都在这样的标签下面:
<img src="https://pic3.zhimg.com/efb71614cacf505f92bd59509919a2d2_b.jpg" data-rawwidth="720" data-rawheight="720" class="origin_image zh-lightbox-thumb" width="720" data-original="https://pic3.zhimg.com/efb71614cacf505f92bd59509919a2d2_r.jpg">
所以,现在就可以制定出我们的策略了:打开页面,分析它的HTML源文件,找出所有img标签,将里面的图片都保存下来,over!

用Python实现你的第一个网络爬虫

Python语言语法简单而灵活,实用的库(别人写好,可以直接实用的代码)非常多,很适合写这种小爬虫。所以我们用Python来写一个网络爬虫。可以点击 这里下载Python环境的安装包,一直下一步就可以安装好了。具体学习Python可以看 这本书,或者 这本书。这里我们的代码并不复杂,需要的知识点不多,我会一点一点讲解的。当然你可能需要一点点程序设计的知识。

在一个文本编辑器里面输入以下代码:
#-*-coding:utf-8-*-
import urllib

url = "https://www.zhihu.com/question/20922273"
page = urllib.urlopen(url)
print page.read()
找一个文件夹,保存为example1.py。然后点击右键选择【EDIT with IDLE】。

打开以后按F5键。应该会出现这样的窗口。


是不是打印出了原来页面的HTML文件的内容?

下面来解释一下这一段代码的意思。
#-*-coding:utf-8-*- 的意思是这一段Python程序的编码格式是UTF-8。理解这一点需要一些编码的知识,这里暂时不用管它。一般每一个Python程序第一句话都是这个。
import urllib 的意思是导入urllib库,python自带的一个网络库,我们可以用它来实现访问网页,下载文件等功能。当然还有很多功能更高级的库,比如urllib2或者requests等,这里我们挑一个简单的先。
url = "https://www.zhihu.com/question/20922273" 这句话的意思是定义url为字符串"https://www.zhihu.com/question/20922273" ,也就是我们要访问的网页的网址,可以看出,网址实际上是一个字符串变量。
page = urllib.urlopen(url) 这句话的意思是使用urllib库里面的urlopen函数,打开网址url,并把打开的这个对象命名为page(打开了一个东西,虽然我们不知道它是什么,但是先给它起个名字吧!)。
page.read() 的意思就是读取page这个对象的内容。print page.read()的意思自然就是打印这个内容了。

于是,我们就完成了第一步,打开页面。下面,我们来写程序分析打开的页面。

写程序,找出所有的图片地址

在IDLE里面,把前面写的代码改一改。
#-*-coding:utf-8-*-
import urllib
from sgmllib import SGMLParser

class ZhihuParser(SGMLParser):
    imgList = []
    def reset(self):
        SGMLParser.reset(self)  #初始化
        self.imgList = []
    def start_img(self, attrs):
        imgUrl = [v for k, v in attrs if k=='data-original']
        if imgUrl:
            self.imgList.append(imgUrl[0])
            imgUrl = ""
   
url = "https://www.zhihu.com/question/20922273"
page = urllib.urlopen(url)
parser = ZhihuParser()
parser.feed(page.read())
print parser.imgList

点击F5运行,看看结果。


果然把所有的图片地址都打印出来了。

下面来解释一下代码。
from sgmllib import SGMLParser 这一句的意思是从sgmllib文件里面导入SGMLParser这个包。这个包是Python自带的一个解析HTML的库。
class ZhihuParser(SGMLParser):是定义一个类ZhihuParser,它继承SGMLParser这个类。我们对它进行一些修改。
imgList = [] 声明了一个列表,我们用它来保存所有图片的地址。
def reset(self): 这个函数是ZhihuParser类的初始化函数。每一次生成ZhihuParser类的对象的时候都会调用这个函数。我们让这个函数先初始化SGMLParser.reset(self),然后把列表制空self.imgList = []。
def start_img(self, attrs): 这个函数是解析img标签的函数。当SGMLParser遇到一个img标签的时候,就会调用这个函数。比如我们要解析<a>标签,那就自己定义一个函数start_a,解析<head>标签,就定义一个函数start_head,函数的内容是我们需要的动作。对于</something>这样的结束标签,对应的函数是end_something(self)。start_something函数的参数是(self,attrs)。self自然就是类本身,attrs是SGMLParser解析出来的标签参数,比如知乎img的代码:
<img src="//zhstatic.zhihu.com/assets/zhihu/ztext/whitedot.jpg" data-rawwidth="720" data-rawheight="720" class="origin_image zh-lightbox-thumb lazy" width="720" data-original="https://pic3.zhimg.com/efb71614cacf505f92bd59509919a2d2_r.jpg" data-actualsrc="https://pic3.zhimg.com/efb71614cacf505f92bd59509919a2d2_b.jpg">
里面的src, data-rawwidth等等,都是标签的参数,它以字典的形式传入函数中,即一系列参数名:参数的二元对。这里我们需要提取出地址,即data-original的内容,imgUrl = [v for k, v in attrs if k=='data-original'],意思是遍历attrs,如果字典的名为data-original,就保存下来。
如果imgUrl保存成功,我们就把它导入到列表里,self.imgList.append(imgUrl[0])。然后将临时变量imgUrl制空(这句话其实不必要)。
parser = ZhihuParser()定义了一个ZhihuParser对象,parser.feed(page.read())将页面的内容传入parser,进行解析。print parser.imgList将所有的图片地址打印出来。

下面我们继续修改代码,把所有图片保存下来。
在上面的基础上加入如下代码
cnt = 1
for url in parser.imgList:
    f = open("%d.jpg"%cnt,'wb')
    img = urllib.urlopen(url)
    f.write(img.read())
    f.close()
    print "%d was done!"%cnt
    cnt += 1
cnt = 1是定义一个计数器,记录我们下载图片的个数。
for url in parser.imgList:遍历图片地址列表。
f = open("%d.jpg"%cnt,'wb')打开一个文件。我们将下载下来的图片写入这个文件中。
img = urllib.urlopen(url)打开图片地址。
f.write(img.read())将图片写入文件,然后关闭文件。

运行程序,不一会就看见我们将所有的网页上的图片都下载了下来。


于是乎,我们就完成了一个最简单的网络爬虫。

后记

事实上,真正的通用网络爬虫是很难写的,要考虑的因素很多。比如怎样规避网站的反爬虫机制(有的网站可不愿意你下载文件,占用带宽),怎样避免爬虫陷阱(比如有两个页面有相互连接的超级链接,于是爬虫就会一直在这两个页面间爬来爬去,不去其他页面),怎样大规模快速地爬(分布式爬虫),怎样解析JavaScript等等等等。事实上,网络爬虫是搜索引擎的核心技术之一,google,百度等搜索引擎公司每天都不停地在网络上爬取页面,解析,并排序,给我们提供搜索服务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值