只要能看懂python代码,并且不懂的地方能查google,就可以试一下了。

本文运行环境:

  • Windows 10 家庭中文版
  • Python3.7 + Spyder (如果是Anaconda而非单个安装,就爽多了,虽然Anaconda有很多bug,但是用来做数据分析特别爽)

惯例,先贴上所有代码,再一一解释。代码直接复制到py文件里运行就可以:

# -*- coding: utf-8 -*-

import os
import requests
import json
import time

headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0'
}

proxies_v2ray = {
'http':'socks5://127.0.0.1:10808',
'https':'socks5://127.0.0.1:10808',
}

proxies_ssr={
'http':'socks5://127.0.0.1:10807',
'https':'socks5://127.0.0.1:10807',
}

tags=input('Input:请输入您的tags(默认为空则爬取整个konachan站):')
pages_num=int(input('Input:请输入您爬取的页数,默认为所有页数:'))

if tags==None:
final_folder = './Konachan/'
else:
final_folder = './Konachan/'+tags+'/'

os.makedirs(final_folder, exist_ok=True)

if pages_num<11486:
pages_num=pages_num+1
else:
pages_num=11487

for n in range(1,pages_num):
url_com='https://konachan.com/post.json?page='+str(n)+'&tags='+tags
url_net='https://konachan.com/post.json?page='+str(n)+'&tags='+tags
req=requests.get(url_net,headers=headers,proxies=proxies_v2ray).content
jsonlist=json.loads(req)
if len(jsonlist)==0:
break

for i in range(len(jsonlist)):
id_number=jsonlist[i]['id']
file_suffix = os.path.splitext(jsonlist[i]['file_url'])[1]
if os.access(final_folder+'%s'%id_number+file_suffix, os.F_OK):
print('图片{}存在,跳过'.format(id_number))
else:
r=requests.get(jsonlist[i]['file_url'],headers=headers,proxies=proxies_v2ray)
print('下载{}图片'.format(id_number))
with open(final_folder+'%s'%id_number+file_suffix, 'wb') as f:
f.write(r.content)

print('进入休眠3s')
time.sleep(3)

print('Finish!')

打开spyder,抬头会自动设置为 coding:utf-8,如果你用的是python3,那么编码问题就完美解决了。此处Python2用户请节哀。

我接触的爬虫来看,有三种思路的爬虫,每个网站都不太一样,

  1. 直接爬取网页,就是网站上显示的啥就爬啥。这种情况可以用于大部分的网站,缺点除了有些网站爬不下来(比如动漫之家等漫画网站,这种的需要selenium+phantomjs,我自己不会写代码但找到下载器看了下),还有就是麻烦(看不懂网页源代码,也就是html格式)
  2. 爬取json格式,此次爬取konachan就是用的这个,缺点是绝大多数网站不希望被爬虫,一般不会开放json(很麻烦才能找到)。Konachan特别友好,它提供了这种方式的下载(也就是开放了API):http://konachan.com/help/api,作为python教程特别爽
  3. 爬取xml格式,这个其实我也不会,但是看到konachan的help里有在这种格式,我觉得也可以用来爬虫(虽然直接打开来看我懵了,不过和RSS很像,RSSHub或许可以开始工作了XD)

爬虫要设置请求头,就是python“假装”为某一标识来爬取,设置成什么网上有很多,比如手机或者chrome/firefox。

因为国内网络访问Konachan很很恨很恨很tm慢(被墙了),无论是18+的.com还是全年龄向的.net(也没全年龄到哪里去hhhhh),所以我用了ssr和v2ray做代理辅助,下面以v2rayN为例show一下我的设置:

image-20200329183218358

我设置成socks5代理,端口为10808(这个数字可以改的,当时为了设置telegeam我才用的这个),反映在python中的代码是:

proxies_v2ray = {
'http':'socks5://127.0.0.1:10808',
'https':'socks5://127.0.0.1:10808',
}

哦对了,记得在pac文件里加上 ||koanchan的字样。ssr同理。

下面是在电脑上要存放的文件夹地址:

tags=input('Input:请输入您的tags(默认为空则爬取整个konachan站):')

if tags==None:
final_folder = './Konachan/'
else:
final_folder = './Konachan/'+tags+'/'

os.makedirs(final_folder, exist_ok=True)

tags是指在konachan中的图片tags,也即在网站中的搜索框里输入的东西,比如我要爬取朝田诗乃的图片,tags就输入为“shinon_(sao)”就可以了。如果没有输入就直接enter键跳过……

os.makedirs比os.mkdir的用法更为高级,不需要考虑“啊提示konachan文件夹没有创建诶”这种rz问题……exist_ok参数标明只有在目录不存在时创建目录,目录已存在时不会抛出异常。

接下来是一个大的for循环,在循环之前,我们先来解析一下Konachan。konachan的网址中有俩参数:pages,tags( 例如:http://konachan.com/post?page=5&tags=misaka_mikoto )

screenshot-konachan.com-2020.03.29-20_48_56

但是requests.get获取本页面的json之后,会显示本页面所有图片的信息:http://konachan.com/post.json?page=5&tags=misaka_mikoto

json具体格式是啥我也不知道,但是用python的json包可以解析json为字典dict格式:jsonlist=json.loads(req)。随便一张图片的信息我们提取一下:

{'id': 194190,
'tags': '2girls black_hair brown_hair kongou_mitsuko misaka_mikoto pakupaku_choppu ponytail to_aru_kagaku_no_railgun to_aru_majutsu_no_index wink',
'created_at': 1420312375,
'creator_id': 87866,
'author': 'Freenight',
'change': 817864,
'source': 'http://i1.pixiv.net/img99/img/pinkygoldr/35427686.jpg',
'score': 62,
'md5': '6f63d46dcc45864751dd8ed41e99e63c',
'file_size': 907578,
'file_url': 'https://konachan.com/image/6f63d46dcc45864751dd8ed41e99e63c/Konachan.com%20-%20194190%202girls%20black_hair%20brown_hair%20kongou_mitsuko%20misaka_mikoto%20pakupaku_choppu%20ponytail%20to_aru_kagaku_no_railgun%20to_aru_majutsu_no_index%20wink.jpg',
'is_shown_in_index': True,
'preview_url': 'https://konachan.com/data/preview/6f/63/6f63d46dcc45864751dd8ed41e99e63c.jpg',
'preview_width': 150,
'preview_height': 94,
'actual_preview_width': 300,
'actual_preview_height': 188,
'sample_url': 'https://konachan.com/image/6f63d46dcc45864751dd8ed41e99e63c/Konachan.com%20-%20194190%202girls%20black_hair%20brown_hair%20kongou_mitsuko%20misaka_mikoto%20pakupaku_choppu%20ponytail%20to_aru_kagaku_no_railgun%20to_aru_majutsu_no_index%20wink.jpg',
'sample_width': 1280,
'sample_height': 800,
'sample_file_size': 0,
'jpeg_url': 'https://konachan.com/image/6f63d46dcc45864751dd8ed41e99e63c/Konachan.com%20-%20194190%202girls%20black_hair%20brown_hair%20kongou_mitsuko%20misaka_mikoto%20pakupaku_choppu%20ponytail%20to_aru_kagaku_no_railgun%20to_aru_majutsu_no_index%20wink.jpg',
'jpeg_width': 1280,
'jpeg_height': 800,
'jpeg_file_size': 0,
'rating': 's',
'has_children': False,
'parent_id': None,
'status': 'active',
'width': 1280,
'height': 800,
'is_held': False,
'frames_pending_string': '',
'frames_pending': [],
'frames_string': '',
'frames': []}

很显然,id就是图片的id号(方便整理),tags就是图片的标签,source就是图片的来源,file_url就是图片的链接,这是我们想要提取的最主要信息。

剩下的工作就是遍历所有的图片链接,并下载下来,命名为id号,最后的后缀与图片链接的后缀保持相同即可。并且由于辣鸡代理(并不是辣鸡代理而是辣鸡墙),日常断开,最好还是检验一下如果待爬取图片已下载在本地(命名格式相同),那么就不用爬了直接跳过。

最后设置停止即在某一个pages下,json内容为空,则结束爬虫。至于大循环下设置range(1,11487),是因为整个konachan站下的页面数为11486页(截止到2020.03.28)。

给爷爬完之后记得给爷说一下Finish,整个代码就完成了。


说说感想吧。

不得不说konachan作为一个Anime Wallpaper站,真的太太太瑟琴了(wdnmd我裤子动了我不干了),

作为一个python的初学者,可以作为爬虫项目练习,比爬取什么JD、Taobao之类的友好多了。

唯一不足的是需要科学上网,否则速度不堪忍受。

其实我学习python应该是走Data Science方向的,是那种一上来就三行代码万年不变:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

爬虫只是满足我日常的满足感罢了,也正因如此,我的2T大移动硬盘都快满了emmm

image-20200328194829116

看来需要再破费买一个WD硬盘了……


课后题环节!!

请仿照上述代码,爬取wallhaven.cc站的toplist高清图片:

import os
import requests
import json
import time

#请求头
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0'
}

proxies_v2ray = {
'http':'socks5://127.0.0.1:10808',
'https':'socks5://127.0.0.1:10808',
}

proxies_ssr={
'http':'socks5://127.0.0.1:10807',
'https':'socks5://127.0.0.1:10807',
}

api_key='此处涉及隐私,故略去'

final_folder = './wallhaven/'
os.makedirs(final_folder, exist_ok=True)

url_original='https://wallhaven.cc/api/v1/search?sorting=toplist'+'&apikey='+api_key
req_original=requests.get(url_original,headers=headers,proxies=proxies_v2ray).content
jsonlist_original=json.loads(req_original)
jsonlist_original_meta=jsonlist_original['meta']
pages_num=jsonlist_original_meta['last_page']

pages=input('Input:请输入您爬取的页数,默认为所有:')
if pages == '':
pages_num=pages_num+1
else:
pages_num_input=int(pages)
if pages_num_input<pages_num:
pages_num=pages_num_input+1
else:
pages_num=pages_num+1
print('共爬取{}页'.format(pages_num))

for n in range(1,pages_num):
url_com='https://wallhaven.cc/api/v1/search?sorting=toplist&page='+str(n)+'&apikey='+api_key
req=requests.get(url_com,headers=headers,proxies=proxies_v2ray).content
jsonlist=json.loads(req)
jsonlist_data=jsonlist['data']
for i in range(len(jsonlist_data)):
id_number=jsonlist_data[i]['id']
file_suffix = os.path.splitext(jsonlist_data[i]['path'])[1]
if os.access(final_folder+'%s'%id_number+file_suffix, os.F_OK):
print('图片{}存在,跳过'.format(id_number))
else:
r=requests.get(jsonlist_data[i]['path'],headers=headers,proxies=proxies_v2ray)
print('下载{}图片'.format(id_number))
with open(final_folder+'%s'%id_number+file_suffix, 'wb') as f:
f.write(r.content)

print('进入休眠3s')
time.sleep(3)

print('Finish!')