技術雑記帳兼日記帳

AWS、Python、Terraformの使い方をコッソリ

python スクレイピング その1

はじめに

この本を買ったので、学習成果をまとめていく。

準備

スクレイピングWikipediaの「今日は何の日」を取得するんだけど芸がないので、「新しい記事」のリンクのタイトルをすべて取得してみる。

  • newArticles.py
import requests
from bs4 import BeautifulSoup

url = 'https://ja.wikipedia.org/'

resp = requests.get(url)
soup = BeautifulSoup(resp.content, 'html.parser')
new_articles = soup.find("div", attrs={"id": "new_articles"})
links = new_articles.find_all('a')

for link in links:
    print(link.get('title'))
  • 実行方法と結果
$ python newArticles.py 
特別:新しいページ
タチジャコウソウ
シソ科
イブキジャコウソウ属
多年生植物
ハーブ
香辛料
和名
麝香
衝動買い
ブランド
ヨーゼフ・メルク
17951181852616日
ウィーン
チェリスト
ウィーン国立歌劇場
チェロ
ウィーン国立音楽大学
ギター
ヴァイオリン
フランシス・グレスナー・リー
18783251962127日
アメリカ合衆国
法科学
1/12スケール (存在しないページ)
ドールハウス
ジオラマ
Wikipedia:メインページ新着投票所
Wikipedia:月間新記事賞
  • 簡単な解説
resp = requests.get(url)
soup = BeautifulSoup(resp.content, 'html.parser')

ここでリクエストを投げて、取得したレスポンスをBeautifulSoupでhtml.parserする。

new_articles = soup.find("div", attrs={"id": "new_articles"})
links = new_articles.find_all('a')

div要素でIDがnew_articlesの要素を取り出して、a Tagの要素(hrefやtitle)を取り出す。

for link in links:
    print(link.get('title'))

取得したlinkの要素titleをgetメソッドで取得する。


まとめ

find_allタグ全体とって、要素をgetメソッドで取るのかなぁ?
何にしてもスクレイピングは以外と面白いな。



python Amazon Rekognition Detect Text

はじめに

今回は画像からテキストを分析するDetect Textを使ってみる。

準備

下記のプログラムを用意する。

  • detect_text.py.py
from PIL import Image, ImageDraw, ImageFont
import boto3, sys, json, os, traceback

#画像の分析結果を返す
def detect_text(image):
    client=boto3.client('rekognition')
    #写真を読み出して、Byte変換してdetect_faces送信
    file = open(image,'rb')
    response = client.detect_text(Image={'Bytes':file.read()})

    return response

#BoundingBoxを設定する
def set_bounding_box(image, bounding_box_data):
    #画像の高さと幅を取得
    imgWidth, imgHeight = image.size
    draw = ImageDraw.Draw(image)

    #位置を算出
    left = bounding_box_data['Left'] * imgWidth
    top = bounding_box_data['Top'] * imgHeight
    width = bounding_box_data['Width'] * imgWidth
    height = bounding_box_data['Height'] * imgHeight

    points = (
            (left,top),
            (left + width, top),
            (left + width, top + height),
            (left , top + height),
            (left, top)

        )

    draw.line(points, fill='#00d400', width=2)
    
    return

#指定箇所に文字列を設定して返す
def set_bounding_box_text(image, text, bounding_box_data):

    #画像の高さと幅を取得
    imgWidth, imgHeight = image.size
    draw = ImageDraw.Draw(image)

    #位置を算出
    left = bounding_box_data['Left'] * imgWidth
    top = bounding_box_data['Top'] * imgHeight
    width = bounding_box_data['Width'] * imgWidth
    height = bounding_box_data['Height'] * imgHeight

    draw = ImageDraw.Draw(image)
    #フォントの設定
    font_ttf = "/usr/share/fonts/truetype/fonts-japanese-gothic.ttf"
    draw.font = ImageFont.truetype(font_ttf, 25)

    color = (0,255,0)
    pos = (left, top)
    #文字の設定
    draw.text(pos, text, color)
    return

if __name__ == '__main__':
    if len(sys.argv) != 3:
        exit()
    
    in_file_name = sys.argv[1]
    out_file_name = sys.argv[2]

    try:
        #画像分析
        #jsonが無ければ分析あれば使う
        json_file_name = in_file_name + '.json'
        if (os.path.exists(json_file_name) == True):
            file = open(json_file_name, 'r')
            response = file.read()
            response = json.loads(response)
        else:
            response = detect_text(in_file_name)
            file = open(json_file_name, 'w')
            file.write(json.dumps(response))

        #分析元の画像を読み込み
        image = Image.open(in_file_name)
        
        #画像に分析結果の設定
        cnt = 1
        for text in response['TextDetections']:
            #boundingboxを設定
            set_bounding_box(image, text['Geometry']['BoundingBox'])
            set_bounding_box_text(image, str(cnt), text['Geometry']['BoundingBox'])
            print(str(cnt) + ' is ' + 'Type:' + text['Type'] + ' [' + text['DetectedText'] + ']')
            cnt = cnt + 1

        #結果の出力
        image.save(out_file_name)


    except Exception as e:
        print(e)
        print(traceback.format_exc())

実行方法

python detect_text.py 分析したいファイル.jpg 出力したいファイル名.jpg
  • 実行結果
    今回は何枚か画像を貼り付けてみる
    数字がかぶって見づらいけど画像に数字を埋め込んで、ターミナルに数字と文字タイプ、文字列を表示するようにした。


1個めはIRON MAIDENのアルバムジャケット

f:id:halhalhal1:20210503101313j:plain

1 is Type:LINE [IRAN MAAIDEN]
2 is Type:LINE [Kllers]
3 is Type:WORD [IRAN]
4 is Type:WORD [MAAIDEN]
5 is Type:WORD [Kllers]

2個めはNIRVANAのアルバムジャケット
f:id:halhalhal1:20210503101603j:plain

1 is Type:LINE [NIRVANA]
2 is Type:LINE ["BLEACH'']
3 is Type:WORD [NIRVANA]
4 is Type:WORD ["BLEACH'']

3個めはネコは見ている系の寄せ集め
f:id:halhalhal1:20210503101825j:plain

1 is Type:LINE [7IL71]
2 is Type:LINE [HOMLSI]
:
48 is Type:WORD [LT ]
49 is Type:WORD [t]
50 is Type:WORD [OHOEN'OCFLS]
51 is Type:WORD [K]

まとめ

ラストが若干狂気じみているが、これはdetect textが日本語に対応していないからっぽい。
崩し文字は、そのままそれっぽく見えるアルファベットに変換してくれる。
NIRVANA」や「BLEACH」はさすがの検出力。



python pillow 色々な画像変換

はじめに

今回はpillowを使って色々な画像変換を試してみる。

準備

下記のプログラムを用意する。

  • convert.py
from PIL import Image, ImageOps
import sys

if len(sys.argv) != 2:
    exit()

#画像をsampleを開く
inFileName = sys.argv[1]
outFileName = 'out' + sys.argv[1] 
im = Image.open(inFileName)

# グレースケール
gray = im.convert('L')
gray.save('00'+'_gray_'+outFileName)

# ネガポジ
inve = im.convert('RGB')
inve = ImageOps.invert(inve)
inve.save('01'+'_invert1st_'+outFileName)

# ネガポジ戻し
inve = ImageOps.invert(inve)
inve.save('01'+'_invert2nd_'+outFileName)


# モノクロ
mono = im.convert(mode="1") 
mono.save('02'+'_mono_'+outFileName)

実行方法

python convert.py 画像.jpg

実行結果
グレイ変換
f:id:halhalhal1:20210501095611j:plain

ネガポジ変換
f:id:halhalhal1:20210501095731j:plain

ネガポジ変換戻し
f:id:halhalhal1:20210501095748j:plain

モノクロ変換
f:id:halhalhal1:20210501095804j:plain


まとめ

使いでが難しそうだけど、使いこなせれば画像解析とかに役立てるのかな?



python pillow 色々なメソッド

はじめに

今回はpillowを使って線や四角形などを描画してみる。

準備

下記のプログラムを用意する。

  • pil.py
from PIL import Image, ImageDraw

#640x480の黒い画像を準備 
im = Image.new('RGB', (640, 480), (0, 0, 0))
draw = ImageDraw.Draw(im)

#丸四角の線の色
outLineColor = (255, 255, 255)

#丸四角の座標値設定
#((左上のx座標, 左上のy座標), (右下のx座標, 右下のy座標))
#(左上のx座標, 左上のy座標, 右下のx座標, 右下のy座標)
#丸の描画
draw.ellipse((50, 50, 150, 150), fill=(255, 0, 0), outline=outLineColor)
#四角の描画
draw.rectangle((200, 100, 300, 200), fill=(0, 255, 0), outline=outLineColor)

#線の座標値設定
#(x1, y1, x2, y2, x3, y3...)
#((x1, y1), (x2, y2), (x3, y3)...)
#線の描画
#draw.line(((350, 100), (450, 250), (500, 100), (350, 200), (350, 100)), fill=(255, 255, 0), width=3)
linePoints = (
    (350, 100), (450, 250), (500, 100), 
    (350, 200), (350, 100)    
)
draw.line(linePoints, fill=(255, 255, 0), width=3)

#多角形の描画
polygonPoints = (
    (550, 200), (450, 350), (500, 200),
    (550, 300), (250, 200)    
)
draw.polygon(polygonPoints, fill=(0, 0, 255),outline=outLineColor)

#点の描画
points = (
    (250, 400), (250, 450), (300, 450),
    (350, 400), (400, 400)    
)
draw.point(points, fill=(0, 255, 255))

#保存しておく
im.save('image.png')

#めんどくさいから表示させる
im.show()

実行方法

python pil.py
  • 実行結果

f:id:halhalhal1:20210430115206p:plain

ごちゃごちゃしているがちゃんと表示された
pointは点なので分かりづらいがちゃんと表示されている


まとめ

Pillowのほうが直感的に使いやす印象がある。
グレー変換とかも挑戦してみようと思う。



python pillow Image

はじめに

今回はdetect_facesのBounding Boxとテキストをpillowに置き換えてみる。
※写真はAIで生成したものらしいです。

準備

先にpillowをインストールする。

pip install pillow

下記のプログラムを用意する。

  • detect_faces_pillow.py
from PIL import Image, ImageDraw, ImageFont
import boto3, sys, json, os, traceback

#detect_facesで顔の分析結果を返す
def detect_faces(image):
    client=boto3.client('rekognition')
    #写真を読み出して、Byte変換してdetect_faces送信
    file = open(image,'rb')
    response = client.detect_faces(Image={'Bytes':file.read()},Attributes=['ALL'])

    return response

#BoundingBoxを設定する
def set_bounding_box(image, bounding_box_data):
    #画像の高さと幅を取得
    imgWidth, imgHeight = image.size
    draw = ImageDraw.Draw(image)

    #位置を算出
    left = bounding_box_data['Left'] * imgWidth
    top = bounding_box_data['Top'] * imgHeight
    width = bounding_box_data['Width'] * imgWidth
    height = bounding_box_data['Height'] * imgHeight

    points = (
            (left,top),
            (left + width, top),
            (left + width, top + height),
            (left , top + height),
            (left, top)

        )

    draw.line(points, fill='#00d400', width=2)
    
    return

#指定箇所にtextを設定する
def set_text_image(image, text, pos):

    draw = ImageDraw.Draw(image)
    #フォントの設定
    font_ttf = "/usr/share/fonts/truetype/fonts-japanese-gothic.ttf"
    draw.font = ImageFont.truetype(font_ttf, 25)

    color = (0,255,0)
    #文字の吐き出し
    draw.text(pos, text, color)

    return


if __name__ == '__main__':
    if len(sys.argv) != 3:
        exit()
    
    in_file_name = sys.argv[1]
    out_file_name = sys.argv[2]

    try:
        #顔分析
        #jsonが無ければ分析あれば使う
        json_file_name = in_file_name + '.json'
        if (os.path.exists(json_file_name) == True):
            file = open(json_file_name, 'r')
            response = file.read()
            response = json.loads(response)
        else:
            response = detect_faces(in_file_name)
            file = open(json_file_name, 'w')
            file.write(json.dumps(response))

        #分析元の画像を読み込み
        image = Image.open(in_file_name)
        
        #画像に分析結果の設定(複数人できるが今回は一人だけ)
        for faceDetail in response['FaceDetails']:
            #boundingboxを設定
            text = '対象の年齢は'+ str(faceDetail['AgeRange']['Low']) + 'から' + str(faceDetail['AgeRange']['High']) + '歳です。'
            set_bounding_box(image, faceDetail['BoundingBox'])
            set_text_image(image, text, (0,0))
            break

            
        #結果の出力
        image.save(out_file_name)


    except Exception as e:
        print(e)
        print(traceback.format_exc())

実行方法

python detect_faces_pillow.py 分析したいファイル.jpg 出力したいファイル名.jpg
  • 実行結果

f:id:halhalhal1:20210428221847j:plain

BoundingBoxもちゃんと表示され、テキストも日本語で表示されている。


まとめ

Pillowのほうがフォントがボヤボヤしている気がするけど、線はきれい感じがする。
使いでとしては、簡易なCV2と行った感じなのかな?



python Amazon Rekognition Recognize Celebrities

はじめに

今回は有名人の顔を分析するRecognize Celebritiesを使ってみる。

準備

下記のプログラムを用意する。

  • recognize_celebrities.py
import boto3, sys, json, cv2, math, os, traceback

#recognize_celebritiesで顔の分析結果を返す
def recognize_celebrities(in_image_file):
    client=boto3.client('rekognition')
    #写真を読み出して、Byte変換してdetect_faces送信
    file = open(in_image_file,'rb')
    response = client.recognize_celebrities(Image={'Bytes':file.read()})

    return response

#Bounding Boxを設定して返す
def set_bounding_box(image_file, bounding_box_data):
    #画像の高さと幅を取得
    imgHeight, imgWidth = image_file.shape[:2]

    #位置を算出
    left = math.ceil(bounding_box_data['Left'] * imgWidth)
    top = math.ceil(bounding_box_data['Top'] * imgHeight)
    width = math.ceil(bounding_box_data['Width'] * imgWidth)
    height = math.ceil(bounding_box_data['Height'] * imgHeight)

    image_file = cv2.rectangle(image_file
                            , (left, top)
                            , (width+left, height+top)
                            , (0,255,0)
                            , 2)

    return image_file

#Bounding Boxの左下に名前を設定して返す
def set_celebrity_name(image_file, celebrity_name, boundingBox):
    #画像の高さと幅を取得
    imgHeight, imgWidth = image_file.shape[:2]

    left = math.ceil(boundingBox['Left'] * imgWidth)
    top = math.ceil(boundingBox['Top'] * imgHeight)
    height = math.ceil(boundingBox['Height'] * imgHeight)

    image_file = cv2.putText(image_file
                            , celebrity_name
                            , (left, height+top)
                            , cv2.FONT_HERSHEY_PLAIN
                            , 2
                            , (255, 255, 255)
                            , 2
                            , cv2.LINE_AA)

    return image_file


if __name__ == '__main__':
    if len(sys.argv) != 3:
        exit()
    
    in_file_name = sys.argv[1]
    out_file_name = sys.argv[2]

    try:
        #顔分析
        #jsonが無ければ分析あれば使う
        json_file_name = in_file_name + '.json'
        if (os.path.exists(json_file_name) == True):
            file = open(json_file_name, 'r')
            response = file.read()
            response = json.loads(response)
        else:
            response = recognize_celebrities(in_file_name)
            file = open(json_file_name, 'w')
            file.write(json.dumps(response))

        #分析元の画像を読み込み
        origin_image = cv2.imread(in_file_name)
        after_image = origin_image.copy()
        
        #画像に分析結果の設定
        for celebrity in response['CelebrityFaces']:
            after_image = set_bounding_box(after_image, celebrity['Face']['BoundingBox'])
            after_image = set_celebrity_name(after_image, celebrity['Name'], celebrity['Face']['BoundingBox'])
            
        #結果の出力
        if after_image is not None:
            cv2.imwrite(out_file_name, after_image)


    except Exception as e:
        print(e)
        print(traceback.format_exc())

実行方法

python recognize_celebrities.py 分析したいファイル.jpg 出力したいファイル名.jpg
  • 実行結果 f:id:halhalhal1:20210426211733j:plain

BoundingBoxを設定して、名前を設定してみた。
動作はほぼ共通化していて、流用が簡単にできるのでAmazonAPIが共通的に作られているかがわかった。
やっぱ、似たようなAPIは似たような構造になるんだよな、普通。


まとめ

今までの集大成で一気に書き上げることができた。
text設定とかはもうちょっとうまくメソッド化できそうだなぁ。