堆糖上面的图片一直是我打算爬一下的,但说着说着就忘了这么一回事-_-||
今天正好想起来,就麻利儿的把这件事给搞定了。
话不多说,先上成果。
接着说思路,代码在最后面。
最开始,我以为堆糖是那种一页一页显示图片的,就下意识地按照其他方法去爬了,结果写完才发现堆糖的结果显示是需要下拉刷新的,所以这里就有两份不同的代码。
为了以后自己查看自己的文章方便,这里就不记述静态爬取的过程了。
在这里,我以“古风”关键词为例,把爬虫的过程解释一下。
一.分析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)