天天看点

【爬虫】堆糖图片爬取

堆糖上面的图片一直是我打算爬一下的,但说着说着就忘了这么一回事-_-||

今天正好想起来,就麻利儿的把这件事给搞定了。

话不多说,先上成果。

【爬虫】堆糖图片爬取
【爬虫】堆糖图片爬取

接着说思路,代码在最后面。

最开始,我以为堆糖是那种一页一页显示图片的,就下意识地按照其他方法去爬了,结果写完才发现堆糖的结果显示是需要下拉刷新的,所以这里就有两份不同的代码。

为了以后自己查看自己的文章方便,这里就不记述静态爬取的过程了。

在这里,我以“古风”关键词为例,把爬虫的过程解释一下。

一.分析url

因为已经知道是动态拉取的,所以直接在堆糖的搜索界面上右键—查看元素—网络—XHR,停在这个页面后,就下拉鼠标,直到新一页加载出来,此时我们可以获得一条新请求。

【爬虫】堆糖图片爬取

将消息头里的请求网址粘贴到记事本。继续下拉,查看其它请求网址。

【爬虫】堆糖图片爬取

分析了一下请求网址,可以看出,每次不同的地方就在最后的两个&处。

其中第一个start=,我们可以认为是每次请求后已获得的图片的数量,而后面的那个,我也不知道是什么.....

但可以看出,每次请求,它的值都是加一的。

把第一个请求网址在浏览器打开,可以看到浏览器自动以json文件打开。

【爬虫】堆糖图片爬取

在这张截图里,我们正好可以看到三条链接,而且三条链接都是不同属性。

分析一下,这里的0,应该是指保存到的专辑封面。

avatar:阿凡达,化身。也就是用户的头像(顺便吐槽下,其实把头像趴下来也不错,其中好多头像还蛮好看的。

path:就是我们要找的图片的属性了。

既然已经找到了,我们就可以开工了。

# coding:utf-8
from urllib import request, parse
import json
import jsonpath
import requests
import os

def DuiTang(keyword,keynum):


    name = 1  #拿来记录获取的顺序,便于对下载下来的文件进行命名

    # 在当前目录下创建文件夹
    try:
        os.mkdir(r'堆糖'+keyword+'下载')
    except Exception:
        pass
    finally:
        os.chdir(r'堆糖'+keyword+'下载')

    url = 'https://www.duitang.com/napi/blog/list/by_search/?kw='
    form=parse.quote(keyword)  #动态分析中,需要把keyword转化为网页编码所需要的形式,在这里浪费了大量时间,   应该去查看一下quote()之后用的是哪一种编码
    headers = {'User-Agent':'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'}
    for page in range(1,int(keynum)):
        #Fin_url = url+form+'&type=feed&start='+str(page*24)+'.json'
        req = request.Request(url = url+form+'&type=feed&include_fields=top_comments,is_root,source_link,item,buyable,root_id,status,like_count,like_id,sender,album,reply_count,favorite_blog_id&_type=&start='+str(page*24)+'&_='+str(1540109911555+page), headers=headers)
        # 因为不清楚url中每部分的具体含义,所以中间的一部分进行保留,但按理来说,其应该没有价值
        #最后的两组不同的数字,缺一不可,只有一个,就不能刷新成功
        #一个是24*i,另一个每次都不一样,这里只取某一次刷新时候获得的值,目前还没观察到底每次刷新时值的不同有什么规律

        response = request.urlopen(req)
        # 取出json文件里的内容,返回的格式是字符串
        html = response.read()
        # 把json形式的字符串转换成python形式的Unicode字符串
        unicodestr = json.loads(html)

        # python形式的列表
        url_list = jsonpath.jsonpath(unicodestr, "$..path")
        for li in url_list:
            try:
                document = requests.get(url=li, headers=headers)
                filename = keyword+str(name) + '.jpeg'
                name = name + 1
                with open(filename, 'wb') as f:
                    f.write(document.content)
                    print('文件 ' + filename + '已下载完成!')
            except Exception:
                    print('文件 ' + filename + ' 下载失败并忽略!')
                    pass




if __name__ == '__main__':
    word = input("Input key word: ")
    num = input("Input the pages:")
DuiTang(word,num)
           

代码在上面,很容易看懂。

需要解释的几点有:

1)在输入搜索词的时候,我本以为是可以直接把keyword转换为网页编码需要的格式的,但事实是,requests.get(url....)中url里加入汉字是可以使用的,但这里的request.Request()没成功,至于原因是什么,我也不清楚.....

为了解决这个问题,代码中加入了form=parse.quote(keyword),将关键词设立为我们需要的格式,然后将一大堆String连起来,获得需要的url。

2)因为每个人需要的图片数目不一样,所以这里加入参数keynum,控制发送的请求次数,也就是获得的图片=24*keynum。

3)为了获取path,我们把json里的内容转换为python下的Unicode字符串,然后将path提取到列表里。然后逐条读取,下载。

4)堆糖上图片的主要格式是jpeg和png,这两个都无所谓,下载下来后都以jpeg的格式保存了,但GIF的就没办法保存了。当然,解决这个也能解决,但是我本来就只打算爬些静图来做手机壁纸和板绘素材,所以就懒得改代码了。

要解决gif的问题,只需要把每个链接最后提出来,看是不是“.gif"就行,是的话,写个条件语句,保存为后缀”.gif“就行。

【爬虫】堆糖图片爬取
【爬虫】堆糖图片爬取

以上就是代码的运行结果。就酱o(*▽*)q

下面再加一个写错的代码,但是也勉强能爬就是了。

# coding:utf-8
import re
import os
import sys
import random
import requests
from bs4 import BeautifulSoup

def DuiTang(keyword):
    url = 'https://b-ssl.duitang.com/uploads/'
    headers = {'User-Agent': 'Mozilla/5.0(WindowsNT6.1;rv:2.0.1)Gecko/20100101Firefox/4.0.1'}

    name = 1  #记录正在下载的是第几个文件

    # 在当前目录下创建文件夹
    try:
        os.mkdir(r'堆糖下载')
    except Exception:
        pass
    finally:
        os.chdir(r'堆糖下载')

    try:
        page = requests.get(url= 'https://www.duitang.com/search/?kw='+keyword+'&type=feed', headers=headers)
        temp = 'https://www.duitang.com/search/?kw='+keyword+'&type=feed'
        print(temp)
        print(page)

        print('找到关键词:'+keyword+'的图片,现在开始下载图片...')
        page.encoding = 'utf-8'
        soup = BeautifulSoup(page.text, 'lxml')
        info = soup.find_all('img')[0:]
        print(info)
    except Exception:
        print('可能遇到了一些问题,脚本即将退出运行!')
        sys.exit()

    for tag in info:
        height = tag['height']
        if int(height) > 150:   #因为tag[alt]的时候,在info中有大部分是没有alt=keyword的,是无效的,无法进行筛选。所以选择大家都有的height属性,其中,我们需要的资源都是height比较大的,根据观察一般而言都大于150,据此进行筛选。然后获取里面的tag[src],
            num = re.search('item.*thumb', tag['src']).group()[:-1]
            pattern = re.search('224_0.*',tag['src']).group()[:-1]  #pattern是为了区分png,jpeg
            #print(pattern)
            if pattern == '224_0.jpe':
                downloadUrl = url+num+'b.700_0.jpeg'
            else:
                downloadUrl = url+num+'b.700_0.png'
            print(downloadUrl)
            try:
                document = requests.get(url =downloadUrl ,headers=headers)
                filename = str(name)+'.jpeg'
                name = name +1
                with open(filename,'wb') as f:
                    f.write(document.content)
                    print('文件 '+filename + '已下载完成!')
            except Exception:
                print('文件 ' + filename + ' 下载失败并忽略!')
                pass
    print('目前已经爬取到了第'+str(name)+'张!')



if __name__ == '__main__':
    word = input("Input key word: ")
DuiTang(word)