教女朋友爬虫(续)

867 查看

上次写的教程让女票高兴了很久,但她高兴的原因恐怕并不是学会了爬虫,这点我还是非常清醒的。这篇教程的续集我很早就想好,但碍于实在没有时间将它写出来,终于咖啡馆的网络不好用,又没带书,只好耐着性子写完它,算给自己为了爬虫痴迷那几天一个交代。我还是尽量写的通俗,以求能让更多的人看懂,比如我可爱的女票。

重要的前提,我假定你们已经看过第一篇爬虫教程,并且已经实操了,那么我们跳过环境设置,直入主题。最近开了博客(www.readlist.cn),我将教程放在那里,欢迎大家有空去看看,涨个人气。

我们的目的是爬取网页中的图片,可能你已经发现了,我们只能下载网页中的20张图片。实际上,我们用浏览器浏览该网页时,页面中的照片不止20张,随着滚动条的滚动,好像网页在不断的加载新的照片,可不可以贪心的把所有的照片下载下来呢?当然可以!

还用用chorme打开我们爬虫的目标网页,页面上右击选择显示网页源代码,我们在新打开的标签页看到了该网页的源代码。


显示源代码

有了前面的基础,我们可以轻松的找到我们需要的图片地址,<img class="pic" src="后面跟着的就是我们需要的图片地址。按command+F搜索这串代码,发现chrome在该源代码中找到20个对象,这与我们第一篇教程得到的结果是相符的。接下来,你可以试试,无论你怎么滚动滚动条,源代码不变,也就是源代码只有前20张照片的地址,那么后面的图片地址在哪里?


查找图片地址

异步加载

其实,这个网页用到了一种常见的网页加载技术——异步加载。试想一下,该网页可能有成千上万的照片,如果当浏览器访问时,全部显示显然要花很长时间才能全部加载,浪费时间浪费带宽,解决这一不合理状况的就是异步加载技术。你浏览该网页时,浏览器只会加载前20张照片,当你滚动条滑动最下面时,浏览器认为你还需要阅读更多的内容,就会向服务器提出申请说,“这个用户想看更多的,再来些“,服务器会再发20张图片的内容给浏览器,以此类推。

上述过程,你可以通过在网页空白处右键点击检查,下面的chrome工具栏中的Network标签会告诉你浏览器是怎么实现我们上面所述的异步加载。滚动滚动条至最下面,你会发现工具栏中出现了21个对象,其中20个为图片,就是页面新加载的20张图片,剩下1个是json文件。继续滚动,这里会再增加21个,以此类推,有没有很有意思。


检查元素

我们可以将json理解为一个包裹,这个包裹里放着的就是20张图片的地址,我们每滚动到页尾,就会向服务器申请一个包裹,然后浏览器拆开包裹,将里面的内容显示在网页上。可是,服务器里存放了无数个包裹,它怎么知道我们需要哪个包裹,有过网购经验的MM肯定可以想到,每个包裹都有一个唯一的身份证,我们称之为“快递单号”。这样,一切就可以解释,浏览器将我们需要的包裹号发送给服务器,服务器然后如实将该包裹发送回来。我们可以仔细看下json,选中json文件,在右侧的工具栏中选择Preview标签,里面的html中放着的是有新增加图片地址的源代码,last_key是下一段代码的包裹号,所以不难推测,源代码中有一个last_key指向本包裹。


JsonPreview

我们重新捋一下思路,首先爬取源代码中的图片,然后向服务器发送包裹号,获取包裹,解析其中的内容,然后再发送包裹号,再解析包裹中的内容,以此类推。接下来,终于可以上代码了。

#-*-coding:utf8-*-
import re
import requests
import json

url = 'http://www.qdaily.com/categories/17'
url_more = 'http://www.qdaily.com/categories/categorymore/17/'

html = requests.get(url).text
last_key = re.search('lastKey="(.*?)"',html,re.S).group(1)

for i in range(3):
    pic_url = re.findall('"pic" src="(.*?)"',html,re.S)
    j = 0
    for each in pic_url:
        url = 'http://www.qdaily.com' + each
        print('now downloading:' + url)
        pic = requests.get(url)
        fp = open('pic//' + str(i) +str(j) + '.jpg','wb')
        fp.write(pic.content)
        fp.close()
        j += 1

    url = url_more + last_key + 'json'
    data = requests.get(url)
    html = json.loads(data.text)[u'data'][u'html']
    last_key = str(json.loads(data.text)[u'data'][u'last_key'])

代码解析

#-*-coding:utf8-*-的意思上字符编码是utf-8

import re import requests import json的意思上导入rerequestsjson库,告诉电脑,我们下面要用这三个库中的程序了。

前面我们分析得到下一个包裹的包裹号,但是到底怎么获取它呢?可以在检查工具栏中,点击Headers,查看到json的地址。


显示下一个Json地址

接下来定义两个变量(就是存储容器)存放两个地址,我们接下来需要用到这两个地址。

url = 'http://www.qdaily.com/categories/17'
url_more = 'http://www.qdaily.com/categories/categorymore/17/'

然后获取url中地址的源代码,并且在源代码中查找last_key

html = requests.get(url).text
last_key = re.search('lastKey="(.*?)"',html,re.S).group(1)

下面的代码是我们这篇教程的核心,用循环来不断向获取新的包裹,拆开它,通过里面的图片地址下载图片,我们这里只循环3次,下载60张图片,当然你想循环多少次就改一下数字即可,相信你可以办到。for i in range(3):就是循环语句,表示接下来的语句要执行三次。
逻辑是非常清楚的:

  • 从源代码中获取图片地址
pic_url = re.findall('"pic" src="(.*?)"',html,re.S)
  • 将图片下载到指定文件夹
j = 0
for each in pic_url:
    url = 'http://www.qdaily.com' + each
    print('now downloading:' + url)
    pic = requests.get(url)
    fp = open('pic//' + str(i) +str(j) + '.jpg','wb')
    fp.write(pic.content)
    fp.close()
    j += 1
  • 获取下一批图片的源代码
url = url_more + last_key + 'json'
data = requests.get(url)
html = json.loads(data.text)[u'data'][u'html']
  • 获取下一批图片源代码中包含的last_key
last_key = str(json.loads(data.text)[u'data'][u'last_key'])

我没有详细解释代码的具体含义,因为接下来我要去逛书店,没有耐心了。如果你有疑问,欢迎到我的博客留言提问,有问必答。

执行程序

将上面到代码保存为picdownloader2.py,置于根目录下,并且新建文件夹pic。打开终端,写下如下代码,回车即可

python picdownloader2.py

终于完成了“教女朋友爬虫”的续集,关于爬虫的教程先告一段落。上次的教程让女票(领导)甚是开心,顺便提出了新要求,要我继续写教程,因为她还要学很多,学markdown,学印象笔记,学Hexo博客。。。再一次硬广,欢迎关注我的博客微博

今天是女票麻麻的生日,未来丈母娘生日快乐。


欢迎关注公众号“百杏果阁”