MENU

云服务器简单实现Python自动运维

November 3, 2018 • Read: 1480 • 技术杂谈

某次上课无聊,突然想写个图片爬虫玩玩,顺便把爬取的图片做个随机图片API接口(之前说写也一直拖到现在...);因为部分知识领域只知道其存在,但从没有实战学习过,干脆从头到尾练手一次,于是有了这篇博文,记录一下过程。

实现目标:

利用云服务器自动后台运行 Python脚本,包含开启自启;脚本实现内容:爬虫自动爬取图片,临时保存到本地,再上传到搜狗图床,自动保存图片上传之后的地址,以Json形式保存到本地文件。


采集日志图片
采集日志.png

采集结果图片
采集结果.png

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】

采集源站:http://www.ivsky.com/

最后:本次实践只是一种学习记录,博文也仅限用于交流学习,对于用于其它用途产生的后果自负。

参考文章:
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

Last Modified: November 5, 2018