开发函数计算的正确姿势 —— 爬虫

news/2024/5/20 8:17:44 标签: 爬虫, serverless, json

在 《函数计算本地运行与调试 - Fun Local 基本用法》 中,我们介绍了利用 Fun Local 本地运行、调试函数的方法。但如果仅仅这样简单的介绍,并不能展现 Fun Local 对函数计算开发的巨大效率的提升。

这一次,我们拿一个简单的场景来举例子——开发一个简单的爬虫函数(代码参考函数计算控制台模板),介绍如何以正确姿势,从零开始,开发一个自动伸缩、按调用次数收费的 serverless 爬虫应用。

开发步骤
我们将这个完整的应用拆分成多步,并且在每一步完成后,我们都会进行相应的运行验证。

  1. 创建 Fun 项目
    首先,我们创建一个名为 image-crawler 的目录作为项目的根。然后在该目录下创建一个名为 template.yml 的文件,内容为:

ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
localdemo:
Type: 'Aliyun::Serverless::Service'
Properties:
Description: 'local invoke demo'
image-crawler:
Type: 'Aliyun::Serverless::Function'
Properties:
Handler: index.handler
CodeUri: code/
Description: 'Hello world with python2.7!'
Runtime: python2.7
如果不了解 Fun 定义的 Serverless Application Model,可以参考 这里。

操作完成后,我们的项目目录结构如下:

.
└── template.yml

  1. 编写 helloworld 函数代码
    在根目录下创建一个名为 code 的目录,并在该目录下创建一个名为 index.py 的文件,内容为一个简单的 helloworld 函数:

def handler(event, context):
return 'hello world!'
在项目根目录下执行:

fun local invoke image-crawler
函数运行成功:

开发函数计算的正确姿势 —— <a class=爬虫" />

操作完成后,我们的项目目录结构如下:

.
├── code
│ └── index.py
└── template.yml

  1. 事件触发函数运行
    我们简单修改第 2 步的代码,将 event 打印到 log 中。

import logging

logger = logging.getLogger()

def handler(event, context):
logger.info("event: " + event)
return 'hello world!'
通过触发事件的方式运行函数,得到如下结果:

开发函数计算的正确姿势 —— <a class=爬虫" />

可以看到,我们的函数已经能正确接收到触发事件了。

Fun Local 更多帮助信息,参考。

  1. 获取网页源码内容
    接下来,我们添加获取网页内容的代码。

import logging
import json
import urllib

logger = logging.getLogger()

def handler(event, context):
logger.info("event: " + event)
evt = json.loads(event)
url = evt['url']

html = get_html(url)

logger.info("html content length: " + str(len(html)))
return 'Done!'

def get_html(url):
page = urllib.urlopen(url)
html = page.read()
return html
代码逻辑比较简单,我们这里直接使用了 urllib 库,读取网页内容。

运行函数,得到以下输出:

开发函数计算的正确姿势 —— <a class=爬虫" />

  1. 解析网页中的图片
    我们打算通过正则解析网页中包含的 jpg 图片,因此这一步会比较繁琐,因为涉及到正则表达式的微调。为了能快速的解决问题,我们决定利用 fun local 提供的 local debugging 解决问题。local debugging 方法参考: 《函数计算本地运行与调试 - Fun Local 基本用法》。

首先,我们在下面这一行下个断点:

logger.info("html content length: " + str(len(html)))
然后以 debug 的方式启动,vscode 调试器连接后,函数会继续运行到我们断点的这一行:

开发函数计算的正确姿势 —— <a class=爬虫" />

我们可以直接在 Locals 一栏看到本地变量,其中包含了 html 这个变量,也就是我们获取到的 html 源码。我们可以将它的值复制出来,分析下,然后设计正则表达式。

我们可以先写一个简单的,比如可以是 http:\/\/[^\s,"]*.jpg。

怎么快速校验这段代码的正确性呢?我们可以利用调试器提供的 Watch(监视) 功能。

创建一个 Watch 变量,将下面的值输入进去:

re.findall(re.compile(r'http:\/\/[^\s,"]*.jpg'), html)
回车后,即可看到代码的执行效果:

开发函数计算的正确姿势 —— <a class=爬虫" />

这里一般不太容易一次写对,可以反复修改正则测试,直到正确为止。

我们得到的正确的图片解析的逻辑添加到代码中:

reg = r'http:\/\/[^\s,"]*.jpg'
imgre = re.compile(reg)

def get_img(html):
return re.findall(imgre, html)
然后在 handler 方法中调用即可:

def handler(event, context):
logger.info("event: " + event)
evt = json.loads(event)
url = evt['url']

html = get_html(url)

img_list = get_img(html)
logger.info(img_list)

return 'Done!'

编写完成后,可以继续本地执行,验证下结果:

echo '{"url": "https://image.baidu.com/search/index?tn=baiduimage&word=%E5%A3%81%E7%BA%B8"}' \
| fun local invoke image-crawler
可以看到,img_list 已经输出到控制台了:

开发函数计算的正确姿势 —— <a class=爬虫" />

  1. 将图片上传到 oss
    解析到的图片,我们选择使用 oss 存储。

首先,我们需要通过环境变量配置 OSS Endpoint 以及 OSS Bucket。

在 template 中配置环境变量(需提前创建好 oss bucket):

EnvironmentVariables:
OSSEndpoint: oss-cn-hangzhou.aliyuncs.com
BucketName: fun-local-test
然后就可以直接在函数中获取到这两个环境变量了:

endpoint = os.environ['OSSEndpoint']
bucket_name = os.environ['BucketName']
另外,fun local 运行函数时,还会提供一个额外的变量用来标识这是一个本地运行的函数。通过这个标识,我们可以用来做一些本地化的操作,比如我们可以在线上运行时连接 RDS,在本地运行时连接 Mysql。

这里,我们用该标识以不同的的方式创建 oss client,原因是线上运行时,通过 credentials 获取到的是扮演角色的临时 ak,有有效期限制,而本地运行时,没有该限制。oss 提供了这两种方式的构造方法,我们直接使用即可:

creds = context.credentials

if (local):
auth = oss2.Auth(creds.access_key_id,
creds.access_key_secret)
else:
auth = oss2.StsAuth(creds.access_key_id,
creds.access_key_secret,
creds.security_token)

bucket = oss2.Bucket(auth, endpoint, bucket_name)
接着我们遍历所有图片,将所有的图片上传到 oss:

count = 0
for item in img_list:
count += 1
logging.info(item)

Get each picture

pic = urllib.urlopen(item)
# Store all the pictures in oss bucket, keyed by timestamp in microsecond unit
bucket.put_object(str(datetime.datetime.now().microsecond) + '.png', pic)  

再在本地运行一下函数:

echo '{"url": "https://image.baidu.com/search/index?tn=baiduimage&word=%E5%A3%81%E7%BA%B8"}' \
| fun local invoke image-crawler
可以从日志看到,图片被一张一张的解析出来,并被上传到 oss 上了。
开发函数计算的正确姿势 —— <a class=爬虫" />

登陆 oss 控制台,可以看到这些图片。
开发函数计算的正确姿势 —— <a class=爬虫" />

部署
本地开发完成后,我们还需要将其发布到线上,让其成为一个可被调用的服务。以往,你可能觉得比较麻烦,比如要登陆控制台,创建服务、创建函数、配置环境变量,创建角色等,现在有了 fun 后,这一切都不需要了。

不过,本地与线上还是有些区别的,那就是要授权函数计算能够访问 OSS,怎么做呢?很简单,在我们的 template 中加入一行配置即可(Polices 文档,可以参考):

Policies: AliyunOSSFullAccess
添加后的 template.yml 内容如下:

ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
localdemo:
Type: 'Aliyun::Serverless::Service'
Properties:
Description: 'local invoke demo'
Policies: AliyunOSSFullAccess
image-crawler:
Type: 'Aliyun::Serverless::Function'
Properties:
Handler: index.handler
CodeUri: code/
Description: 'Hello world with python2.7!'
Runtime: python2.7
EnvironmentVariables:
OSSEndpoint: oss-cn-hangzhou.aliyuncs.com
BucketName: fun-local-test
然后,使用 fun deploy 后,可以看到部署成功的日志。

开发函数计算的正确姿势 —— <a class=爬虫" />

验证
通过控制台验证
登陆控制台,可以看到,我们的服务、函数、代码、环境变量等都已经就绪了。

开发函数计算的正确姿势 —— <a class=爬虫" />

在触发事件中,写入我们用来测试的 json,然后执行:

开发函数计算的正确姿势 —— <a class=爬虫" />

可以发现,会获得与本地一致的效果:

开发函数计算的正确姿势 —— <a class=爬虫" />

通过 fcli 验证
fcli 帮助文档 参考。

在终端执行以下命令,可以获取函数列表:

fcli function list --service-name localdemo
可以看到我们的 image-crawler 已经创建成功了。

{
"Functions": [
"image-crawler",
"java8",
"nodejs6",
"nodejs8",
"php72",
"python27",
"python3"
],
"NextToken": null
}
使用以下命令则可以调用函数运行:

fcli function invoke --service-name localdemo \
--function-name image-crawler \
--event-str '{"url": "https://image.baidu.com/search/index?tn=baiduimage&word=%E5%A3%81%E7%BA%B8"}'
运行成功后,会得到与控制台与 fun local 一致的结果。

小结
至此,我们的开发就算告一段落。

本文利用 fun local 提供的本地运行、调试的能力,做到了在本地开发函数,并且通过反复的执行函数得到反馈以便于快速迭代代码。

在本地开发完成后,不需要对代码进行任何修改,通过 fun deploy 命令,一键部署到云端,达到预期的效果。

本文介绍的方法,并不是开发函数计算的唯一方式。本文的目的,是能够向开发者传达一种信号——开发函数计算时,只要身姿正确,就会非常享受,开发流程也会十分顺畅。祝您使用愉快。

转载于:https://blog.51cto.com/14031893/2330097


http://www.niftyadmin.cn/n/1408209.html

相关文章

分片决策

在与客户讨论分片决策的时候&#xff0c;我经常会先给他们讲下面这个真实的故事。 几年前&#xff0c;有客户来找我&#xff0c;希望获得关于如何对系统分片的一些指导建议。他告诉我说&#xff0c;自从他知道很多应用MySQL的巨头&#xff08;比如Facebook和Twitter&#xff09…

VS中,无法嵌入互操作类型“***”,请改用适用的接口的问题

最近引用一个dll文件&#xff0c;发现在调用后&#xff0c;出现了无法嵌入互操作类型“……”&#xff0c;请改用适用的接口的错误提示。最后终于找到解决方案&#xff0c;如下&#xff1a; 选中项目中引入的dll&#xff0c;鼠标右键&#xff0c;选择属性&#xff0c;把“嵌入互…

采样方法---混合蒙特卡罗算法

为什么80%的码农都做不了架构师&#xff1f;>>> 动态系统 混合蒙特卡罗方法 转载于:https://my.oschina.net/liyangke/blog/2988272

wpf学习笔记-窗口

本文目录 1.窗口的外观 2.窗口的位置 3.窗口的大小 4.窗口的可见性和状态 5.窗口的生命周期 1.窗口的外观 WPF中默认窗口框架的外观&#xff0c;主要取决于Icon、Title、WindowsStyle、ResizeMode等属性。 Icon 指定窗口的图标&#xff1b; Title 指定窗口的标题&#xff1b; W…

[SDOI2010]星际竞速——费用流

类似于最短路的网络流&#xff0c;而且还要保证每个点经过一次&#xff0c;拆点就比较方便了。 连边怎么连&#xff1f;要保证最大流是n&#xff08;每个点经过一次&#xff09;还要能从直接跳转 将每个点拆点。源点向每个点的入点连一条容量为1费用为0的边。源点向每个点的出点…

基于Jmeter和Testlink的自动化测试框架研究与实施

1、摘 要目前基于Jmeter的接口自动化测试框架&#xff0c;大多只实现脚本维护和自动调度&#xff0c;无法与Testlink进行互通&#xff0c;实现测试方案与自动化实施流程连接&#xff0c;本文基于Testlink、Jmeter、Jenkins实现&#xff1a;通过Testlink统一维护接口自动化测试用…

wpf学习笔记-布局

本文目录 1.布局简介 2.面板&#xff08;Panel&#xff09; 3.视图框&#xff08;Viewbox&#xff09; 4.滚动视图控件&#xff08;ScrollViewer&#xff09; 5.公共布局属性 1.布局简介 应用程序界面设计中&#xff0c;合理的元素布局至关重要&#xff0c;它可以方便用户使用&…

「洛谷P1402」酒店之王 解题报告

P1402 酒店之王 题目描述 XX酒店的老板想成为酒店之王&#xff0c;本着这种希望&#xff0c;第一步要将酒店变得人性化。由于很多来住店的旅客有自己喜好的房间色调、阳光等&#xff0c;也有自己所爱的菜&#xff0c;但是该酒店只有p间房间&#xff0c;一天只有固定的q道不同的…