某次上课无聊,突然想写个图片爬虫玩玩,顺便把爬取的图片做个随机图片API接口(之前说写也一直拖到现在...);因为部分知识领域只知道其存在,但从没有实战学习过,干脆从头到尾练手一次,于是有了这篇博文,记录一下过程。
实现目标:
利用云服务器自动后台运行 Python脚本,包含开启自启;脚本实现内容:爬虫自动爬取图片,临时保存到本地,再上传到搜狗图床,自动保存图片上传之后的地址,以Json形式保存到本地文件。
采集日志图片
采集结果图片
Ps:这里的搜狗图床其实抓的搜狗图片搜索上传接口,我测试了百度(会改变图片分别率),360(不支持SSL且最大只能2M),所以选择了搜狗。至于为啥是JSON形式保存,这是方便后面用PHP写接口时,直接读取保存数组即可,方便调用。
环境要求:
个人使用的是百度云服务器,配置有点低...
机子配置:1H、2G、1M,操作系统:Centos7.2,运行环境:Python3.7
操作步骤
1.配置Python环境
注意:本步骤全是都在root超级用户权限下执行,非root用户注意添加 sudo
新购服务器,简单配置一下,升级所有包,升级软件和系统内核,非必须,需要一点时间,等一会儿吧。
yum -y update
Centos7.2默认安装了Python2.7版本,环境要求是3.7版本。
安装依赖包:
sudo yum -y groupinstall "Development tools"
sudo yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel libffi-devel
下载Python3.7安装包:
wget https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz
解压安装包:
tar -xzvf Python-3.7.1.tgz
配置安装目录并安装:
cd python3
./configure --preix=/usr/python3 && make && make install
等待安装完成
检查安装是否完成:
python3 -V && pip3 -V
>>Python 3.7.1
>>pip 18.1 from /usr/python3/lib/python3.7/site-packages/pip (python 3.7)
这里的pip是默认安装,但默认版本不是18.1,这是后来升级的,更新命令:
pip3 install pip --upgrade
根据安装路径和环境变量创建软链接:
个人安装路径:/root/python3,环境变量:/root/sbin
创建命令:
ln -s /usr/python3 /usr/sbin/python3
ln -s /usr/python3/bin/pip3 /usr/sbin/pip3
Ps:如果你安装完成,但在没有创建软链接之前,敲入Python3 命令没有反应,可能是由于的工作路径不再环境变量中,进入python3安装路径的bin目录测试即可;至于如何配置环境变量,请另行百度。
所以到现在为止,Python3.7的环境已经全部安装完成!
2.后台运行Python脚本
nohup python3 /root/python/AutoPic.py > log.txt &
解释:把脚本运行挂到后台,且在退出之后持续运行,标准输出重定向到log.txt作为日志。AutoPic.py 这个是爬虫脚本,我会在文章最后附上代码。
之后可使用 jobs命令查看后台任务,如若已重连终端,需要使用以下命令才能查看。
ps -ef|grep python3
3.设置开机自动运行
cd /etc/rc.d && chmod +x rc.local && vi rc.local
打开Vi编辑,在该文件最后加上以下语句,切勿修改其它设置
nohup python3 /root/python/AutoPic.py > log.txt &
最后按 ESC 输入 :wq! 退出保存,OK,完工!
4.爬虫源码
按照自己的机子配置的,比如线程数,受限服务器宽带限制,并不是越大越好。
必须模块:requests,bs4;安装命令。
pip3 install requests
pip3 install bs4
爬虫源代码:
# -*- coding: utf-8 -*-
from requests import get
from requests import post
from bs4 import BeautifulSoup as bs
from urllib.parse import unquote as urldecode
import re,os,random,queue,threading,time,json
def randstr(length=0):
km=''
s='ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
if length == 0:
length=32
for x in range(length):
i=random.randint(0,len(s)-1)
km+=s[i]
return(km)
def GetIndexUrl(Ptype,pages):
#@ paras Ptype str 壁纸分类
#@ paras Pages int 爬取页数
#@ return list 爬取地址
print('正在抓取图片地址(1)...')
index_list=list()
host='http://www.ivsky.com'
index_url=host+'/bizhi/'+Ptype+'/index_'
for x in range(pages):
index_list.append(index_url+str(x)+'.html')
return index_list
def GetIndex2Url(index_list):
#@ paras index_list list URL列表
print('正在抓取图片地址(2)...')
URL=list()
host='http://www.ivsky.com'
for url in index_list:
ret=get(url,timeout=5)
ret.encoding='utf-8'
html=ret.text
soup=bs(html,'html.parser')
for x in soup.find_all(name='div',attrs={"class":"il_img"}):
URL.append(host+(x.a.get('href')))
return URL
def GetImageUrl(IndexUrl):
#@ paras index_list list URL列表
print('正在处理图片地址(3)...')
URL=list()
for url in IndexUrl:
html=get(url,timeout=5)
soup2=bs(html.text,'html.parser')
for x in soup2.find_all(name='div',attrs={"class":"il_img"}):
u=x.img.get('src')
URL.append(u.replace('/t/','/pic/'))
print('返回图片真实地址(4)...')
return URL
def SavePic(ImageUrl):
#@ paras ImageUrl str 图片真实地址
global q
res=None
Name=randstr(6)+'.jpg'
FilePath=os.getcwd()+'/Temp/'+Name
headers={'Referer':'http://www.ivsky.com/'}
try:
res=get(ImageUrl,headers=headers,timeout=5)
if res.status_code == 200:
JPGbyte=res.content
with open(FilePath,'wb') as file:
file.write(JPGbyte)
file.close()
return FilePath
else:
q.put(ImageUrl)
except:
q.put(ImageUrl)
def UpPic(FilePath):
#@ paras FilePath str 文件路径
#@ return tuple 0:bool,1:strinfo
if not os.path.isfile(FilePath):
return (False,'文件不存在')
SGapi='https://pic.sogou.com/ris_upload'
with open(FilePath,'rb') as jpg:
files={'file':jpg}
try:
res=post(SGapi,files=files,allow_redirects=False,timeout=(3, 60))
except:
return (False,'文件上传失败__001')
jpg.close()
headers=dict(res.headers)
if 'Location' not in headers:
return (False,'文件上传失败__002')
ret=re.search('query=(.*?)&',res.headers['Location'])
PicUrl=urldecode(ret.group(1))
return (True,PicUrl,FilePath)
def SaveJson(strlist,Path):
#@ paras strlist list 保存数据
#@ paras Path str 保存路径
#@ return Bool True:success
ret=json.dumps(strlist)
try:
with open(Path,'w') as File:
File.write(ret)
File.close()
return True
except:
return False
def Run():
global q,PicUrl
while not q.empty() and Switch:
ImgUrl=q.get()
PicPath=SavePic(ImgUrl)
ret=UpPic(PicPath)
if ret[0]:
PicUrl.append(ret[1])
os.remove(ret[2])
else:
q.put(ImgUrl)
break
if __name__ == '__main__':
Num=5 #线程数
Switch=True #线程开关
PicUrl=list() #图片地址
Thread=list() #线程列表
#//获取所有图片真实地址
IndexUrl=GetIndex2Url(GetIndexUrl('nvxing',50))
ImgUrl=GetImageUrl(IndexUrl)
#//建立缓存文件夹
Path=os.getcwd()+'/Temp/'
if not os.path.exists(Path):
os.makedirs(Path)
#//填充图片地址队列
q=queue.Queue()
for url in ImgUrl:
q.put(url)
#//创建线程
for x in range(Num):
t=threading.Thread(target=Run)
t.setDaemon(False)
t.start()
Thread.append(t)
while not q.empty():
SaveJson(PicUrl,os.getcwd()+'/list.txt')
try:
for x in range(len(Thread)): #重启线程
if not Thread[x].isAlive():
Thread.pop(x)
t=threading.Thread(target=Run)
t.setDaemon(False)
t.start()
Thread.append(t)
else:
pass
print('活:',len(Thread),'剩:',q.qsize())
time.sleep(3)
except:
Switch=False
print('异常,结束所有线程,退出!')
break
else:
SaveJson(PicUrl,os.getcwd()+'/list.txt')
5.总结
文章内容虽然不多,却因为菜整整写了两天,主要是爬虫脚本出现很神奇的BUG,一直无法复现,浪费了一整天时间;脚本目前已经放在了服务器正常运行,过两天再把采集的图片利用PHP单独做个API。
接下来会更新的博文:【CentOS实现每日定时自动采集图片】,【[PHP]利用采集简单实现随机图片API】
最后:本次实践只是一种学习记录,博文也仅限用于交流学习,对于用于其它用途产生的后果自负。
参考文章:
https://blog.csdn.net/qiushisoftware/article/details/79520869
https://blog.csdn.net/wzx104104104/article/details/70832747
https://www.cnblogs.com/zhanglong8681/p/8421512.html
https://blog.csdn.net/elija940818/article/details/79238813