用不到20行代码制作一个 “手绘风” 视频


今天分享的文章与计算机视觉相关,用不到 20 行Python代码将一张实拍图片转化为手绘风,无需对图片进行任何预处理、后处理;代码中只借助了两个常见库,核心计算由  Numpy 负责 ,Pillow 负责图片读写

在正文开始之前,先看一下效果,下面是单张图片转换前后对比

图一

用不到20行代码制作一个 “手绘风” 视频

图二

用不到20行代码制作一个 “手绘风” 视频

图三

用不到20行代码制作一个 “手绘风” 视频

为了增加趣味性,再将这段代码应用到一个视频中,新鲜的 “手绘风视频” 出炉

 

“手绘风”实现步骤

讲解之前,需要了解手绘图像的三个主要特点:

  • 图片需为灰度图,是单通道的;
  • 边缘部分线条较重涂抹为黑色,相同或相近像素值转换后趋于白色;
  • 在光源效果的加持下,灰度变化可模拟人类视觉的远近效果

读取图片,转化为数组

因为后面要用到像素计算,为了方便,事先将读取后的图片转化为数组

a = np.asarray(Image.open("Annie1.jpg").convert('L')).astype('float')

计算 x,y,z 轴梯度值,并归一化

刚才提到手绘照片的一个特点,就是 手绘照片对边缘区域更加侧重,定位图片边缘部分,最有效方式就是计算梯度,用灰度变化来模拟图片远近效果,depth 表示预设深度,z 轴默认梯度为 1

depth = 10.  # (0-100)
grad = np.gradient(a)  # 取图像灰度的梯度值
grad_x, grad_y = grad  # 分别取横纵图像梯度值
grad_x = grad_x * depth / 100.
grad_y = grad_y * depth / 100.

对梯度值完成归一化操作

A = np.sqrt(grad_x ** 2 + grad_y ** 2 + 1.)
uni_x = grad_x / A
uni_y = grad_y / A
uni_z = 1. / A

加入光源效果

手绘风图片除了计算梯度值之外,还需要考虑光源影响。光源入射的角度不同会对 x,y,z 各轴上的梯度值有不同程度的影响。添加一个模拟光源,放置在斜上方,与 x , y 分别形成两个夹角

用不到20行代码制作一个 “手绘风” 视频

并且这两个夹角是通过实验得到是已知的,然后根据正弦余弦函数计算出最终新的像素值

vec_el = np.pi / 2.2  # 光源的俯视角度,弧度值
vec_az = np.pi / 4.  # 光源的方位角度,弧度值
dx = np.cos(vec_el) * np.cos(vec_az)  # 光源对 x轴的影响
dy = np.cos(vec_el) * np.sin(vec_az)  # 光源对 y轴的影响
dz = np.sin(vec_el)  # 光源对z 轴的影响

b = 255 * (dx * uni_x + dy * uni_y + dz * uni_z)  # 光源归一化,8 255
b = b.clip(0, 255)# 对像素值低于0,高于255部分做截断处理

导出图片,并保存

im.save("Annie_shouhui.jpg")

以下是该步骤涉及到的的全部代码

from PIL import Image
import numpy as np


a = np.asarray(Image.open("Annie1.jpg").convert('L')).astype('float')

depth = 10.  # (0-100)
grad = np.gradient(a)  # 取图像灰度的梯度值
grad_x, grad_y = grad  # 分别取横纵图像梯度值
grad_x = grad_x * depth / 100.
grad_y = grad_y * depth / 100.
A = np.sqrt(grad_x ** 2 + grad_y ** 2 + 1.)
uni_x = grad_x / A
uni_y = grad_y / A
uni_z = 1. / A

vec_el = np.pi / 2.2  # 光源的俯视角度,弧度值
vec_az = np.pi / 4.  # 光源的方位角度,弧度值
dx = np.cos(vec_el) * np.cos(vec_az)  # 光源对 x轴的影响
dy = np.cos(vec_el) * np.sin(vec_az)  # 光源对 y轴的影响
dz = np.sin(vec_el)  # 光源对z 轴的影响

b = 255 * (dx * uni_x + dy * uni_y + dz * uni_z)  # 光源归一化
b = b.clip(0, 255)

im = Image.fromarray(b.astype('uint8'))  # 重构图像
im.save("Annie_shouhui.jpg")

 

制作手绘风视频

图片转化后的效果虽然也不错,但图片毕竟是静态的,人作为视觉动物,如果能做成动态的那再好不过了,知道上面的方法之后,只需对视频再加上一个拆帧合并操作,就能制作一个手绘风视频效果。

首先我们需要一个待转换的视频文件。这里我用 you-get 工具在 B 站上找了一个视频,下载了下来

you-get --format=dash-flv -o ./ https://www.bilibili.com/video/BV1tT4y1j7a9?from=search&8014393453748720686
用不到20行代码制作一个 “手绘风” 视频

下载完之后,用 OpenCV2 对视频进行切帧操作,将视频转为一张张图片,同时对图片进行手绘风格转化,再写出到本地视频文件中

 vc = cv2.VideoCapture(video_path)
    c = 0
    if vc.isOpened():
        rval,frame = vc.read()
        height,width = frame.shape[0],frame.shape[1]
        print(height, width)
    else:
        rval = False
        height,width = 960,1200

    # jpg_list = [os.path.join('Pic_Directory/',i) for i in os.listdir('Pic_Directory') if i.endswith('.jpg')]

    fps = 24 # 视频帧率
    video_path1 = './text.mp4'
    video_writer = cv2.VideoWriter(video_path1,cv2.VideoWriter_fourcc(*'mp4v'),fps,(width,height))

    while rval:
        rval,frame = vc.read()# 读取视频帧
        img = coonvert_jpg(Image.fromarray(frame))
        frame_converted = np.array(img)

        # 转化为三通道
        image = np.expand_dims(frame_converted,axis = 2)
        result_arr = np.concatenate((image,image,image),axis = -1)

        video_writer.write(result_arr)
        print('Sucessfully Conveted---------{}'.format(c))
        c = c + 1
        if c >= 3000:
            break
    video_writer.release()

在图片序列提取时,需要注意一点,因为转化后的图片是单通道的,直接借助 OpenCV 生成视频序列是无法播放的,需增加一个步骤单通道转化为三通道!

 # 转化为三通道
 image = np.expand_dims(frame_converted,axis = 2)
 result_arr = np.concatenate((image,image,image),axis = -1)

想让生成的视频更有感觉的话可以添加一个背影音乐,借助剪辑软件、Python 都可以实现。

小结

本文主要介绍了如何用 Python将一张图片转化为手绘风格,代码量不多,可以很方便地应用。不过背后的原理并不简单,涉及数学、物理相关的识点

文中涉及到的源码大部分其实都已经贴在文章,但为了方便起见,我已经将数据和源码整合在一起,想获取的同学可以,请在公号后台回复关键字:手绘

如果文章对你有帮助,欢迎转发/点赞/收藏~

作者:zeroing

来源:小张Python

转自:https://mp.weixin.qq.com/s/dGP5ec5lgNF9QpDnOLJBpQ

Scrapy 爬虫框架 10 分钟快速入门

作者:wedo实验君

来源:Python中文社区

1. scrapy简介

scrapy是基于事件驱动的Twisted框架下用纯python写的爬虫框架。很早之前就开始用scrapy来爬取网络上的图片和文本信息,一直没有把细节记录下来。这段时间,因为工作需要又重拾scrapy爬虫,本文和大家分享下,包你一用就会, 欢迎交流。

1.1 scrapy框架

scrapy框架包括5个主要的组件和2个中间件Hook。

  • ENGIINE:整个框架的控制中心, 控制整个爬虫的流程。根据不同的条件添加不同的事件(就是用的Twisted)
  • SCHEDULER:事件调度器
  • DOWNLOADER:接收爬虫请求,从网上下载数据
  • SPIDERS:发起爬虫请求,并解析DOWNLOADER返回的网页内容,同时和数据持久化进行交互,需要开发者编写
  • ITEM PIPELINES:接收SPIDERS解析的结构化的字段,进行持久化等操作,需要开发者编写
  • MIDDLEWARES:ENGIINESPIDERS, ENGIINEDOWNLOADER之间一些额外的操作,hook的方式提供给开发者

从上可知,我们只要实现SPIDERS(要爬什么网站,怎么解析)和ITEM PIPELINES(如何处理解析后的内容)就可以了。其他的都是有框架帮你完成了。(图片来自网络,如果侵权联系必删)

Scrapy 爬虫框架 10 分钟快速入门

1.2 scrapy数据流

我们再详细看下组件之间的数据流,会更清楚框架的内部运作。(图片来自网络,如果侵权联系必删)

Scrapy 爬虫框架 10 分钟快速入门

    1. SPIDERS发爬虫请求给ENGIINE, 告诉它任务来了
    2. ENGIINE将请求添加到SCHEDULER调度队列里, 说任务就交给你了,给我安排好
    3. SCHEDULER看看手里的爬取请求很多,挑一个给ENGIINE, 说大哥帮忙转发给下载DOWNLOADER
    4. ENGIINE:好的, DOWNLOADER你的任务来了
    5. DOWNLOADER:开始下载了,下载好了,任务结果 交给ENGIINE
    6. ENGIINE将结果给SPIDERS, 你的一个请求下载好了,快去解析吧
    7. SPIDERS:好的,解析产生了结果字段。又给SPIDERS转发给ITEM PIPELINES
    8. ITEM PIPELINES: 接收到字段内容,保存起来。

第1步到第8步,一个请求终于完成了。是不是觉得很多余?ENGIINE夹在中间当传话筒,能不能直接跳过?可以考虑跳过了会怎么样。

这里分析一下

  • SCHEDULER的作用:任务调度, 控制任务的并发,防止机器处理不过来
  • ENGIINE:就是基于Twisted框架, 当事件来(比如转发请求)的时候,通过回调的方式来执行对应的事件。我觉得ENGIINE让所有操作变的统一,都是按照事件的方式来组织其他组件, 其他组件以低耦合的方式运作;对于一种框架来说,无疑是必备的。

2. 基础:XPath

写爬虫最重要的是解析网页的内容,这个部分就介绍下通过XPath来解析网页,提取内容。

2.1 HTML节点和属性

(图片来自网络,如果侵权联系必删)

Scrapy 爬虫框架 10 分钟快速入门

2.2 解析语法

  • a / b:‘/’在 xpath里表示层级关系,左边的 a是父节点,右边的 b是子节点
  • a // b:表示a下所有b,直接或者间接的
  • [@]:选择具有某个属性的节点
    • //div[@classs], //a[@x]:选择具有 class属性的 div节点、选择具有 x属性的 a节点
    • //div[@class=”container”]:选择具有 class属性的值为 container的 div节点
  • //a[contains(@id, “abc”)]:选择 id属性里有 abc的 a标签

一个例子

response.xpath('//div[@class="taglist"]/ul//li//a//img/@data-original').get_all()
# 获取所有class属性(css)为taglist的div, 下一个层ul下的所有li下所有a下所有img标签下data-original属性

# data-original这里放的是图片的url地址

更多详见

http://zvon.org/comp/r/tut-XPath_1.html#Pages~List_of_XPaths

3. 安装部署

Scrapy 是用纯python编写的,它依赖于几个关键的python包(以及其他包):

  • lxml 一个高效的XML和HTML解析器
  • parsel ,一个写在lxml上面的html/xml数据提取库,
  • w3lib ,用于处理URL和网页编码的多用途帮助程序
  • twisted 异步网络框架
  • cryptography 和 pyOpenSSL ,处理各种网络级安全需求
# 安装
pip install scrapy

4. 创建爬虫项目

scrapy startproject sexy

#
 创建一个后的项目目录
# sexy
# │  scrapy.cfg
# │
# └─sexy
#     │  items.py
#     │  middlewares.py
#     │  pipelines.py
#     │  settings.py
#     │  __init__.py
#     │
#     ├─spiders
#     │  │  __init__.py
#     │  │
#     │  └─__pycache__
#     └─__pycache__

#
 执行 需要到scrapy.cfg同级别的目录执行
scrapy crawl sexy

从上可知,我们要写的是spiders里的具体的spider类和items.py和pipelines.py(对应的ITEM PIPELINES

5. 开始scrapy爬虫

5.1 简单而强大的spider

这里实现的功能是从图片网站中下载图片,保存在本地, url做了脱敏。需要注意的点在注释要标明

  • 类要继承 scrapy.Spider
  • 取一个唯一的name
  • 爬取的网站url加到start_urls列表里
  • 重写parse利用xpath解析reponse的内容

可以看到parse实现的时候没有转发给ITEM PIPELINES,直接处理了。这样简单的可以这么处理,如果业务很复杂,建议交给ITEM PIPELINES。后面会给例子

# 目录结果为:spiders/sexy_spider.py
import scrapy
import os
import requests
import time


def download_from_url(url):
    response = requests.get(url, stream=True)
    if response.status_code == requests.codes.ok:
        return response.content
    else:
        print('%s-%s' % (url, response.status_code))
        return None


class SexySpider(scrapy.Spider):
   # 如果有多个spider, name要唯一
    name = 'sexy'
    allowed_domains = ['uumdfdfnt.94demo.com']
    allowed_urls = ['http://uumdfdfnt.94demo.com/']

    # 需要爬取的网站url加到start_urls list里
    start_urls = ['http://uumdfdfnt.94demo.com/tag/dingziku/index.html']
    save_path = '/home/sexy/dingziku'

    def parse(self, response):
        # 解析网站,获取图片列表
        img_list = response.xpath('//div[@class="taglist"]/ul//li//a//img/@data-original').getall()
        time.sleep(1)

        # 处理图片, 具体业务操作, 可交给items, 见5.2 items例子
        for img_url in img_list:
            file_name = img_url.split('/')[-1]
            content = download_from_url(img_url)
            if content is not None:
                with open(os.path.join(self.save_path, file_name), 'wb'as fw:
                    fw.write(content)

        # 自动下一页(见5.3 自动下一页)
        next_page = response.xpath('//div[@class="page both"]/ul/a[text()="下一页"]/@href').get()
        if next_page is not None:
            next_page = response.urljoin(next_page)
            yield scrapy.Request(next_page, callback=self.parse)

5.2 items和pipline例子

这里说明下两个的作用

  • items:提供一个字段存储, spider会将数据存在这里
  • pipline:会从items取数据,进行业务操作,比如5.1中的保存图片;又比如存储到数据库中等

我们来改写下上面的例子

  • items.py其实就是定义字段scrapy.Field()
import scrapy
class SexyItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    img_url = scrapy.Field()
  • spiders/sexy_spider.py
import scrapy
import os
# 导入item
from ..items import SexyItem

class SexySpider(scrapy.Spider):
   # 如果有多个spider, name要唯一
    name = 'sexy'
    allowed_domains = ['uumdfdfnt.94demo.com']
    allowed_urls = ['http://uumdfdfnt.94demo.com/']

    # 需要爬取的网站url加到start_urls list里
    start_urls = ['http://uumdfdfnt.94demo.com/tag/dingziku/index.html']
    save_path = '/home/sexy/dingziku'

    def parse(self, response):
        # 解析网站,获取图片列表
        img_list = response.xpath('//div[@class="taglist"]/ul//li//a//img/@data-original').getall()
        time.sleep(1)

        # 处理图片, 具体业务操作, 可交给yield items
        for img_url in img_list:
            items = SexyItem()
            items['img_url'] = img_url
            yield items
  • pipelines.py
import os
import requests


def download_from_url(url):
    response = requests.get(url, stream=True)
    if response.status_code == requests.codes.ok:
        return response.content
    else:
        print('%s-%s' % (url, response.status_code))
        return None


class SexyPipeline(object):

    def __init__(self):
        self.save_path = '/tmp'

    def process_item(self, item, spider):
        if spider.name == 'sexy':
            # 取出item里内容
            img_url = item['img_url']
            
            # 业务处理
            file_name = img_url.split('/')[-1]
            content = download_from_url(img_url)
            if content is not None:
                with open(os.path.join(self.save_path, file_name), 'wb'as fw:
                    fw.write(content)
        return item
  • 重要的配置要开启settings.py中开启piplines类,数值表示优先级
ITEM_PIPELINES = {
   'sexy.pipelines.SexyPipeline'300,
}

5.3 自动下一页

有时候我们不仅要爬取请求页面中的内容,还要递归式的爬取里面的超链接url,特别是下一页这种,解析内容和当前页面相同的情况下。一种笨方法是手动加到start_urls里。大家都是聪明人来试试这个。

  • 先在页面解析下下一页的url
  • scrapy.Request(next_page, callback=self.parse) 发起一个请求,并调用parse来解析,当然你可以用其他的解析

完美了,完整例子见5.1

next_page = response.xpath('//div[@class="page both"]/ul/a[text()="下一页"]/@href').get()
if next_page is not None:
   next_page = response.urljoin(next_page)
   yield scrapy.Request(next_page, callback=self.parse)

5.4 中间件

  • 下载中间件 中间件的作用是提供一些常用的钩子Hook来增加额外的操作。中间件的操作是在middlewares.py。可以看到主要是处理请求process_request,响应process_response和异常process_exception三个钩子函数。
  • 处理请求process_request: 传给DOWNLOADER之前做的操作
  • 响应process_responseDOWNLOADERENGIINE响应之前的操作

这里举一个添加模拟浏览器请求的方式,防止爬虫被封锁。重写process_request

from scrapy.contrib.downloadermiddleware.useragent import UserAgentMiddleware
import random
agents = ['Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;',
              'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv,2.0.1) Gecko/20100101 Firefox/4.0.1',
              'Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11',
              'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11',
              'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)']

class RandomUserAgent(UserAgentMiddleware):
    def process_request(self, request, spider):
        ua = random.choice(agents)
        request.headers.setdefault('User-agent',ua,)

统一要在settings.py中开启下载中间件,数值表示优先级

DOWNLOADER_MIDDLEWARES = {
    'sexy.middlewares.customUserAgent.RandomUserAgent'20,
}

5.5 可用配置settings.py

除了上面提供的pipline配置开启和中间件配置外,下面介绍几个常用的配置

  • 爬虫机器人规则:ROBOTSTXT_OBEY = False, 如果要爬取的网站有设置robots.txt,最好设置为False
  • CONCURRENT_REQUESTS:并发请求
  • DOWNLOAD_DELAY:下载延迟,可以适当配置,避免把网站也爬挂了。

所有的配置详见 https://doc.scrapy.org/en/latest/topics/settings.html

6. 总结

相信从上面的介绍,你已经可以动手写一个你自己的爬虫了。我也完成了做笔记的任务了。scrapy还提供更加详细的细节,可参见https://docs.scrapy.org/en/latest/

最后总结如下:

  • scrapy是基于事件驱动Twisted框架的爬虫框架。ENGIINE是核心,负责串起其他组件
  • 开发只要编写spider和item pipline和中间件, download和schedule交给框架
  • scrapy crawl 你的爬虫name,name要唯一
  • 爬取的url放在start_urls, spider会自动Request的,parse来解析
  • pipline和中间件要记得在settings中开启
  • 关注下settings的常用配置,需要时看下文档

 

作者简介:wedo实验君, 数据分析师;热爱生活,热爱写作

转自:https://mp.weixin.qq.com/s/4gABstkXc5i6DYxU9fpsvA

B 站上这些 Python 视频真香!

大家好,我是东哥。

无论什么时候,都有初学者存在找不到学习资源的困惑。其实呢,一般通过百度、知乎估计也都能找到个的结果,不过质量可能就会层次不齐了。

像知乎上有些回答还是蛮良心的,推荐一些好的书籍或者学习链接,百度的话。。广告多不说,而且很多时候搜索出来的结果都不专业或者重复率极高,需要很长时间去甄别。

所以这里不得不再次提一下b站了,真的是越来越香了,可以作为资源搜索的强大补充,甚至首选。虽然东哥还不是b站up主(未来有计划),但对b站的粘性还挺高,通过平日里的积累也收藏了不少专业的技术视频资源,比如Python、数据分析、机器学习技术等。

之前可能也给大家分享过,不过这次是结合自己的收藏和日常积累,认真整理的一些优质UP主视频学习资源,全部免费,你说香不香。大概有以下这么几个方向。

该死!B 站上这些 Python 视频真香!

一、小甲鱼零基础入门学习Python

该死!B 站上这些 Python 视频真香!

小甲鱼的这个教学视频在b站已经有1455万的播放量了,搜索结果排名第一名,全套共96节,课程有趣幽默,很多朋友都是通过这个入门的。如果你是零基础开始学习Python,这个视频比较适合,可以一步一步深入熟悉Python

链接:https://www.bilibili.com/video/av4050443

二、数据结构与算法Python版

该死!B 站上这些 Python 视频真香!

该课程是由北京大学陈斌老师主讲的,内容包括算法的分析、基本线性结构、递归与动态规划、排序与查找、树及其算法、图及其算法等,以Python语言讲解。

整个课程将围绕着 算法+数据结构=程序的思路,以问题求解为导向进行学习,帮助学生提高理论、抽象、设计的能力。在课程中还穿插了一些案例和编程练习,教你积极建立数据抽象和层次分析的思维模式。

链接:https://www.bilibili.com/video/BV1VC4y1x7uv

三、Python自动化测试

该死!B 站上这些 Python 视频真香!

对于学习自动化测试的朋友,这个课程可以看下,作者白月黑羽,声音很好听。他的专栏下介绍了SeleniumAppiumPytest的各种使用方法,无论是浏览器还是手机全部都可以搞定

1. Selenium 浏览器自动化

链接:https://www.bilibili.com/video/BV1Z4411o7TA

2. Appium 手机 App 自动化 + Python

链接:https://www.bilibili.com/video/BV1tE411n7rV

3. pytest 自动化测试框架

链接:https://www.bilibili.com/video/BV1bV41167a4

四、Python自动化办公

该死!B 站上这些 Python 视频真香!

对于用Python自动化办公是很多朋友的刚需,写个脚本可以彻底释放双手难道不香吗?这个专栏整个视频48节,写了对于日常办公的各种操作,比如excelwordPPT、自动发邮件、钉钉群发、文件管理等。

链接:https://www.bilibili.com/video/BV1gt4y1D7W8

五、Python Web开发

1. Django 网站开发

该死!B 站上这些 Python 视频真香!

链接:https://www.bilibili.com/video/BV1AE41117Up

2. Flask 网站开发

该死!B 站上这些 Python 视频真香!

介绍了Python编程语言基础,Web前端基础开发,熟练基于Flask世界流行Web框架的网站后台开发技术,包括Jinja2模板、SQLAlchemy ORM框架、并且掌握Flask站点在LinuxNginx部署与发布等。

链接:https://www.bilibili.com/video/BV1H4411M72k

六、Python数据分析

1. 《利用Python进行数据分析》适合初学者

该死!B 站上这些 Python 视频真香!

UP主叫 Chenghsi Hsieh,这套视频就是在讲《利用Python进行数据分析》这本书,东哥推荐过无数次了,还在视频号里赠送过纸质版。这个课程54节,以代码形式讲解Python数据分析的各种操作,NumpypandasMatplotlib等,如果觉得自己学习书籍有难度,可以配合这个视频讲解一起学习。

链接:https://www.bilibili.com/video/BV1ZJ411W77D

2. 【莫烦Python】Numpy & Pandas (数据处理教程)

该死!B 站上这些 Python 视频真香!

UP主莫烦,课程就是将Numpypandas如何进行数据处理的,简单粗暴,感兴趣直接跟着学就完了。另外,他的专栏还有很多系列也可以学习。

链接:https://www.bilibili.com/video/av16378934

七、Python爬虫

该死!B 站上这些 Python 视频真香!

这个爬虫视频内容比较全面了,从爬虫概念、httpsrequestsbeautifulsoupxpath、模拟登录、代理、异步、aiohhtpseleniumscrapy框架和一些实战练习。

如果你已经有了基本的Python基础,那么可以上手学一下这些技能,如果能自己加以实战练习,锻炼几次相信就能独立抓取信息了。不过对于爬虫的尺度,这个大家自己衡量该死!B 站上这些 Python 视频真香!

接:https://www.bilibili.com/video/BV1ha4y1H7sx

八、Python机器学习

1. 菜菜的机器学习sklearn

该死!B 站上这些 Python 视频真香!

UP主菜菜,一个妹子,自称全站最贴心的算法老师,详细讲解了Sklearn的所有用法,从各种模型的工作原理、公式推导、以及代码实现,总共85节完结。另外她还有其他专栏单独讲解xgboost模型的,也都是一个非常不错的学习教程。

链接:https://www.bilibili.com/video/BV1vJ41187hk

2.【一起啃书】机器学习西瓜书白话解读

该死!B 站上这些 Python 视频真香!

也是一个妹子,ID叫致敬大神。她的课程是专门啃一些经典书籍,分享自己的学习笔记,比如西瓜书、蜥蜴书、深度学习入门等等。相信啃过西瓜书的小伙伴都知道,是需要一些数学基础的,在自学的过程中肯定会有不懂的地方,那么这就是一个很好的辅助教程啦。

链接:https://www.bilibili.com/video/BV17J411C7zZ

九、Python深度学习

1. 《python深度学习》教程

该死!B 站上这些 Python 视频真香!

UP主像科比一样扣篮,结合书籍《python深度学习》的详细讲解,目前已完结,共69课。非常好的一个深度学习教程,讲的细致易懂,推荐想要学习深度学习的朋友学习。

链接:https://www.bilibili.com/video/BV1fK4y147rp

2. PyTorch深度学习快速入门教程(绝对通俗易懂!)

该死!B 站上这些 Python 视频真香!

UP主土推,讲解PyTorch的入门教程,他将带你用全新的思路,快速入门PyTorch独创的学习思路

链接:https://www.bilibili.com/video/BV1hE411t7RN

上面是东哥收藏的一些关于Python各个方向的视频学习资源,供大家参考。UP主们还是输出了很有价值的东西的,比如一起啃书系列,可以帮助大家看书的同时梳理自己的知识体系,解答疑惑。

另外,对于数据分析、机器学习、深度学习这些方向以及一些基础课程,如数学、线代、统计、优化论、信息论、博弈论等等会在下一篇文章总结。

可能是全网最完整的 Python 操作 Excel库总结!

来自公众号:早起Python

在之前的办公自动化系列文章中,我已经对Python操作Excel的几个常用库openpyxlxlrd/xlwtxlwingsxlsxwriter等进行了详细的讲解。

为了进一步带大家了解各个库的异同,从而在不同场景下可以灵活使用,本文将横向比较7个可以操作 Excel 文件的常用模块,在比较各模块常用操作的同时进行巩固学习!

首先让我们来整体把握下不同库的特点

  1. xlrdxlwtxlutils 各自的功能都有局限性,但三者互为补充,覆盖了Excel文件尤其是 .xls 文件的操作。xlwt 可以生成 .xls 文件,xlrd 可以读取已经存在的 .xls 文件,xlutils 连接 xlrdxlwt 两个模块,使用户可以同时读写一个 .xls 文件。简单来说,xlrd 负责读、xlwt 负责写、xlutils 负责提供辅助和衔接
  2. xlwings 能够非常方便的读写 Excel 文件中的数据,并且能够进行单元格格式的修改
  3. XlsxWriter 是一个用来写 .xlsx 文件格式的模块。它可以用来写文本、数字、公式并支持单元格格式化、图片、图表、文档配置、自动过滤等特性。但不能用来读取和修改 Excel 文件
  4. openpyxl 通过 工作簿 “workbook – 工作表 sheet – 单元格 cell” 的模式对 .xlsx 文件进行读、写、改,并且可以调整样式
  5. pandas 大家都不陌生,是进行数据处理和分析的强大模块,有时也可以用来自动化处理Excel

如果你懒得看详细的对比过程,可以直接看最后的总结图,然后拉到文末收藏点赞就算学会了

可能是全网最完整的 Python 操作 Excel库总结!

一、安装

7个模块均为非标准库,因此都需要在命令行中 pip 进行安装:

pip install xlrd
pip install xlwt
pip install xlutils
pip install xlwings
pip install XlsxWriter
pip install openpyxl
pip install pandas

二、模块导入

多数模块可以直接通过名字导入,有些模块约定俗称会使用缩写:

import xlrd
import xlwt
import xlwings as xw
import xlsxwriter
import openpyxl
import pandas as pd

xlutils 模块是 xlrdxlwt 之间的桥梁,最核心的作用是拷贝一份通过 xlrd 读取到内存中的 .xls 对象,然后再拷贝对象上通过 xlwt 修改 .xls 表格的内容。xlutils 可以将 xlrd 的 Book 对象复制转换为 xlwt 的Workbook 对象,具体使用时通常导入的是模块中的 copy 子模块:

import xlutils.copy

三、读取 Excel 文件

3.1 获取文件

并不是所有7个模块都可以读取 Excel 文件,而即使能读取Excel文件也要分不同后缀名进行讨论,具体如下:

  1. xlwtxlutilsXlsxWriter 不能读取文件
  2. xlrd 可以读取 .xls.xlsx 文件
  3. xlwings 可以读取 .xls.xlsx 文件
  4. openpyxl 可以读取 .xlsx 文件
  5. pandas 可以读取 .xls.xlsx 文件

下面使用两个大小均为 10MB 的 .xls.xlsx 文件进行测试:

xls_path = r'C:xxxDesktoptest.xls'
xlsx_path = r'C:xxxDesktoptest.xlsx'

3.1.1 xlrd 读取文件

xlrd 可以读取 .xls.xlsx 文件

xls = xlrd.open_workbook(xls_path)
xlsx = xlrd.open_workbook(xlsx_path)

3.1.2 xlwings 读取文件

xlwings 直接对接的是 apps,也就是 Excel 应用程序,然后才是工作簿 books 和工作表 sheets,xlwings 需要安装有 Excel 应用程序的环境xlwings 可以读取 .xls.xlsx 文件

app = xw.App(visible=True, add_book=False# 程序可见,只打开不新建工作薄
app.display_alerts = False # 警告关闭
app.screen_updating = False # 屏幕更新关闭
# wb = app.books.open(xls_path)
wb = app.books.open(xlsx_path)
wb.save() # 保存文件
wb.close() # 关闭文件
app.quit() # 关闭程序

3.1.3 openpyxl 读取文件

openpyxl 可以读取 .xlsx 文件

wb = openpyxl.load_workbook(xlsx_path)

如果读取 .xls 文件会报错:

wb = openpyxl.load_workbook(xls_path)

openpyxl.utils.exceptions.InvalidFileException: openpyxl does not support the old .xls file format, please use xlrd to read this file, or convert it to the more recent .xlsx file format.

3.1.4 pandas 读取文件

pandas 可以读取 .xls.xlsx 文件

xls = pd.read_excel(xls_path, sheet_name='Sheet1')
xlsx = pd.read_excel(xlsx_path, sheet_name='Sheet1')

接下来比较四个模块在同一配置电脑下读取 10MB .xlsx 文件的时间(运行3次求平均值),所用代码为:

import time
import xxx

time_start = time.time()
xxx
time_end = time.time()
print('time cost: ', time_end-time_start, 's')

最后测试的结果是,xlwings 读取 10MB 文件最快,xlrd 次之,openpyxl 最慢(因电脑而异,结果仅供参考)

读入 Excel 文件部分的表格总结如下:可能是全网最完整的 Python 操作 Excel库总结!

3.2 获取工作表

针对上述4个可以读取 Excel 文件的模块,进一步讨论其获取工作表 sheet 的方式

3.2.1 xlrd 获取工作表

可以通过 sheet 名查找:

sheet = xlsx.sheet_by_name("Sheet1")

也可通过索引查找:

sheet = xlsx.sheet_by_index(0)

3.2.2 xlwings 获取工作表

xlwings 的工作表分为活动工作表以及指定工作簿下的特定工作表:

sheet = xw.sheets.active  # 在活动工作簿
sheet = wb.sheets.active  # 在特定工作簿

3.2.3 openpyxl 获取工作表

.active 方法默认获取工作簿的第一张工作表

sheet = wb.active

另外也可以通过工作表名指定获取工作表:

sheet = wb['Sheet1']

3.2.4 pandas 获取工作表

单独获取工作表完全没有 pandas 什么事情,因为读取文件的同时已经且必须指定工作表才能读取:

xlsx = pd.read_excel(xlsx_path, sheet_name='Sheet1')

四、创建 Excel 文件

简单总结创建 Excel 文件的情况:

  1. xlrdxlutils 不能创建 Excel 文件
  2. xlwt 只能创建 .xls 文件,不能创建 .xlsx 文件
  3. xlwings可以创建 .xls.xlsx 文件
  4. XlsxWriter 可以创建 .xlsx 文件
  5. openpyxl 可以创建 .xls.xlsx 文件
  6. pandas 没有创建 Excel 的概念,但可以存储时产生 .xls.xlsx 文件

4.1 xlwt 创建文件

xlwt 只能创建 .xls 文件,不能创建 .xlsx 文件

xls = xlwt.Workbook(encoding= 'ascii')
# 创建新的sheet表
worksheet = xls.add_sheet("Sheet1")

4.2 xlwings 创建文件

xlwings可以创建 .xls.xlsx 文件,只需要最后保存时写清楚后缀即可。使用如下命令:

wb = app.books.add()

无论是新建还是打开都需要保存工作簿、关闭工作簿、关闭程序,即:

wb.save(path + r'new_practice.xlsx') 
wb.close() 
app.quit() 

4.3. XlsxWriter 创建文件

XlsxWriter 可以创建 .xlsx 文件:

xlsx = xlsxwriter.Workbook()   
# 添加工作表
sheet = xlsx .add_worksheet('Sheet1')

4.4 openpyxl 创建文件

openpyxl可以创建 .xls.xlsx 文件,只需要最后保存时写清楚后缀即可。使用如下命令:

wb = Workbook()
# 新工作簿中指定即创建工作表
sheet = wb.active

4.5. pandas 创建文件

pandas 只需要最后转存时写清楚后缀即可。实际上比较抽象,pandas 并不需要一开始先创建一个 Excel 文件,可以围绕数据框做各式操作后用 .to_excel 命令再用 .xls 或者 .xlsx 做文件后缀。如果一定要产生一个空白 Excel 文件可以用如下命令:

df = pd.DataFrame([])
df.to_excel(r'C:xxxtest1.xlsx')

五、保存文件

简单总结保存 Excel 文件的情况:

  1. xlrd 不能保存 Excel 文件
  2. xlwt 可以保存 .xls 文件
  3. xlutils 可以将 xlrd 对象复制为 xlwt 对象后保存 .xls 文件
  4. xlwings可以保存 .xls.xlsx 文件
  5. XlsxWriter 可以保存 .xlsx 文件
  6. openpyxl 可以保存 .xlsx 文件
  7. pandas 可以保存 .xls.xlsx 文件

5.1 xlwt 保存文件

xlwt 可以保存 .xls 文件

# xls = xlwt.Workbook(encoding= 'ascii')
# worksheet = xls.add_sheet("Sheet1")
xls.save("new_table.xls")

5.2 xlutils 保存文件

xlutils 可以将 xlrd 对象复制为 xlwt 对象后保存 .xls 文件

# xls_path = r'C:xxxxtest.xls'
# xls = xlrd.open_workbook(xls_path)
xls_xlutils = xlutils.copy.copy(xls)
xls_xlutils.save('new_text.xls')

5.3 xlwings 保存文件

xlwings可以保存 .xls.xlsx 文件

# wb = app.books.open(xls_path)
wb = app.books.open(xlsx_path)
wb.save() # 保存文件
wb.close() # 关闭文件
app.quit() # 关闭程序

5.4 XlsxWriter 保存文件

XlsxWriter 可以保存 .xlsx 文件,.close 命令执行后文件关闭的同时保存:

# xlsx = xlsxwriter.Workbook()
# sheet = xlsx .add_worksheet('Sheet1')
xlsx.close()

5.5 openoyxl 保存文件

openpyxl 可以保存 .xlsx 文件

# wb = openpyxl.load_workbook(xlsx_path)
# wb = Workbook()
# sheet = wb.active
wb.save('new_test.xlsx')

6. pandas 保存文件

pandas 可以保存 .xls.xlsx 文件

df1 = pd.DataFrame([123])
df2 = pd.DataFrame([124])
df1.to_excel(r'C:xxxxtest1.xls')
df2.to_excel(r'C:xxxxtest2.xlsx')

六、获取单元格的值

获取单元格的值基本前提是能够读取文件,因此基本围绕 xlrdxlwingsopenpyxlpandas 介绍。xlutils 由于能够复制一份 .xls 因此也可以使用和 xlrd 完全一样的读取单元格方法。

6.1. xlrd/xlutils 获取单元格

xlutils 因为是直接拷贝一份 xlrd 适用的对象,读取单元格使用的方法和 xlrd 完全一样。xlwt 没有读取单元格的能力

# xls = xlrd.open_workbook(xls_path)
# sheet = xlsx.sheet_by_name("Sheet1")
value = sheet.cell_value(46# 第5行第7列的单元格
print(value)
rows = table.row_values(4)
cols = table.col_values(6)
for cell in rows:
    print(cell)

6.2. xlwings 获取单元格

# app = xw.App(visible=True, add_book=False) 
# app.display_alerts = False 
# app.screen_updating = False 
# wb = app.books.open(xls_path)
# sheet = wb.sheets.active

# 获取单个单元格的值
A1 = sheet.range('A1').value
print(A1)
# 获取横向或纵向多个单元格的值,返回列表
A1_A3 = sheet.range('A1:A3').value
print(A1_A3)
# 获取给定范围内多个单元格的值,返回嵌套列表,按行为列表
A1_C4 = sheet.range('A1:C4').value
print(A1_C4)
# 获取单个单元格的值
A1 = sheet.range('A1').value
print(A1)
# 获取横向或纵向多个单元格的值,返回列表
A1_A3 = sheet.range('A1:A3').value
print(A1_A3)
# 获取给定范围内多个单元格的值,返回嵌套列表,按行为列表
A1_C4 = sheet.range('A1:C4').value
print(A1_C4)

6.3 openpyxl 获取单元格

# wb = openpyxl.load_workbook(xlsx_path)
# wb = Workbook()
# sheet = wb.active

# 一、指定坐标范围的值
cells = sheet['A1:B5']
# 二、指定列的值
cells = sheet['A']
cells = sheet['A:C']
# 三、指定行的值
cells = sheet[5]
cells = sheet[5:7]
# 获取单元格的值
for cell in cells:
    print(cell.value)

6.4 pandas 获取单元格的值

pandas 读取 Excel 文件后即将它转换为数据框对象,解析内容的方法基本是 pandas 体系中的知识点,如 .iloc() .loc() .ix() 等:

print(df1.iloc[0:1, [1]])
print(df1.loc['b'])
print(df2.ix['a''a']) # 有些版本取消了ix,可以用iat

七、写入数据

还是先简单总结对 Excel 文件写入数据的情况:

  1. xlrd 不能写入数据
  2. xlwt 可以写入数据
  3. xlutils 可以借用 xlwt 方法写入数据
  4. xlwings可以写入数据
  5. XlsxWriter 可以写入数据
  6. openpyxl 可以写入数据
  7. pandas 将 Excel 文件读取为数据框后,是抽象出数据框层面进行操作,没有了对 Excel 进行单元格写入和修改的概念

7.1. xlwt/xlutils 写入数据

# xls = xlrd.open_workbook(xls_path)
# xls_xlutils = xlutils.copy.copy(xls)
# sheet = xls_xlutils.sheet_by_name("Sheet1")
# value = sheet.cell_value(4, 6)
# print(value)
sheet.write(46"新内容")

7.2 xlwings 写入数据

# app = xw.App(visible=True, add_book=False) 
# app.display_alerts = False 
# app.screen_updating = False 
# wb = app.books.open(xls_path)
# sheet = wb.sheets.active

# 写入 1 个单元格
sheet.range('A2').value = '大明'
# 一行或一列写入多个单元格
# 横向写入A1:C1
sheet.range('A1').value = [1,2,3]
# 纵向写入A1:A3
sheet.range('A1').options(transpose=True).value = [1,2,3]
# 写入范围内多个单元格
sheet.range('A1').options(expand='table').value = [[1,2,3], [4,5,6]]

7.3 XlsxWriter 写入数据

代码中的 new_format 是之前预设好的样式,下文会进行介绍

# xlsx = xlsxwriter.Workbook()
# sheet = xlsx .add_worksheet('Sheet1')

# 一、写入单个单元格
sheet.write(row, col, data, new_format)
# A1:从A1单元格开始插入数据,按行插入
sheet.write_row('A1', data, new_format)
# A1:从A1单元格开始插入数据,按列插入
sheet.write_column('A1', data, new_format)

7.4. openpyxl 写入数据

# wb = openpyxl.load_workbook(xlsx_path)
# wb = Workbook()
# sheet = wb.active

# 一、写入单元格
cell = sheet['A1']
cell.value = '业务需求'
# 二、写入一行或多行数据
data1 = [123]
sheet.append(data1)
data2 = [[123], [456]]
sheet.append(data2)

八、样式调整

依旧简单总结对 Excel 文件样式调整的情况:

  1. xlrdxlutils 不能调整样式 (也可以说 xlutils 可以,只不过是借用了 xlwt 的方法)
  2. xlwt 可以调整样式
  3. xlwings可以调整样式
  4. XlsxWriter 可以调整样式
  5. openpyxl 可以调整样式
  6. pandas 不能调整样式

8.1 xlwt 调整样式

xlwt 支持调整字体、边框、颜色等样式

# 字体部分
# 初始化样式
style1 = xlwt.XFStyle()
# 为样式创建字体
font = xlwt.Font()
font.name = 'Times New Roman'   #字体
font.bold = True                #加粗
font.underline = True           #下划线
font.italic = True              #斜体
# 设置样式
style1.font = font
# 使用样式
sheet.write(46"新内容1", style1)

# 边框部分
borders = xlwt.Borders()
# 设置线型
borders.left = xlwt.Borders.DASHED
borders.right = xlwt.Borders.DASHED
borders.top = xlwt.Borders.DASHED
borders.bottom = xlwt.Borders.DASHED
# 设置样色
borders.left_colour = 0x40
borders.right_colour = 0x40
borders.top_colour = 0x40
borders.bottom_colour = 0x40

style2 = xlwt.XFStyle()
style2.borders = borders
# 使用样式
sheet.write(58"新内容2", style2)

8.2 xlwings 调整样式

简单介绍 xlwings 对颜色的调整:

# 获取颜色
print(sheet.range('C1').color)
# 设置颜色
sheet.range('C1').color = (2550120)
# 清除颜色
sheet.range('C1').color = None

8.3 XlsxWriter 调整样式

XlsxWriter 包含大量功能,可以创建工作表后对工作表进行高定自定义的样式修改:

new_format = xlsx.add_format({
        'bold':  True,  # 字体加粗
        'border'1,  # 单元格边框宽度
        'align''left',  # 水平对齐方式
        'valign''vcenter',  # 垂直对齐方式
        'fg_color''#F4B084',  # 单元格背景颜色
        'text_wrap'True  # 是否自动换行
    })

sheet.write(row, col, data, new_format)

8.4 openpyxl 调整样式

openpyxl 样式主要包括字体、边框、段落对齐样式等

# 字体样式
from openpyxl.styles import Font 
cell = sheet['A1'] 
font = Font(name='Arial', size=12, bold=True, italic=True, color='FF0000') 
cell.font = font

# 段落对齐
from openpyxl.styles import Alignment 
cell = sheet['B2'] 
alignment = Alignment(horizontal='center', vertical='center',                     text_rotation=45, wrap_text=True) 
cell.alignment = alignment 

# 边框样式
from openpyxl.styles import Side, Border 
cell = sheet['B2'] 
side1 = Side(style='thin', color='FF0000') 
side2 = Side(style='dashed') 
border = Border(left=side1, right=side1, top=side2, bottom=side2) 
cell.border = border

九、插入图片

简单总结对 Excel 文件插入图片的情况:

  1. xlrdxlutils 不能调整样式 (也可以说 xlutils 可以,只不过是借用了 xlwt 的方法)
  2. xlwt 可以插入 .bmp 图片
  3. xlwings可以插入 图片
  4. XlsxWriter 可以插入图片
  5. openpyxl 可以插入图片
  6. pandas 不能插入图片

9.1 xlwt 插入图片

xlwt 插入图片要求图片格式必须是 .bmp 格式才能插入成功

sheet.insert_bitmap("test.bmp"23220.50.5)

insert_bitmap(img, x, y, x1, y1, scale_x, scale_y)img 表示要插入的图像地址,x 表示行,y 表示列x1 y1 表示相对原来位置向下向右偏移的像素scale_x scale_y表示相对原图宽高的比例,图片可放大缩小

9.2 xlwings 插入图片

下面是用xlwings 插入图片的代码,可以指定位置

sheet.pictures.add(r'C:\xxx.jpg')
# 也可以给定位置插入
sheet.pictures.add(r'C:\xxx.jpg', left=sheet.range('A2').left, top=sheet.range('A2').top, width=100, height=100)

9.3 XlsxWriter 插入图片

第一个参数是插入的起始单元格,第二个参数是图片文件的绝对路径

sheet.insert_image('A1'r'C:\xxx.jpg')

9.4 openpyxl 插入图片

openpyxl也可以给Excel中插入指定图片并修改大小

from openpyxl.drawing.image import Image
img = Image('test.jpg')
newsize = (180360) 
img.width, img.height = newsize # 设置图片的宽和高
sheet.add_image(img, 'A2'# 往A2单元格插入图片

小结

以上就是根据不同 Python 模块,对常见的 Excel 操作进行对比的全部内容,最终结果汇总如下表所示

可能是全网最完整的 Python 操作 Excel库总结!

请注意,本文目的并不是要评出一个最好的库,仅是从不同角度对不同库进行对比,希望能够让大家了解各个库所擅长的工作。比如pandas虽然处理方便,但是不能添加图片修改样式,openpyxl虽然各种操作都支持,但是速度又相对慢一点等。

只有充分了解不同工具的特点,才能够在不同的场景下灵活运用不同的方法来高效解决问题!如果喜欢本文的话,希望你可以给本文点个赞来支持早起创作!

推荐↓↓↓

可能是全网最完整的 Python 操作 Excel库总结!

人工智能与大数据技术

发封邮件就能关机?用python实现远程控制电脑

一、前言

大家好,欢迎来到 Crossin的编程教室 !

不知道你有没有遇到类似的情况:眼看要出门了,结果一个程序要运行很久才能结束,导致无法立刻关机。这种时候就想,如果可以远程查看电脑运行状态甚至控制关机就好了。

类似这样的“远控”软件有不少,一般需要在电脑端和控制端各安装一个软件,即可实现远程监控和操作。

其实我们也可以用 Python 做一个简便的小工具,实现远程控制电脑。当然,我们需要的操作十分简单:要能通过邮件和电脑进行通讯,让电脑以截图的方式为我们显示程序当前运行的状态;还要可以通过邮件发送一些预设好的指令,比如执行某个程序,或者远程关机等等。

因为是通过邮件,所以手机上无需任何开发环境和特殊App,只要能联网发送Email就可以。

下面我们就来分享下这样一个“邮件远控”工具的Python实现。

二、使用 yagmail 进行邮件发送

使用 yagmail 发邮件非常简单,我们首先需要安装两个模块:

pip install yagmailpip install keyring

2.1、发送一个简单的邮件

我们用 yagmail 来发一封简单的邮件试试:

import yagmailimport keyring
sender = '发件邮箱'password = '发件人密码'receiver = '收件邮箱'
# 注册yagmail.register(sender, password)
# 用 SMTP 服务器发邮件yag = yagmail.SMTP(user=sender, host='smtp.163.com')
# 设置邮件的内容contents = ['Do not go gentle into that good night!']
# 发送邮件yag.send(receiver, '邮件主题', contents=contents)

这里密码可以使用真实密码,也可以使用授权码。授权码的获得需要到各个邮件的网页获取,这里以网易邮箱为例:

发封邮件就能关机?用python实现远程控制电脑

我们进入网页版邮箱,依次点击 1、2,进入如下页面:

发封邮件就能关机?用python实现远程控制电脑

点击开启,然后按照指引获取授权码即可。

在连接邮箱服务器的时候,我们设置的一个 host,这个同样需要在邮箱网页中获取。其中 163 的邮箱地址为 smtp.163.com,QQ 邮箱服务器地址为 smtp.qq.com,其它邮箱的可以自己查找一下。

邮箱内容是一个列表,我们现在是放入了一句话。

在调用 send 方法发送邮件时,我们需要传入几个参数。第一个是收件人,第二个是邮件的主题,第三个就是邮件的内容了。邮件内容我们传入开始的内容列表。大家可以使用自己的邮箱测试一下。

2.2、发送附件

yagmail 非常智能,我们不需要像原始的 email 模块一样,先将附件上传到邮箱服务器,然后再发给收件人。yagmail 会自动识别内容中的资源文件,然后自动上传:

import yagmailimport keyring
sender = '发件邮箱'password = '发件人密码'receiver = '收件邮箱'
# 注册yagmail.register(sender, password)
# 用 SMTP 服务器发邮件yag = yagmail.SMTP(user=sender, host='smtp.163.com')
# 设置邮件的内容contents = ['im.jpg']
# 发送邮件yag.send(receiver, '邮件主题', contents=contents)

其中 im.jpg 是我本地的一张图片,在发送的时候 yagmail 会自动识别,然后发送出去。我们没做什么修改,就完成了附件的发送。

我们也可以直接发送带有图片的邮件,而不是以附件的形式发送:

import yagmailimport keyring
sender = '发件邮箱'password = '发件人密码'receiver = '收件邮箱'
# 注册yagmail.register(sender, password)
yag = yagmail.SMTP(user=sender, host='smtp.163.com')contents = [    '这是一张图片',    yagmail.inline('im.jpg')]yag.send(receiver, '邮件主题', contents=contents)

我们只要用 yagmail.inline 包含图片路径即可。不过需要注意,发送的内容不能单是一张图片。

三、邮件读取

邮件的读取需要使用到 imbox 模块。我们需要先使用 keyring 模块读取密码,keyring 可以读取到在 yagmail 中注册了的邮箱密码:

import yagmailimport keyringfrom imbox import Imbox
sender = '你的邮箱'# 获取邮箱密码password = keyring.get_password(sender)# 读取邮箱邮件with Imbox('imap.163.com', sender, password, ssl=True) as imbox:    # 读取所有未读邮件    all_msg = imbox.messages(unread=True)

Imbox 对象就是我们的邮箱对象,里面包含了所有邮箱信息。我们调用 messages 方法就可以获取所有邮件,如果我们将 unread 设置为 True,则表示读取未读邮件。获取了所有邮件,我们就可以查看更多信息了:

import yagmailimport keyringfrom imbox import Imbox
sender = '你的邮箱'# 获取邮箱密码password = keyring.get_password(sender)# 读取邮箱邮件with Imbox('imap.163.com', sender, password, ssl=True) as imbox:    # 读取所有未读邮件    all_msg = imbox.messages(unread=True)    # 遍历邮件    for uid, message in all_msg:            # 输出邮件的主题            print(message.subject)            # 标记已读            imbox.mark_seen(uid)            # 获取邮件的文本内容            msg = message.body['plain']

我们要实现控制电脑就需要一直读取,我们可以使用 schedule 模块设置定时任务:

import timeimport schedule
def func():    print('test')
schedule.every(2).seconds.do(func)while True:    schedule.run_pending()    time.sleep(1)

这样的话,可以每两秒执行一次 func 函数。

四、开发电脑控制工具

我们定义一个 ComputerController 类,用于控制电脑。该类的最基本方法就发邮件和收邮件:

import timeimport yagmailimport keyringimport schedulefrom imbox import Imbox

class ComputerController():
    TIME_SPACE = 10
    SMTP_163 = 'smtp.163.com'
    SMTP_QQ = 'smtp.qq.com'
    def __init__(self, user, receiver):        self.user = user        self.receiver = receiver        # 注册        yagmail.register(self.user, '你的密码')
    def send_main(self, msg):        """发邮件"""        yag = yagmail.SMTP(user=self.user, host=self.SMTP_163)        contents = [            msg        ]        yag.send(self.receiver, '来自电脑的邮件', contents=contents)

    def receive_mail(self):        """收邮件"""
        # 读取密码        password = keyring.get_password('yagmail', self.user)        with Imbox('imap.163.com', 'sockwz@163.com', password, ssl=True) as imbox:            all_msg = imbox.messages(unread=True)            for uid, message in all_msg:                if '110' == message.subject:                    # 标记已读                    imbox.mark_seen(uid)                    msg = message.body['plain']                    print(msg)
    def run_controller(self):          # 定时读取邮件        schedule.every(self.TIME_SPACE).seconds.do(controller.receive_mail)        while True:            schedule.run_pending()            time.sleep(self.TIME_SPACE)

我们只需要创建这个类就可以了:

if __name__ == '__main__':    controller = ComputerController('发件邮箱', '收件邮箱')    controller.run_controller()

我们需要传入收件邮箱和发件邮箱,另外还需要在 init 方法中,修改密码即可。

五、实现一些预设功能

下面我们实现一些小功能。

5.1、截图

from PIL import ImageGrabdef grab_windows(self):    im = ImageGrab.grab()    im.save('status.jpg')

使用 Pillow 模块需要先安装:

pip install pillow

5.2、关机

import osdef shut_down(self):    """关机"""    os.system('shutdown /s /t 0')

这个是通用的两个功能。

另外我们可以使用 pynput 模块控制键盘,进行一些操作。这个需要根据自己电脑的情况操作,比如我使用 PyCharm 运行程序的快捷键是 shift+f10,所以可以使用下列代码运行程序:

def run_program(self):    """运行程序"""    with self.kb.pressed(Key.shift):        self.kb.press(Key.f10)        self.kb.release(Key.f10)

类似的方法,大家可以自己定制一些操作。

整个程序完整代码如下:

import osimport timeimport yagmailimport keyringimport schedulefrom imbox import Imboxfrom PIL import ImageGrabfrom pynput.keyboard import *
class ComputerController():
    TIME_SPACE = 10    SMTP_163 = 'smtp.163.com'    SMTP_QQ = 'smtp.qq.com'    kb = Controller()
    def __init__(self, user, receiver):        self.user = user        self.receiver = receiver        # 注册        yagmail.register(self.user, '授权码或密码')
    def send_main(self, msg):        """发邮件"""        yag = yagmail.SMTP(user=self.user, host=self.SMTP_163)        contents = [            msg        ]        yag.send(self.receiver, '来自电脑的邮件', contents=contents)
    def receive_mail(self):        """收邮件"""
        # 读取密码        password = keyring.get_password('yagmail', self.user)        with Imbox('imap.163.com', 'sockwz@163.com', password, ssl=True) as imbox:            all_msg = imbox.messages(unread=True)            for uid, message in all_msg:                if '110' == message.subject:                    # 标记已读                    imbox.mark_seen(uid)                    msg = message.body['plain']                    if msg[0].__contains__('截图'):                        # 截图                        self.grab_windows()                        # 发送截图                        self.send_main('status.jpg')                    if msg[0].__contains__('重启'):                        self.restart_server()                        time.sleep(3)                        self.grab_windows()                        self.send_main('status.jpg')                    if msg[0].__contains__('关机'):                        self.shut_down()
    def grab_windows(self):        im = ImageGrab.grab()        im.save('status.jpg')
    def restart_server(self):        time.sleep(3)        os.system('shutdown')        time.sleep(3)        os.system('startup')
    def run_program(self):        """运行程序"""        with self.kb.pressed(Key.shift):            self.kb.press(Key.f10)            self.kb.release(Key.f10)
    def shut_down(self):        """关机"""        os.system('shutdown /s /t 0')
    def run_controller(self):        schedule.every(self.TIME_SPACE).seconds.do(controller.receive_mail)        while True:            schedule.run_pending()            time.sleep(self.TIME_SPACE)

if __name__ == '__main__':    controller = ComputerController('发件邮箱', '收件邮箱')    controller.run_controller(

这个代码里我们设定,在读取邮件时只读取主题为“110”的邮件,这样可以避免干扰邮件。所以我们在发送邮件时需要将主题设置为“110”,而邮件内容则是一些关键词。比如“截图”就是截图操作,“关机”就是关机操作。这个可以自己定义。

以上便实现了一个通过邮件向远程电脑发送操作指令的工具。大家可以在此基础上进行改进和扩展。欢迎把你的想法分享在留言中。

如果文章对你有帮助,欢迎转发/点赞/收藏~

作者:ZackSock
来源:建文件夹X

转自:https://mp.weixin.qq.com/s/Iuaxx7rV0MPt7EmCzxlvhw