解説記事:Python

最終更新日:

Python についての簡単な紹介。


環境設定

Visual Studio Codeの場合


データ構造(リスト、辞書、集合、タプル)

データ構造 概要 表記例
リスト 一般的な配列に使う ['a', 'b']
辞書 連想配列として使う {'a':1, 'b':2}
集合 重複を許さないリスト。検索が高速。 {'a', 'b'}
タプル 挿入順を保証できるリスト。変更できないリスト。 ('a', 'b')

リストはスライスできる。

stepにマイナスを指定すると逆順に参照できる。

startにマイナスを指定すると後ろからの文字数を指定できる。

letters[start:stop:step]


Pythonの辞書はキーと値のペア(kv:key/value pair)。変更可能なハッシュテーブルとして実装されている。


C++とJavaでは辞書は「マップ」と呼ばれる。

PerlとRubyでは辞書は「ハッシュ」と呼ばれる。

person = { '名前': 'Taro Yamada', '性別': '男性', '職業': '研究者'}


辞書を反復処理する

for k, v in sorted(person.items()): print(k, 'の値は', v)


集合は重複を取り除く(キーと値は存在しない。setで集合に変換。unionで集合を結合。differenceは差集合。intersectionは積集合)

u = word1.union(set(word2))

d = word1.difference(set(word2))

i = word1.intersection(set(word2))

注:空の集合はset()と表現される


データを変更しない場合はタプルを使う(要素が1つだけのときは必ず,を後ろにつける。そうしないとタプルにならないので注意。)

t = ('Python', )


辞書の辞書を作ることも可能(pprintモジュールを使うと、整形して表示できる)

import pprint

dict['ja'] = {'表示':'日本語', '英語':'japanese'}

dict['en'] = {'表示':'英語', '英語':'english'}

pprint.pprint(dict)


zip、アンパック、enumerate(要素の値とインデックスの組み合わせ)

#結合
for d in zip(city, sale):
    print(d)

#分解
for c, s in zip(city, sale):
    print("city:", c, " sales:", s)

#要素とインデックスを組み合わせ
for d in enumerate(city):
    print(d)
        

リストを集計する

max(リスト) 最大値を求める

min(リスト) 最小値を求める

sum(リスト) 合計値を求める

sorted(リスト) ソートする(reverse=Trueとすると逆順ソート)


関数定義

関数直下の"""で囲まれた箇所は説明文。bool関数はTrue/Falseを返す。戻り値は複数の値を返すことも可能。

def search4vowels(word):
    """入力された単語内の母音を表示する"""
    vowels = set('aeiou')
    found = vowels.intersection(set(word))
    return bool(found)
        

Python3からアノーテーションにより引数・戻り値の型を表現できる(オプション)。

word:strは文字列を引数に取る、-> setは戻り値が集合(set)であることを示す。

def search4vowels(word: str) -> set:
    """指定された単語内の母音を表示する"""
    return set('aeiou').intersection(set(word))
        

デフォルト引数を指定できる。

def search4vowels(word: str, letters: str='aeiou') -> set:
    """入力された単語内の母音を表示する"""
    return set(letters).intersection(set(word))
        

呼び出し側でキーワード引数を指定できる。

このとき引数の順序は問題にならない。

serch4vowels(letters='xyz', word='abc')
        

Pythonはオブジェクト参照渡し

代入文があるとオブジェクトが上書きされる。

代入文がなく、不変なデータでなければ、オブジェクトが変更される。

def double(arg):
    print('実行前':, arg)
    arg = arg * 2  #オブジェクト上書き
    print('実行後:', arg)

def change(arg):
    print('実行前':, arg)
    arg.append('追加データ')
    print('実行後:', arg)

num = 10
double(num)
print(num) # データは更新されていないように見える

numbers = [1, 2, 3]
change(numbers)
print(numbers) # 追加データが見える
        

lamda関数(無名関数)

data = [1,2,3,4,5]
for d in map(lambda x: x*2, data):
    print(d)
        

リスト内包表記でも同じ

            data = [1,2,3,4,5]
            for d in map(x*2 for x in data):
                print(d)
        

モジュール

関数を持つファイルがモジュールになる。

import ファイル名(拡張子.pyを除く)で参照できる


モジュールは、作業ディレクトリ、site-packages、標準ライブラリ、のいずれかに置く。


Python3.4以降ではsetuptoolsモジュールを使うと簡単に配布パッケージが作成できる

setup.pyとREADME.txt(空でもよい)の2つがあれば良い

py_modulesにはパッケージに入れる.pyファイルのリストを指定する。

from setuptools import setup
setup(
    name='testmodule',
    version='1.0',
    description='test module',
    author='author',
    author_email='xxx@xxx.com',
    url='xxx.com',
    py_modules=['testmodule']
)
        

配布パッケージの作成(Windows)。

sdistはソース配布ファイルに結合することを指定している。ソースコードを含む。

py -3 setup.py sdist


配布パッケージの作成(Linux)

python3 setup.py sdist


pipによるパッケージのインストール

py -3 -m pip install testmodule-1.0.zip (Windows)

sudo python3 -m pip install testmodule-1.0.tar.gz (Linux)


第3者と共有したい場合、配布パッケージをPyPIというWebベースの集中管理型ソフトウェアリポジトリにアップロードすることができる。


テストツールとPEP8準拠

py -3 -m pip install pytest

py -3 -m pip install pytest-pep8

py.test --pep8 testmodule.py


Webアプリケーション

Flaskのインストール

py -3 -m pip install Flask

サンプルコード(webappフォルダにhello_flask.pyで保存する)

"@app.route('/')"は関数デコレータで関数のコードを変更せずに既存の関数の振る舞いを追加する。

下記の例では、url '/'をhelloにマッピングしている

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello() -> str:
    return 'Hello world from Flask!'

app.run()
        

py -3 hello_flask.pyで実行(終了はCtrl+C)


Webページにはテンプレートを使う(jinja2)

base.html

<!doctype html>
<html>
    <head>
        <title>{{ the_title }}</title>
        <link rel="stylesheet" href="static/xxx.css" />
        <body>
            {% block body %}

            {% end body %}
        </body>
    </head>
</html>
        

entry.html

{% extends 'base.html' %}
{% block body %}
<h2>{{ the_title }}</h2>
<form method="POST" action='/serach4'>
<table>
    <p>このフォームを使って検索リクエストを送ってください。</p>
    <tr>
        <td>フレーズ:</td>
        <td><input name='phrase' type="text" width='60'></td>
    </tr>
    <tr>
        <td>文字:</td>
        <td><input name='letters' type="text" value='aeiou'></td>
    </tr>
</table>
<p>準備ができたら、以下のボタンを押してください。</p>
<p><input value='実行' type="text"></p>
</form>
{% end body %}
        

result.html

{% extends 'base.html' %}
{% block body %}
<h2>{{ the_title }}</h2>
<p>以下のデータを送信しました。</p>
<table>
    <p>このフォームを使って検索リクエストを送ってください。</p>
    <tr>
        <td>フレーズ:</td>
        <td>{{ the_phrase }}</td>
    </tr>
    <tr>
        <td>文字:</td>
        <td>{{ the_letters }}</td>
    </tr>
</table>
<p>{{ the_phrase }}から{{ the_letters }}を検索すると、次の結果を返します。</p>
<h3>{{ the_results }}</h3>
</form>
{% end body %}
        

サンプルコード(テンプレート使用)

@app.routeのデフォルトは"GET"メソッド。

@app.route('/entry', methods=['GET', 'POST']とするとGET,POSTの両方に対応できる。

app.run(debug=True)とすると変更時に自動リロードしてくれる

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/entry', methods=['POST'])
def entry_page() -> str:
    return render_template('entry.html', the_title='Hello world from Flask!')

app.run()
        

フォルダ構成

webapp -- test.py

webapp -- static --- xxx.css

webapp -- templates --- base.html, entry.html, results.html


リクエストデータの処理(requestパッケージ)

            from flask import Flask, render_template, request

            app = Flask(__name__)
            
            @app.route('/search', methods=['POST'])
            def entry_page() -> str:
                phrase = request.form['phrase']
                letters = request.form['letters']
                return str(search(phrase, letters))
            
            app.run()
        

リクエストデータのリダイレクト(redirectパッケージ)・・・不要

'302'はリダイレクトを示す。

            from flask import Flask, render_template, request, redirect

            app = Flask(__name__)
            
            @app.route('/')
            def hello() -> '302':
                return redirect('/entry')
            
            app.run()
        

リクエストデータのリダイレクト

@app.routeに複数のURLをマッピングすればよい

            from flask import Flask, render_template, request

            app = Flask(__name__)
            
            @app.route('/')
            @app.route('/entry')
            def hello() -> str:
                return hello()
            
            app.run()
        

ローカル実行との区別

__name__と__main__を使う

            if __name__ == '__main__':
                app.run()
        

ファイル操作

ファイルのオープン、処理、クローズ:

tasks = open('todo.txt')
for task in tasks:
    print(task, end='')
tasks.close()
        

ファイルのオープン、処理、クローズ(with):デフォルトはReadモード。

自動でクローズ処理をしてくれる。

with open('todo.txt') as tasks:
    for task in tasks:
        print(task, end='')
        

ファイルのオープン、処理、クローズ(with):Writeモード。

戻り値が無いことを示すのにNoneを使う

def log_request(req: 'falsk_request', res: str) -> None:
    with open('vsearch.log', 'a') as log:
        print(req.form, req.remote_addr, req.user_agent, res, file=log, sep='|')
        

ファイルのオープン、処理、クローズ(with):ログ表示。

escape関数でHTMLタグをエスケープする(from falsk import escapeが必要)

@app.route('/viewlog')
def view_the_log() -> str:
    with open('vsearch.log') as log:
        contents = log.read()
    return escape(contents)
        

ファイルのオープン、処理、クローズ(with):ログ表示。

splitとappendを使ってlistに登録

@app.route('/viewlog')
def view_the_log() -> str:
    contents = []
    with open('vsearch.log') as log:
        for line in log:
            contents.append([])
            for item in line.split('|'):
                contents[-1].append(escape(item))
    return str(contents)
        

ファイルのオープン、処理、クローズ(with):ログ表示。HTML形式。

@app.route('/viewlog')
def view_the_log() -> str:
    contents = []
    with open('vsearch.log') as log:
        for line in log:
            contents.append([])
            for item in line.split('|'):
                contents[-1].append(escape(item))
    titles = ('フォームデータ', 'リモートアドレス', 'ユーザエージェント', '結果')
    return render_template('viewlog.html', the_title='ログの閲覧', the_row_title=titles, the_data=contents,)
        

HTML形式。tableで表示。viewlog.htmlとして保存。

            {% extends 'base.html' %}

            {% block body %}
            
            <h2>{{ the_title }}</h2>
            
            <table>
                <tr>
                    {% for row_title in the_row_titles %}
                        <th>{{row_title}}</th>
                    {% endfor %}
                </tr>
                {% for log_row in the_data %}
                    <tr>
                        {% for item in log_row %}
                            <td>
                                {{item}}
                            </td>
                        {% endfor %}
                    </tr>
                {% endfor %}
            </table>
            
            {% endblock %}
        

CSVファイル

import csv
f = open("Sasmple.csv", "r")
rd = csv.reader(f)
for row in rd:
    for col in row:
        print(col, end=",")
    print()
f.close()
        
import csv
f = open("Sasmple.csv", "w")
w = csv.writer(f)
w.writerow(["ABC", "DEF"])
w.writerows([["ABC", "DEF"],["GHI", "JKL"]])
        

JSONファイル

import json
f = open("Sasmple.json", "r")
data = json.load(f)
print(data)
f.close()
        
json.dump({"ABC":10, "DEF":20}, f)
        

データベース操作

MySQLのインストール


MySQL-Connector/Pythonのインストール

py -3 -m pip install mysql-connector-python


Webアプリケーションのデータベースとテーブルを作成する

mysql> create database vsearchlogDB;

mysql> grant all on vsearchlogDB.* to 'vsearch' identified by 'vsearchpasswd';

mysql> quit


> mysql -u vsearch -p vsearchlogDB

mysql> create table log(
    -> id int auto_increment primary key,
    -> ts timestamp default current_timestamp,
    -> phrase varchar(128) not null,
    -> letters varchar(32) not null,
    -> ip varchar(16) not null,
    -> browser_string varchar(256) not null,
    -> results varchar(64) not null);
        

データベースにログ追加

def log_request(req: 'flask_request', res: str) -> None:
"""Webリクエストの詳細と結果をロギングする。"""
dbconfig = { 'host': '127.0.0.1',
            'user': 'vsearch',
            'password': 'vsearchpasswd',
            'database': 'vsearchlogDB', }

conn = mysql.connector.connect(**dbconfig)
cursor = conn.cursor()

_SQL = """insert into log(phrase, letters, ip, browser_string, results) values (%s, %s, %s, %s, %s)"""
cursor.execute(_SQL, (req.form['phrase'],
                        req.form['letters'],
                        req.remote_addr,
                        req.user_agent.browser,
                        res, ))

conn.commit()
cursor.close()
conn.close()
        

クラス

__init__は初期化の特殊メソッド

クラスメソッドの第1引数は必ずselfをつける

class CountFromBy:
    def __init__(self, v: int=0, i: int=1) -> None:
        self.val = v
        self.incr = i

    def increase(self) -> None:
        self.val += self.incr

    def __repr__(self) -> str:
        return str(self.val)
        

コンテキストマネジメントプロトコル

__enter__は前処理を行う。

__exit__は後処理を行う。

__init__は初期化を行う。

__init__ -> __enter__ -> __exit__ の順で呼ばれる


データベースクラス

import mysql.connector

class UseDatabase:

    def __init__(self, config: dict) -> None:
        self.configuration = config

    def __enter__(self) -> 'cursor':
        self.conn = mysql.connector.connect(**self.configuration)
        self.cursor = self.conn.cursor()
        return self.cursor

    def __exit__(self, exc_type, exc_value, exc_trace) -> None:
        self.conn.commit()
        self.cursor.close()
        self.conn.close()            
        

クラスを使ったwith構文に置き換える

app.config['dbconfig'] = { 'host': '127.0.0.1',
            'user': 'vsearch',
            'password': 'vsearchpasswd',
            'database': 'vsearchlogDB', }

def log_request(req: 'flask_request', res: str) -> None:
"""Webリクエストの詳細と結果をロギングする。"""
with UseDatabase(app.config['dbconfig']) as cursor:
    _SQL = """insert into log(phrase, letters, ip, browser_string, results) values (%s, %s, %s, %s, %s)"""
    cursor.execute(_SQL, (req.form['phrase'],
                        req.form['letters'],
                        req.remote_addr,
                        req.user_agent.browser,
                        res, ))            
        

データベースから取得した情報を表示する

@app.route('/viewlog')
def view_the_log() -> str:
    with UseDatabase(app.config['dbconfig']) as cursor:
        _SQL = """select phrase, letters, ip, browser_string, results from log"""
        cursor.execute(_SQL)
        contents = cursor.fetchall()
    titles = ('フレーズ', 'フォームデータ', 'リモートアドレス', 'ユーザエージェント', '結果')
    return render_template('viewlog.html', the_title='ログの閲覧', the_row_titles=titles, the_data=contents,)            
        

いくつのリクエストに応答したか

select * from log;

select count(*) from log;


もっとも一般的な検索文字のリストは何か

select count(letters) as 'count', letters from log

group by letters

order by count desc

limit 1;


リクエストがどのIPアドレスから来たか

select distinct ip from log;


最もよく使われているブラウザは何か

select browser_string, count(browser_string) as 'count' from log

group by browser_string

order by count desc

limit 1;


クラス変数

selfのつかない変数はクラスで共通。


クラスメソッド

@classmethodの下に定義する。

clsはクラス名を参照するのに用いる

@classmethod
def getCount(cls):
    return cls.count
        

親クラスのメソッド呼び出し

super().親クラスのメソッド()という形式で呼び出す。


関数デコレータ

セッション(session)

from flask import Flask, session

app = Flask(__name__)

app.secret_key = 'YouWillNeverGuess'

@app.route('/setuser/')
def setuser(user: str) -> str:
    session['user'] = user
    return 'User値を設定:' + session['user']

@app.route('/getuser')
def getuser() -> str:
    return 'User値の現在の設定:' + session['user']

if __name__ == '__main__':
    app.run(debug=True)            
        

可変長の引数

*は「引数のリストを受け取る」という意味。ゼロ個以上の引数。

def myfunc(*args):
    for a in args:
        print(a, end=' ')            
    if args:
            print()
        

呼び出し時のリスト展開

*を指定すると引数のリストに展開される

values = [1, 2, 3, 5, 7, 11]
myfunc(values)
myfunc(*values)
        

任意のキーワード引数を受け取る

**はキーワード引数

def myfunc2(**kwargs):
    for k, v in kwargs.items():
        print(k, v, sep='->', end=' ')
    if kwargs:
            print()
        

デコレータ functoolsのwraps関数

独自のデコレータを作成する場合

from flask import session
from functools import wraps
def check_logged_in(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        if 'logged_in' in session:
            return func(*args, **kwargs):
        return 'ログインしていません。'
    return wrapper
        

デコレータを利用

デコレータをインポートする

@構文でデコレータを使う

from checker import checker_logged_in

@app.route('/page1')
@checker_logged_in
def page1() -> str:
    return 'これはページ1です。'
        

デコレータを作成するためのコードテンプレート

from functools import wraps

def decorator_name(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 1. デコレートされる関数を呼び出す前に実行するコードを各。

        # 2. 必要に応じてデコレートされる関数を呼び出し、必要なら結果を返す。
            return func(*args, **kwargs)
        
        # 3. デコレートされる関数を呼び出す代わりに実行するコードを書く。
    return wrapper            
        

例外処理

try-except

try:
    with open('myfile.txt') as fh:
        file_data = fh.read()
    print(file_data)
except FileNotFoundError:
    print('データファイルがありません。')
except PermissionError:
    print('許可されていません。')
except Exception as err:
    print('他のエラーが発生しました。', str(err))
    #err = sys.exc_info()
    #for e in err:
    #    print(e)
else:
    #例外が起きなかったときに処理する文
    ...
finally:
    #必ず最後に処理する文
    ...
        

カスタム例外を作成する

from DBcm import UseDatabase, ConnectionError, CredentialsError
class ConnectionError(Exception):
            pass

raise ConnectionError('接続できません。')
try:
    raise ConnectionError('エラー')
except ConnectionError as err:
    print('データベース接続エラー:', str(err))
except CredentialsError as err:
    print('ユーザーID/パスワード エラー:', str(err))
except Exception as err:
    print('その他のエラー:', str(err))
return 'Error'
        

SQLErrorを投げる

class SQLError(Exception):
    pass

class UseDatabase:

def __enter__(self) -> 'cursor':
    try:
    ...
    self.conn.close()
    if exc_type is mysql.connector.errors.ProgrammingError:
        raise SQLError(exc_value)
    elif exc_type:
        raise exc_type(exc_value)    

def __exit__(self, exc_type, exc_value, exc_traceback):
    self.conn.commit()
    self.cursor.close()
    self.conn.close()
    if exc_type is mysql.connector.errors.ProgrammingError:
        raise SQLError(exc_value)
        

スレッド

from threading import thread
try:
    t = Thread(target=execute_slowly, args=(glacial, plodding, leaden))
    t.start()
except Exception as err:
    print('**** ロギングが失敗しました:', str(err))
        

イタレーション

from datetime import datetime
import pprint

def convert2ampm(time24: str) -> str:
    return datetime.strptime(time24, '%H:%M').strftime('%I:%M%p')

with open('buzzers.csv') as data:
    ignore = data.readline()
    flights = {}
    for line in data:
        k, v = line.strip().split(',')
        flights[k] = v

pprint.pprint(flights)
print()

flights2 = {}
for k, v in flights.items():
    flights2[convert2ampm(k)] = v.title()

pprint.pprint(flights2)
        

内包表記のパターン

内包表記の方がforループより高速

destinations = []
for dest in flights.values()
    destinations.append(dest.title())
--->
more_dests = [dest.title() for dest in flights.values()]
        

リスト内包表記

data = [1,2,3,4,5]
ndata = [n*2 for n in data if n!= 3]
        

ジェネレータ

()で囲まれた内包表記のように見えるもの

データ生成の度に呼ばれるので待ち時間がない

for i in (x*3 for x in [1,2,3,4,5]):
    print(i)
        

ジェネレータ(yeild)

returnで終了せずに、待機して復帰する

import requests
def gen_from_urls(urls: tuple) -> tuple:
    for resp in (requests.get(url) for url in urls):
        yeild len(resp.content), resp.status_code, resp.url
        

正規表現

import re
ptr = "\.(csv|html|py)$"
str = ["Sample.csv", "Sample.exe", "test.py", "index.html"]
pattern = re.compile(ptr)
for valuestr in str:
    res = pattern.search(valuestr)
    if res is not None:
        print("found")
    else:
        print("not found")

for valuestr in str:
    res = pattern.sub(".txt", valuestr)
    mst = "変換前:" + valuestr + " 変換後:" + res
    print(msg)
        

Webスクレイピング

モジュール概要
webbrowserPython付属のモジュール。指定したページをブラウザで開く。
RequestsインターネットからファイルやWebページをダウンロードする。
Beautiful SoupHTMLをパースする
SeleniumWebブラウザを起動し制御する。ブラウザ上のフォームを記入したり、マウスクリックをシミュレートできる。


webbrowserの例(Google Mapで地図を表示する)

(引数で住所を指定するか、メモ帳などに記載した内容をCtrl+Cでコピーした状態で引数なしで起動する)

py -3 -m pip install pyperclipでpyperclipをインストールする必要あり。

import webbrowser, sys, pyperclip

if len(sys.argv) > 1:
    address = ' '.join(sys.argv[1:])
else:
    address = pyperclip.paste()

webbrowser.open('https://www.google.co.jp/maps/place/' + address)

requestsの例

py -3 -m pip install requestsでモジュールをインストール

ファイル名はrequests.py以外にすること

import requests
    res = requests.get('http://automatetheboringstuff.com/files/rj.txt')
    print(type(res))
    res.raise_for_status()
    if (res.status_code == requests.codes.ok):
        print(len(res.text))
        print(res.text[:250])
        play_file = open('RomeoAndJuliet.txt', 'wb')
        for chunk in res.iter_content(100000):
            play_file.write(chunk)
        play_file.close()
    print('exit')

BeautifulSoupの例

pip install beautifulsoup4でモジュールをインストール

import requests, bs4
res = requests.get('http://suoyimi.blog.jp/')
res.raise_for_status()
bs4res = bs4.BeautifulSoup(res.text, features="html.parser")
elems = bs4res.select('p')
print(type(elems))
print(len(elems))
length = len(elems)
for elem in elems:
    print(elem.attrs)
    print(elem.getText())

Google検索結果の上位5ページを開く例

import requests, sys, webbrowser, bs4

    print('Googling...')
    url = 'http://www.google.com/search?q=' + ''.join(sys.argv[1:])
    print(url)
    res = requests.get(url)
    res.raise_for_status()
    
    html_file = open('example.html', 'w', encoding='utf-8')
    html_file.write(res.text)
    html_file.close()
    
    soup = bs4.BeautifulSoup(res.text, features="html.parser")
    
    link_elems = soup.select('.kCrYT a')
    print(link_elems)
    print(len(link_elems))
    
    num_open = min(5, len(link_elems))
    print(num_open)
    for i in range(num_open):
        webbrowser.open('http://www.google.com' + link_elems[i].get('href'))

コミック画像のダウンロードの例

import requests, os, bs4

url = 'http://xkcd.com'
os.makedirs('xkcd', exist_ok=True)

while not url.endswith('#'):
    # ページをダウンロードする
    print('ページをダウンロード中 {}...'.format(url))
    res = requests.get(url)
    res.raise_for_status()

    soup = bs4.BeautifulSoup(res.text)

    # コミック画像のURLを見つける
    comic_elem = soup.select('#comic img')
    if comic_elem == []:
        print('コミック画像が見つかりませんでした。')
    else:
        comic_url = 'http:' + comic_elem[0].get('src')
        # 画像をダウンロードする
        print('画像をダウンロード中 {}...'.format(comic_url))
        res = requests.get(comic_url)
        res.raise_for_status()

        # 画像を./xkcdに保存する
        image_file = open(os.path.join('xkcd', os.path.basename(comic_url)), 'wb')
        for chunk in res.iter_content(100000):
            image_file.write(chunk)
        image_file.close()

    # PrevボタンのURLを取得する
    prev_link = soup.select('a[rel="prev"]')[0]
    url = 'http://xkcd.com' + prev_link.get('href')

print('完了')

Seleniumでブラウザ制御の例

pip install seleniumでモジュールをインストール

chromedriver.exeをダウンロードしてパスの通った場所に置く

https://chromedriver.chromium.org/downloads

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
browser = webdriver.Chrome()
print(type(browser))
browser.get('http://inventwithpython.com')
link_elem = browser.find_element_by_link_text('Read Online for Free')
print(type(link_elem))
link_elem.click()
html_elem = browser.find_element_by_tag_name('html')
html_elem.send_keys(Keys.END)
browser.refresh()
browser.back()

pyinstallerでseleniumをchromedriverと一緒にパッケージ化する場合は、以下のようにコマンドを実行する

add-binaryは"コピー元;コピー先"となっている。

pyinstaller ./main.py --onefile --noconsole --add-binary "./driver/chromedriver.exe;./driver"    

pythonスクリプトのパス指定も以下のように行う。

sys._MEIPASSにはexeファイルからの実行時に一時フォルダのパスが入る。

def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.dirname(__file__)
    return os.path.join(base_path, relative_path)

driver = webdriver.Chrome(resource_path('./driver/chromedriver.exe'),options=options)    

フォームを記入して送信する

from selenium import webdriver
login_username = 'username'
login_passwd = 'password'
browser = webdriver.Chrome()
browser.get('http://localhost/redmine/login')
email_elem = browser.find_element_by_id(login_username)
email_elem.send_keys('not_my_real_email')
password_elem = browser.find_element_by_id(login_passwd)
password_elem.send_keys('12345')
password_elem.submit()

proxy設定

> set HTTP_PROXY=http://{プロキシーサーバ}
> set HTTPS_PROXY=http://{プロキシーサーバ}
> pip install pyinstaller --proxy=http://{プロキシーサーバ}

実行ファイルの作成

main.pyがスクリプト名。 openpyxlなどを使用する場合、main.pyにしないとエラーになる可能性がある。 Testが出力実行ファイル名。

pip install pyinstaller
pyinstaller main.py --onefile --noconsole -n Test

仮想環境

プロジェクト毎に環境を設定する。 pyinstallerで実行ファイルサイズを小さくする目的で用いる。

pip install pipenv
cd exe(フォルダ名)
pipenv --python 3.9
pipenv shell
pipenv install 必要ライブラリ

パッケージインストーラ(pip)

パッケージインストーラ。 ”pip install” と “python -m pip install”で動作が異なる場合があることに注意。 python -m pipとした場合は実行ファイルpythonに対してパッケージをインストールする。 python -m pipを使った方が無難。

参考:https://daeudaeu.com/python-pip-difference/


参考資料

  • Head First Python 第2版
  • やさしいPython
  • 退屈なことはPythonにやらせよう(http://automatetheboringstuff.com/)