在 AWS Lambda 上執行 Tesseract OCR

Falldog Hsieh
GoFreight HQ
Published in
11 min readAug 24, 2019

--

最近因為公司爬蟲需求,需要破解驗證碼,因緣際會接觸到 Tesseract ,發現是個不錯的 tool,還有 python library 可以用😍。在不做任何 Image 的 Preprocess,或是 Training 的情況下,已經可以有五、六成的成功率(我好容易滿足啊XD),由於爬蟲架構的需求,我們覺得 Tesseract 很適合放到 Cloud 上,當成一個 Service 執行,因此,就想在 AWS Lambda 上把它跑起來。

簡單來說,需要以下幾個流程才能把 Tesseract + pytesseract 執行起來

  • Lambda Function - pytesseract
  • Lambda Layer - Tesseract
  • Deploy to Lambda - by Serverless

其實之前有寫過一些簡單的 Lambda (就是單純的 Python code)。但是,一想到這個新的獨立 Service,要重新設定一次所有的東西,還要自己 build 一些 module 就覺得麻煩,所以這次用了一個新的 Tool — serverless framework ,輕輕鬆鬆搞定 AWS Lambda Deploy 的流程。

1. Lambda Function - pytesseract

由於驗證碼的需求,我們執行的環境就需要透過 pip 安裝這兩個 package — pytesseract & Pillow

Lambda 可以讓你把 pip install 的 package 打包成 zip 檔,再包入你自己的 script 後,只要指定好 handler 的 entry point,就可以整包上傳執行了。 .zip 裡的結構大概如下:

├── handler.py
├── PIL/
└── pytesseract/

pytesseract 因為是純 python code,所以 Lambda 執行是沒有問題的。但是 Pillow 的程式碼有些是 C code ,所以在安裝時其實是需要 compile 的。在 Mac or Linux 上,在 build 完後,就會產生 machine dependency 的 .so 檔。如果你把本機端透過 pip 安裝的 python site-packages: PIL 直接包入 .zip 的話,上傳到 Lambda 執行,會產生 PIL import error。因為 .so 是不相容的,Lambda 在 AWS 執行的 VM OS 是 Amazon Linux

因此,需要在本機端把 Pillow.so build 出一份可以在 Lambda 上的 Amazon Linux 才行。不管是用 cross-compile or Docker,只要可以達到我們想要的結果,就是可行的方式。

在下面 serverless 的部分會再解講 build Pillow 的方式。

2. Lambda Layer - Tesseract

在本機端(ex: Mac),如果你想要執行 pytesseract ,除了需要用 pip 安裝 pytesseract 外,還需要透過 brew 安裝 tesseract library

$ brew install tesseract

那麼,在 Lambda 上,該怎麼裝上 tesseract 呢? 也是需要 build 出一個 可以在 Amazon Linux 上執行起來的 tesseract library。這邊的話,沒有官方已經 build 好的版本,所以就必需要自行去 build 才行。

這邊的 github repo - aws lambda tesseract layer 已經幫我們把 Dockerfile 寫好了,可以直接拿來使用。

使用方式

$ git clone https://github.com/bweigel/aws-lambda-tesseract-layer.git
$ cd aws-lambda-tesseract-layer
$ ./build.sh
$ serverless deploy

解釋一下其中大概的流程

  1. 在 Docker (from lambcoi/lambda-base) 中,build 出 Amazon Linux 環境需要的 tesseract library
  2. docker build 完成後,把 container 裡面的 /opt/build-dist複製一份到本機端
  3. 最後透過 serverless deploy 到 Lambda 的 Layer 上
    基本上 Lambda 的 Function 可以指定使用哪一些 Layer,所以我們 deploy tesseract layer 後,pytesseract Lambda Function 就可以綁定使用了

Dockerfile 裡面的參數可以自行微調

ARG LEPTONICA_VERSION=1.78.0
ARG TESSERACT_VERSION=4.1.0-rc4
ARG OCR_LANG=deu
ARG TESSERACT_DATA_SUFFIX=_fast
ARG TESSERACT_DATA_VERSION=4.0.0
  • TESSERACT_VERSION 目前最新的是 4.1.0
  • OCR_LANG 這個 script 預設只會裝 eng osdOCR_LANG 指定的 language
    可以依你的需求,在 Dockerfile 裡多指定一些 language 複製到 tesseract/share/tessdata
  • TESSERACT_DATA_SUFFIX tesseract train data 分成幾種
    - tessdata
    - tessdata_fast : train data 檔案比較小,所以跑的速度比較快
    - tessdata_best : train data 檔案比較大

3. Serverless

官網傳送門

Serverless 針對各個 cloud service 的 serverless solution,提供一個讓使用者們簡單方便的 deploy & manatement 的工具。

因為我們使用的是 AWS Lambda,所以這邊只會介紹 Serverless Lambda 相關的用法。只好在你 project root 裡,寫好一個 serverless.yml 就可以讓 Serverless 幫你處理 build 、deploy 、remove 的動作。在執行 Serverless 的指令時,它會讀取 ~/.aws/credentials 的內容,也可以透過環境變數的方式將 credential 的內容 pass 給 Serverless。細節可以參考 doc

Installation - 透過 npm 安裝 serverless,可以選擇安裝在 system or local,這邊簡單介紹裝在 system 的指令

$ npm install -g serverless

Tips

Serverless 在執行後,會在 project 目錄產生 .serverless/ 資料夾,其實砍掉的話也沒差,可以再執行 deploy or remove ,因為 Serverless 是將 deploy 上去的資訊統統紀錄在 S3 裡面,至於說砍掉 .serverless/ 後,如何找到同一個結果(因為 S3 的 serverless 目錄名稱會有 hash 的值),這邊就沒再深入研究了。

4. Serverless - Tesseract Lambda Layer

serverless.yml

service: tesseract-layer
frameworkVersion: ">=1.34.0 <2.0.0"
provider:
name: aws
region: us-west-1
layers:
tesseract:
path: layer
compatibleRuntimes:
- python3.6
- python3.7
  • provider.region : 改成你的 AWS region
  • layers.<layer-name>.path : local folder name,裡面的內容會整份打包上傳

執行以下指令,即可 deploy 至 AWS 上

$ serverless deploy

5. Serverless - pytesseract Lambda Function

python requirements.txt

Pillow==6.1.0
pytesseract==0.2.9

serverless.yml

service: my-captchaplugins:
- serverless-python-requirements
- serverless-pseudo-parameters
provider:
name: aws
runtime: python3.7
region: us-west-1
apiKeys:
- my-captcha-key
custom:
pythonRequirements:
dockerizePip: true
functions:
tesseract-ocr:
handler: handler.lambda_handler
layers:
- arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:layer:tesseract:1
events:
- http:
path: my-captcha
method: post
private: true

執行以下指令,即可 deploy 至 AWS 上

$ serverless deploy

在這邊用到兩個 Serverless plugin,安裝方式如下,雖然是透過 serverless 的指令執行,但是其實裝的就是 npm 的套件,安裝完後,會把這套件寫入project root 的 package.json

$ serverless plugin install -n serverless-python-requirements
$ serverless plugin install -n serverless-pseudo-parameters
  • serverless-python-requirements : build python package from requirements.txt
  • serverless-pseudo-parameters : serverless.yml 裡面出現的變數 #{xxx} 在執行時會被自動取代掉。(ex: #{AWS::AccountId} #{AWS::Region} ),所以不用填入自己的 AWS region, AccountId ,在執行時會自動抓取

Python package

serverless-python-requirements 會自動讀取 requirements.txt 用 pip 安裝裡面指定的套件,由於 Pillow 會 build c extension .so,所以需要將 dockerizePip 設成 true , Serverless 在執行時便會使用 Docker build pip package,否則就會在 Lambda 執行時看到 import error。

custom:
pythonRequirements:
dockerizePip: true

Lambda Function + API Gateway

當我們將 Lambda deploy 好後,必須掛上 AWS API Gateway,才能用 API 的方式執行 Lambda Function,因此我們在 serverless.yml 裡面需要加上 events http 的設定

provider:
apiKeys:
- my-captcha-key
functions:
tesseract-ocr:
events:
- http:
path: my-captcha # API path
method: post
private: true

這邊要注意到,我們設定 private 為 true,因為我們不希望 AWS API Gateway 的 API 任何人都可以隨便呼叫(預設為 public )。在設 private 為 true 後,要使用 API 的話,必須加上在 HTTP Header 加上 x-api-key 才行。而這把 key ,會在 Serverless deploy 時建出來,因此要注意 Serverless deploy 後的 output,key 會寫在裡面。要注意 output 也會有 API Gateway 的 endpoint URL path。(如果你錯過 output 的話,直接上 AWS Console,也是可以找到的)

Lambda Layer

functions:
tesseract-ocr:
layers:
- arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:layer:tesseract:1

在 Lambda Function 要使用我們剛剛 deploy 的 Layer ,必須在 layers 裡輸入剛剛 deploy 的 tesseract layer 的 ARN ID,要注意最後面的 number 1在 Serverless deploy 後可能會改變。

Call Lambda API

在上面的步驟都完成後,可以至 AWS Lambda 頁面的 dashboard,應該可以看到 Serverless 已經幫你把 Function, Layer, API Gateway 都處理好了,接著就可以試著自己呼叫看看結果吧。

cURL -X POST -H "x-api-key: XXXXXX" https://XXXXX.execute-api.us-west-1.amazonaws.com/dev/my-captcha

--

--