├── .gitignore ├── README.md ├── appendix-D ├── checktoken.py └── checktoken2.py ├── automate-the-boring-stuff-with-python.png ├── ch01 └── hello.py ├── ch02 ├── exitExample.py ├── fiveTimes.py ├── fiveTimes2.py ├── infiniteloop.py ├── littleKid.py ├── printRandom.py ├── swordfish.py ├── vampire.py ├── vampire1.py ├── vampire2.py ├── yourName.py └── yourName2.py ├── ch03 ├── guessTheNumber.py ├── helloFunc.py ├── helloFunc2.py ├── magic8Ball.py ├── sameName.py ├── sameName2.py ├── sameName3.py ├── sameName4.py ├── test3784.py └── zeroDivide.py ├── ch04 ├── allMyCats1.py ├── allMyCats2.py ├── magic8Ball2.py ├── myPets.py └── passingReference.py ├── ch05 ├── birthdays.py ├── characterCount.py ├── fullTicTacToe2.py ├── fullTicTacToeOrg.py ├── picnic.py ├── prettyCharacterCount.py ├── ticTacToe.py └── ticTacToe2.py ├── ch06 ├── bulletPointAdder.py ├── catnapping.py ├── picnicTable.py ├── pw.py └── validateInput.py ├── ch07 ├── JPhoneAndEmail.py ├── isPhoneNumber.py └── phoneAndEmail.py ├── ch08 ├── mcb.pyw ├── mcb.vbs └── randomQuizGenerator.py ├── ch09 ├── backupToZip.py ├── delicious │ ├── cats │ │ ├── catnames.txt │ │ └── zophie.jpg │ ├── spam.txt │ └── walnut │ │ └── waffles │ │ └── butter.txt ├── example.zip └── renameDates.py ├── ch10 ├── boxPrint.py ├── buggyAddingProgram.py ├── buggyAddingProgramFixed.py ├── coinFlip.py ├── errorExample.py ├── factorialLog.py └── factorialLogFixed.py ├── ch11 ├── downloadXkcd.py ├── example.html ├── lucky.py └── mapIt.py ├── ch12 ├── censuspopdata.xlsx ├── example.xlsx ├── produceSales.xlsx ├── readCensusExcel.py └── updateProduce.py ├── ch13 ├── combinePdfs.py ├── demo.docx ├── dictionary.txt ├── encrypted.pdf ├── guests.txt ├── meetingminutes.pdf ├── meetingminutes2.pdf ├── readDocx.py ├── watermark.pdf └── zophie.png ├── ch14 ├── example.csv ├── excelSpreadsheets.zip ├── quickWeather.py ├── removeCsvHeader.py └── removeCsvHeader.zip ├── ch15 ├── alarm.wav ├── calcProd.py ├── countdown.py ├── multidownloadXkcd.py ├── stopwatch.py └── threadDemo.py ├── ch16 ├── duesRecords.xlsx ├── jimapsample.py ├── jsmtpsample.py ├── sendDuesReminders.py ├── textmyself.py └── torrentStarter.py ├── ch17 ├── catlogo.png ├── fontlist.py ├── guests.txt ├── resizeAndAddLogo.py └── zophie.png ├── ch18 ├── formFiller.py ├── mouseNow.py ├── mouseNow2.py └── spiralDraw.py └── practice_pj └── practice_projects_20170727.zip /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 退屈なことはPythonにやらせよう 2 | 3 | --- 4 | 5 | ![表紙](automate-the-boring-stuff-with-python.png) 6 | 7 | --- 8 | 9 | 本リポジトリはオライリー・ジャパン発行書籍『[退屈なことはPythonにやらせよう](https://www.oreilly.co.jp/books/9784873117782/)』(原書名『[Automate the Boring Stuff with Python](https://www.nostarch.com/automatestuff)』) のサポートサイトです。 10 | 11 | ## サンプルコード 12 | 13 | サンプルコードの解説は本書籍をご覧ください。 14 | 15 | ## 【新規】演習プロジェクトの解答例 16 | 17 | 読者の皆様のご要望が多かったため、演習プロジェクトの解答例を追加しました。 18 | (https://github.com/oreilly-japan/automatestuff-ja/blob/master/practice_pj/practice_projects_20170727.zip) 19 | 20 | ご購読者の特典とするため、ZIPファイルにはパスワードを設定してあります。 21 | パスワードは、11.3.4節の本文に現れる最初の英単語3つをつなげたものです。 22 | 23 | 解答例は、以下の注意点をご了承の上お使いください。 24 | 25 | - 訳者が独自に作成したものです。原著者のAl Sweigart氏には問い合わせないでください。 26 | - 動作保証はしません。サンプルとしてお使いください。 27 | - 演習プロジェクトのすべての要求を満たすものではありません。要求があいまいなものや、自由度が多すぎるものについては、適当に端折っています。 28 | - IDやパスワード、APIキーなど、読者の環境に合わせて改変が必要なものがあります。 29 | - 不定期に更新されることがあります。 30 | - ZIPのパスワードは不定期に変更されることがあります。 31 | 32 | ## 正誤表 33 | 34 | 本書の正誤情報は以下のページで公開しています。 35 | 36 | https://github.com/oreilly-japan/automatestuff-ja/wiki/errata 37 | 38 | 本ページに掲載されていない誤植など間違いを見つけた方は、[japan@oreilly.co.jp]()までお知らせください。 39 | -------------------------------------------------------------------------------- /appendix-D/checktoken.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import glob 5 | import re 6 | from collections import Counter 7 | 8 | counter = Counter() 9 | token_re = re.compile(r'([一-龥]+|[A-Za-z][0-9A-Za-z_]*|[ァ-ヾ]+)') 10 | 11 | for fname in glob.glob('ch*.md'): 12 | print(fname) 13 | with open(fname, 'r', encoding='utf-8') as f: 14 | for line in f: 15 | if line.startswith('> ■'): 16 | continue 17 | tokens = token_re.findall(line) 18 | counter.update(tokens) 19 | 20 | for k in sorted(counter.keys()): 21 | print(k + ',' + str(counter[k])) 22 | 23 | -------------------------------------------------------------------------------- /appendix-D/checktoken2.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import glob 5 | import re 6 | from collections import deque 7 | 8 | token_re = re.compile(r'([一-龥]+|[A-Za-z][0-9A-Za-z_]*|[ァ-ヾ]+)') 9 | token_pos = {} 10 | 11 | for fname in glob.glob('ch*.md'): 12 | print(fname) 13 | with open(fname, 'r', encoding='utf-8') as f: 14 | n = 0 15 | for line in f: 16 | n += 1 17 | if line.startswith('> ■'): 18 | continue 19 | tokens = token_re.findall(line) 20 | for t in tokens: 21 | token_pos.setdefault(t, []) 22 | token_pos[t].append(fname + ':' + str(n)) 23 | 24 | last_tokens = deque([ ('', 0) ] * 3) 25 | 26 | def push_and_print(t, length): 27 | last_tokens.popleft() 28 | last_tokens.append((t, length)) 29 | if last_tokens[1][1] == 1: 30 | t1 = last_tokens[1][0] 31 | print(t1 + ',' + str(token_pos[t1][0]), end=',') 32 | print(str(last_tokens[0]) + ',' + str(last_tokens[2])) 33 | 34 | for t in sorted(token_pos.keys()): 35 | push_and_print(t, len(token_pos[t])) 36 | 37 | push_and_print('', 0) 38 | 39 | -------------------------------------------------------------------------------- /automate-the-boring-stuff-with-python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/automate-the-boring-stuff-with-python.png -------------------------------------------------------------------------------- /ch01/hello.py: -------------------------------------------------------------------------------- 1 | # このプログラムは挨拶を表示して名前と年齢を尋ねる ❶ 2 | 3 | print('Hello world!') # ❷ 4 | print('What is your name?') # 名前を尋ねる 5 | my_name = input() # ❸ 6 | print('It is good to meet you, ' + my_name) # ❹ 7 | print('The length of your name is:') # 名前の長さを表示 ❺ 8 | print(len(my_name)) 9 | print('What is your age?') # 年齢を尋ねる ❻ 10 | my_age = input() 11 | print('You will be ' + str(int(my_age) + 1) + ' in a year.') # 来年の年齢を表示 12 | -------------------------------------------------------------------------------- /ch02/exitExample.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | while True: 4 | print('終了するにはexitと入力してください。') 5 | response = input() 6 | if response == 'exit': 7 | sys.exit() 8 | print(response + 'と入力されました。') 9 | -------------------------------------------------------------------------------- /ch02/fiveTimes.py: -------------------------------------------------------------------------------- 1 | print('私の名前は') 2 | for i in range(5): 3 | print('石川五右衛門 (' + str(i) + ')') 4 | -------------------------------------------------------------------------------- /ch02/fiveTimes2.py: -------------------------------------------------------------------------------- 1 | print('私の名前は') 2 | i = 0 3 | while i < 5: 4 | print('石川五右衛門 (' + str(i) + ')') 5 | i = i + 1 6 | -------------------------------------------------------------------------------- /ch02/infiniteloop.py: -------------------------------------------------------------------------------- 1 | while True: 2 | print('Hello world!') 3 | -------------------------------------------------------------------------------- /ch02/littleKid.py: -------------------------------------------------------------------------------- 1 | print('名前を入力してください。') 2 | name = input() 3 | print('年齢を入力してください。') 4 | age = int(input()) 5 | 6 | if name == 'Alice': 7 | print('やぁ、Alice。') 8 | elif age < 12: 9 | print('Aliceじゃないね、お嬢ちゃん。') 10 | else: 11 | print('君はAliceでも子供でもないね。') 12 | -------------------------------------------------------------------------------- /ch02/printRandom.py: -------------------------------------------------------------------------------- 1 | import random 2 | for i in range(5): 3 | print(random.randint(1, 10)) 4 | -------------------------------------------------------------------------------- /ch02/swordfish.py: -------------------------------------------------------------------------------- 1 | while True: 2 | print('あなたはだれ?') 3 | name = input() 4 | if name != 'Joe': # ❶ 5 | continue # ❷ 6 | print('こんにちはJoe。パスワードは何?(魚の名前)') 7 | password = input() # ❸ 8 | if password == 'swordfish': 9 | break # ❹ 10 | print('認証しました。') # ❺ 11 | -------------------------------------------------------------------------------- /ch02/vampire.py: -------------------------------------------------------------------------------- 1 | print('名前を入力してください。') 2 | name = input() 3 | print('年齢を入力してください。') 4 | age = int(input()) 5 | 6 | if name == 'Alice': 7 | print('やぁ、Alice。') 8 | elif age < 12: 9 | print('Aliceじゃないね、お嬢ちゃん。') 10 | elif age > 2000: 11 | print('Ailceはお前のような不死身ではない、吸血鬼め。') 12 | elif age > 100: 13 | print('Aliceじゃないね、お婆ちゃん。') 14 | -------------------------------------------------------------------------------- /ch02/vampire1.py: -------------------------------------------------------------------------------- 1 | print('名前を入力してください。') 2 | name = input() 3 | print('年齢を入力してください。') 4 | age = int(input()) 5 | 6 | if name == 'Alice': 7 | print('やぁ、Alice。') 8 | elif age < 12: 9 | print('Aliceじゃないね、お嬢ちゃん。') 10 | -------------------------------------------------------------------------------- /ch02/vampire2.py: -------------------------------------------------------------------------------- 1 | print('名前を入力してください。') 2 | name = input() 3 | print('年齢を入力してください。') 4 | age = int(input()) 5 | 6 | if name == 'Alice': 7 | print('やぁ、Alice。') 8 | elif age < 12: 9 | print('Aliceじゃないね、お嬢ちゃん。') 10 | elif age > 100: # ❶ 11 | print('Aliceじゃないね、お婆ちゃん。') 12 | elif age > 2000: 13 | print('Ailceはお前のような不死身ではない、吸血鬼め。') 14 | -------------------------------------------------------------------------------- /ch02/yourName.py: -------------------------------------------------------------------------------- 1 | name = '' # ❶ 2 | while name != 'あなたの名前': # ❷ 3 | print('あなたの名前を入力してください。') 4 | name = input() # ❸ 5 | print('どうも!') # ❹ 6 | -------------------------------------------------------------------------------- /ch02/yourName2.py: -------------------------------------------------------------------------------- 1 | while True: # ❶ 2 | print('あなたの名前を入力してください。') 3 | name = input() # ❷ 4 | if name == 'あなたの名前': # ❸ 5 | break # ❹ 6 | print('どうも!') # ❺ 7 | -------------------------------------------------------------------------------- /ch03/guessTheNumber.py: -------------------------------------------------------------------------------- 1 | # 数当てゲーム 2 | import random 3 | secret_number = random.randint(1, 20) 4 | print('1から20までの数を当ててください。') 5 | 6 | # 6回聞く 7 | for guesses_taken in range(1, 7): 8 | print('数を入力してください。') 9 | guess = int(input()) 10 | 11 | if guess < secret_number: 12 | print('小さいです。') 13 | elif guess > secret_number: 14 | print('大きいです。') 15 | else: 16 | break # 当たり! 17 | 18 | if guess == secret_number: 19 | print('当たり!' + str(guesses_taken) + '回で当たりました!') 20 | else: 21 | print('残念。正解は' + str(secret_number) + 'でした。') 22 | -------------------------------------------------------------------------------- /ch03/helloFunc.py: -------------------------------------------------------------------------------- 1 | def hello(): # ❶ 2 | print('Howdy!') # ❷ 3 | print('Howdy!!!') 4 | print('Hello there.') 5 | 6 | hello() # ❸ 7 | hello() 8 | hello() 9 | -------------------------------------------------------------------------------- /ch03/helloFunc2.py: -------------------------------------------------------------------------------- 1 | def hello(name): # ❶ 2 | print('Hello ' + name) # ❷ 3 | 4 | hello('Alice') # ❸ 5 | hello('Bob') 6 | -------------------------------------------------------------------------------- /ch03/magic8Ball.py: -------------------------------------------------------------------------------- 1 | import random # ❶ 2 | 3 | def get_answer(answer_number): # ❷ 4 | if answer_number == 1: # ❸ 5 | return '確かにそうだ' 6 | elif answer_number == 2: 7 | return '間違いなくそうだ' 8 | elif answer_number == 3: 9 | return 'はい' 10 | elif answer_number == 4: 11 | return 'なんとも。もういちどやってみて' 12 | elif answer_number == 5: 13 | return 'あとでもう一度きいてみて' 14 | elif answer_number == 6: 15 | return '集中してもう一度きいてみて' 16 | elif answer_number == 7: 17 | return '私の答えはノーです' 18 | elif answer_number == 8: 19 | return '見通しはそれほどよくない' 20 | elif answer_number == 9: 21 | return 'とても疑わしい' 22 | 23 | r = random.randint(1, 9) # ❹ 24 | fortune = get_answer(r) # ❺ 25 | print(fortune) # ❻ 26 | -------------------------------------------------------------------------------- /ch03/sameName.py: -------------------------------------------------------------------------------- 1 | def spam(): 2 | eggs = 'spam local' # ❶ 3 | print(eggs) # 'spam local'を表示 4 | 5 | def bacon(): 6 | eggs = 'bacon local' # ❷ 7 | print(eggs) # 'bacon local'を表示 8 | spam() 9 | print(eggs) # 'bacon local'を表示 10 | 11 | eggs = 'global' # ❸ 12 | bacon() 13 | print(eggs) # 'global'を表示 14 | -------------------------------------------------------------------------------- /ch03/sameName2.py: -------------------------------------------------------------------------------- 1 | def spam(): 2 | global eggs # ❶ 3 | eggs = 'spam' # ❷ 4 | 5 | eggs = 'global' 6 | spam() 7 | print(eggs) 8 | -------------------------------------------------------------------------------- /ch03/sameName3.py: -------------------------------------------------------------------------------- 1 | def spam(): 2 | global eggs # ❶ 3 | eggs = 'spam' # グローバル変数になる 4 | 5 | def bacon(): 6 | eggs = 'bacon' # ローカル変数になる ❷ 7 | 8 | def ham(): 9 | print(eggs) # グローバル変数になる ❸ 10 | 11 | eggs = 42 # グローバル変数になる 12 | spam() 13 | print(eggs) 14 | -------------------------------------------------------------------------------- /ch03/sameName4.py: -------------------------------------------------------------------------------- 1 | def spam(): 2 | print(eggs) # エラー! 3 | eggs = 'spam local' # ❶ 4 | 5 | eggs = 'global' # ❷ 6 | spam() 7 | -------------------------------------------------------------------------------- /ch03/test3784.py: -------------------------------------------------------------------------------- 1 | def spam(): 2 | eggs = 31337 3 | spam() 4 | print(eggs) 5 | -------------------------------------------------------------------------------- /ch03/zeroDivide.py: -------------------------------------------------------------------------------- 1 | def spam(divide_by): 2 | return 42 / divide_by 3 | 4 | print(spam(2)) 5 | print(spam(12)) 6 | print(spam(0)) 7 | print(spam(1)) 8 | -------------------------------------------------------------------------------- /ch04/allMyCats1.py: -------------------------------------------------------------------------------- 1 | print('猫1の名前を入力してください。') 2 | cat_name1 = input() 3 | print('猫2の名前を入力してください。') 4 | cat_name2 = input() 5 | print('猫3の名前を入力してください。') 6 | cat_name3 = input() 7 | print('猫4の名前を入力してください。') 8 | cat_name4 = input() 9 | print('猫5の名前を入力してください。') 10 | cat_name5 = input() 11 | print('猫6の名前を入力してください。') 12 | cat_name6 = input() 13 | print('猫の名前は次のとおり:') 14 | print(cat_name1 + ' ' + cat_name2 + ' ' + cat_name3 + ' ' + cat_name4 + ' ' + cat_name5 + ' ' + cat_name6) 15 | -------------------------------------------------------------------------------- /ch04/allMyCats2.py: -------------------------------------------------------------------------------- 1 | cat_names = [] 2 | while True: 3 | print('猫' + str(len(cat_names) + 1) + 'の名前を入力してください' + 4 | ' (終了するにはEnterキーだけ押してください)') 5 | name = input() 6 | if name == '': 7 | break 8 | cat_names = cat_names + [name] # リストの連結 9 | print('猫の名前は次のとおり:') 10 | for name in cat_names: 11 | print(' ' + name) 12 | -------------------------------------------------------------------------------- /ch04/magic8Ball2.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | messages = ['確かにそうだ', 4 | '間違いなくそうだ', 5 | 'はい', 6 | 'なんとも。もういちどやってみて', 7 | 'あとでもう一度きいてみて', 8 | '集中してもう一度きいてみて', 9 | '私の答えはノーです', 10 | '見通しはそれほどよくない', 11 | 'とても疑わしい'] 12 | 13 | print(messages[random.randint(0, len(messages) - 1)]) 14 | -------------------------------------------------------------------------------- /ch04/myPets.py: -------------------------------------------------------------------------------- 1 | my_pets = ['Zophie', 'Pooka', 'Fat-tail'] 2 | print('ペットの名前を入力してください:') 3 | name = input() 4 | if name not in my_pets: 5 | print(name + 'という名前のペットは飼っていません。') 6 | else: 7 | print(name + 'は私のペットです。') 8 | -------------------------------------------------------------------------------- /ch04/passingReference.py: -------------------------------------------------------------------------------- 1 | def eggs(some_parameter): 2 | some_parameter.append('Hello') 3 | 4 | spam = [1, 2, 3] 5 | eggs(spam) 6 | print(spam) 7 | -------------------------------------------------------------------------------- /ch05/birthdays.py: -------------------------------------------------------------------------------- 1 | birthdays = {'アリス': '4/1', 'ボブ': '12/12', 'キャロル': '4/4'} # ❶ 2 | 3 | while True: 4 | print('名前を入力してください: (終了するにはEnterだけ押してください)') 5 | name = input() 6 | if name == '': 7 | break 8 | 9 | if name in birthdays: # ❷ 10 | print(name + 'の誕生日は' + birthdays[name]) # ❸ 11 | else: 12 | print(name + 'の誕生日は未登録です。') 13 | print('誕生日を入力してください:') 14 | bday = input() 15 | birthdays[name] = bday # ❹ 16 | print('誕生日データベースを更新しました。') 17 | -------------------------------------------------------------------------------- /ch05/characterCount.py: -------------------------------------------------------------------------------- 1 | message = 'It was a bright cold day in April, and the clocks were striking thirteen.' 2 | count = {} 3 | 4 | for character in message: 5 | count.setdefault(character, 0) 6 | count[character] = count[character] + 1 7 | 8 | print(count) 9 | -------------------------------------------------------------------------------- /ch05/fullTicTacToe2.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # -*- coding: utf-8 -*- 3 | # 三目並べ 4 | # 相川愛三が改良 5 | 6 | import random, re 7 | 8 | board10 = [None, '1', '2', '3', '4', '5', '6', '7', '8', '9'] 9 | 10 | def draw_board(board): 11 | # ボードを表示する 12 | # "board"は10個の文字列のリスト。インデックス0は無視。 13 | for i in range(7, 0, -3): 14 | print(' | |') 15 | print(' ' + ' | '.join(board[i:i+3])) 16 | print(' | |') 17 | if i > 1: 18 | print('-----------') 19 | 20 | def input_player_letter(): 21 | # プレイヤーに OかXかを選んでもらう。 22 | # プレーヤーの駒が先、コンピューターの駒が後のリストを返す。 23 | letter = '' 24 | while not (letter == 'X' or letter == 'O'): 25 | print('O=先手、X=後手、どちらにしますか? (O or X)') 26 | letter = input().upper() 27 | 28 | # [プレーヤーの駒, コンピューターの駒]というリストを返す 29 | if letter == 'X': 30 | return ['X', 'O'] 31 | else: 32 | return ['O', 'X'] 33 | 34 | def play_again(): 35 | # プレーヤーがもう一度遊ぶと答えたならTrue、そうでなければFalseを返す。 36 | print('もう一度遊ぶ? (yes or no)') 37 | return input().lower().startswith('y') 38 | 39 | def make_move(board, letter, move): 40 | board[move] = letter 41 | 42 | def is_winner(bo, le): 43 | # プレーヤーがか勝ちならTrueを返す。 44 | # boはボード、leはプレーヤーの駒 45 | return ((bo[7] == le and bo[8] == le and bo[9] == le) or # 上の行 46 | (bo[4] == le and bo[5] == le and bo[6] == le) or # 中の行 47 | (bo[1] == le and bo[2] == le and bo[3] == le) or # 下の行 48 | (bo[7] == le and bo[4] == le and bo[1] == le) or # 左の列 49 | (bo[8] == le and bo[5] == le and bo[2] == le) or # 中の列 50 | (bo[9] == le and bo[6] == le and bo[3] == le) or # 右の列 51 | (bo[7] == le and bo[5] == le and bo[3] == le) or # 対角線 52 | (bo[9] == le and bo[5] == le and bo[1] == le)) # 対角線 53 | 54 | def get_board_copy(board): 55 | # ボードのコピーを作る 56 | return board[:] 57 | 58 | def is_space_free(board, move): 59 | # ボードが空いていればTrue 60 | return board[move] == ' ' 61 | 62 | def get_player_move(board): 63 | # プレーヤーに次の手を入力してもらう 64 | while True: 65 | print('1=左下~9=右上のどこに打つ?(1-9)') 66 | move = input() 67 | if move in board10: 68 | imove = int(move) 69 | if is_space_free(board, imove): 70 | return imove 71 | else: 72 | print('そのマスは空いてません。') 73 | else: 74 | print('マスの番号') 75 | draw_board(board10) 76 | 77 | def choose_random_move_from_list(board, moves_list): 78 | # 渡されたリストから有効な次の手をランダムに選んで返す。 79 | # 打つ場所がなければNoneを返す 80 | possible_moves = [] 81 | for i in moves_list: 82 | if is_space_free(board, i): 83 | possible_moves.append(i) 84 | 85 | if len(possible_moves) != 0: 86 | return random.choice(possible_moves) 87 | else: 88 | return None 89 | 90 | def get_computer_move(board, computer_letter): 91 | # コンピューターの次の手を選ぶ。 92 | if computer_letter == 'X': 93 | player_letter = 'O' 94 | else: 95 | player_letter = 'X' 96 | 97 | # 三目並べの人工知能のアルゴリズム 98 | # まず、次の手で勝てるかどうかを調べる。 99 | for i in range(1, 10): 100 | copy = get_board_copy(board) 101 | if is_space_free(copy, i): 102 | make_move(copy, computer_letter, i) 103 | if is_winner(copy, computer_letter): 104 | return i 105 | 106 | # プレーヤーが次の手で勝てるなら、それを防ぐ。 107 | for i in range(1, 10): 108 | copy = get_board_copy(board) 109 | if is_space_free(copy, i): 110 | make_move(copy, player_letter, i) 111 | if is_winner(copy, player_letter): 112 | return i 113 | 114 | # 角が空いていれば、そこに打つ。 115 | move = choose_random_move_from_list(board, [1, 3, 7, 9]) 116 | if move != None: 117 | return move 118 | 119 | # 真ん中が空いていれば、そこに打つ。 120 | if is_space_free(board, 5): 121 | return 5 122 | 123 | # 上下左右に打つ。 124 | return choose_random_move_from_list(board, [2, 4, 6, 8]) 125 | 126 | def is_board_full(board): 127 | # ボードが埋まったらTrueを返す。 128 | for i in range(1, 10): 129 | if is_space_free(board, i): 130 | return False 131 | return True 132 | 133 | 134 | print('三目並べにようこそ!') 135 | 136 | while True: 137 | # ボードをリセットする。 138 | the_board = [' '] * 10 139 | 140 | player_letter, computer_letter = input_player_letter() 141 | if player_letter == 'O': 142 | turn = 'プレーヤー' 143 | else: 144 | turn = 'コンピューター' 145 | print(turn + 'が先手。') 146 | 147 | while True: 148 | if is_board_full(the_board): 149 | draw_board(the_board) 150 | print('引き分け!') 151 | break 152 | 153 | if is_winner(the_board, player_letter): 154 | draw_board(the_board) 155 | print('おめでとう! あなたの勝ち!') 156 | break 157 | 158 | if is_winner(the_board, computer_letter): 159 | draw_board(the_board) 160 | print('コンピューターの勝ち!') 161 | break 162 | 163 | if turn == 'プレーヤー': 164 | # プレーヤーの番 165 | draw_board(the_board) 166 | move = get_player_move(the_board) 167 | make_move(the_board, player_letter, move) 168 | turn = 'コンピューター' 169 | 170 | else: 171 | # コンピューターの番 172 | move = get_computer_move(the_board, computer_letter) 173 | make_move(the_board, computer_letter, move) 174 | turn = 'プレーヤー' 175 | 176 | if not play_again(): 177 | break 178 | -------------------------------------------------------------------------------- /ch05/fullTicTacToeOrg.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # -*- coding: utf-8 -*- 3 | # 三目並べ 4 | 5 | import random 6 | 7 | def drawBoard(board): 8 | # ボードを表示する 9 | 10 | # "board"は10個の文字列のリスト。インデックス0は無視。 11 | print(' | |') 12 | print(' ' + board[7] + ' | ' + board[8] + ' | ' + board[9]) 13 | print(' | |') 14 | print('-----------') 15 | print(' | |') 16 | print(' ' + board[4] + ' | ' + board[5] + ' | ' + board[6]) 17 | print(' | |') 18 | print('-----------') 19 | print(' | |') 20 | print(' ' + board[1] + ' | ' + board[2] + ' | ' + board[3]) 21 | print(' | |') 22 | 23 | def inputPlayerLetter(): 24 | # プレイヤーに OかXかを選んでもらう。 25 | # プレーヤーの駒が先、コンピューターの駒が後のリストを返す。 26 | letter = '' 27 | while not (letter == 'X' or letter == 'O'): 28 | print('どちらの駒にしますか? (O or X)') 29 | letter = input().upper() 30 | 31 | # [プレーヤーの駒, コンピューターの駒]というリストを返す 32 | if letter == 'X': 33 | return ['X', 'O'] 34 | else: 35 | return ['O', 'X'] 36 | 37 | def whoGoesFirst(): 38 | # 先手をランダムに選ぶ 39 | if random.randint(0, 1) == 0: 40 | return 'コンピューター' 41 | else: 42 | return 'プレーヤー' 43 | 44 | def playAgain(): 45 | # プレーヤーがもう一度遊ぶと答えたならTrue、そうでなければFalseを返す。 46 | print('もう一度遊ぶ? (yes or no)') 47 | return input().lower().startswith('y') 48 | 49 | def makeMove(board, letter, move): 50 | board[move] = letter 51 | 52 | def isWinner(bo, le): 53 | # プレーヤーがか勝ちならTrueを返す。 54 | # boはボード、leはプレーヤーの駒 55 | return ((bo[7] == le and bo[8] == le and bo[9] == le) or # 上の行 56 | (bo[4] == le and bo[5] == le and bo[6] == le) or # 中の行 57 | (bo[1] == le and bo[2] == le and bo[3] == le) or # 下の行 58 | (bo[7] == le and bo[4] == le and bo[1] == le) or # 左の列 59 | (bo[8] == le and bo[5] == le and bo[2] == le) or # 中の列 60 | (bo[9] == le and bo[6] == le and bo[3] == le) or # 右の列 61 | (bo[7] == le and bo[5] == le and bo[3] == le) or # 対角線 62 | (bo[9] == le and bo[5] == le and bo[1] == le)) # 対角線 63 | 64 | def getBoardCopy(board): 65 | # ボードのコピーを作る 66 | dupeBoard = [] 67 | 68 | for i in board: 69 | dupeBoard.append(i) 70 | 71 | return dupeBoard 72 | 73 | def isSpaceFree(board, move): 74 | # ボードが空いていればTrue 75 | return board[move] == ' ' 76 | 77 | def getPlayerMove(board): 78 | # プレーヤーに次の手を入力してもらう 79 | move = ' ' 80 | while move not in '1 2 3 4 5 6 7 8 9'.split() or not isSpaceFree(board, int(move)): 81 | print('どこに打つ? (1-9)') 82 | move = input() 83 | return int(move) 84 | 85 | def chooseRandomMoveFromList(board, movesList): 86 | # 渡されたリストから有効な次の手をランダムに選んで返す。 87 | # 打つ場所がなければNoneを返す 88 | possibleMoves = [] 89 | for i in movesList: 90 | if isSpaceFree(board, i): 91 | possibleMoves.append(i) 92 | 93 | if len(possibleMoves) != 0: 94 | return random.choice(possibleMoves) 95 | else: 96 | return None 97 | 98 | def getComputerMove(board, computerLetter): 99 | # コンピューターの次の手を選ぶ。 100 | if computerLetter == 'X': 101 | playerLetter = 'O' 102 | else: 103 | playerLetter = 'X' 104 | 105 | # 三目並べの人工知能のアルゴリズム 106 | # まず、次の手で勝てるかどうかを調べる。 107 | for i in range(1, 10): 108 | copy = getBoardCopy(board) 109 | if isSpaceFree(copy, i): 110 | makeMove(copy, computerLetter, i) 111 | if isWinner(copy, computerLetter): 112 | return i 113 | 114 | # プレーヤーが次の手で勝てるなら、それを防ぐ。 115 | for i in range(1, 10): 116 | copy = getBoardCopy(board) 117 | if isSpaceFree(copy, i): 118 | makeMove(copy, playerLetter, i) 119 | if isWinner(copy, playerLetter): 120 | return i 121 | 122 | # 角が空いていれば、そこに打つ。 123 | move = chooseRandomMoveFromList(board, [1, 3, 7, 9]) 124 | if move != None: 125 | return move 126 | 127 | # 真ん中が空いていれば、そこに打つ。 128 | if isSpaceFree(board, 5): 129 | return 5 130 | 131 | # 上下左右に打つ。 132 | return chooseRandomMoveFromList(board, [2, 4, 6, 8]) 133 | 134 | def isBoardFull(board): 135 | # ボードが埋まったらTrueを返す。 136 | for i in range(1, 10): 137 | if isSpaceFree(board, i): 138 | return False 139 | return True 140 | 141 | 142 | print('三目並べにようこそ!') 143 | 144 | while True: 145 | # ボードをリセットする。 146 | theBoard = [' '] * 10 147 | playerLetter, computerLetter = inputPlayerLetter() 148 | turn = whoGoesFirst() 149 | print(turn + 'が先手。') 150 | gameIsPlaying = True 151 | 152 | while gameIsPlaying: 153 | if turn == 'プレーヤー': 154 | # プレーヤーの番 155 | drawBoard(theBoard) 156 | move = getPlayerMove(theBoard) 157 | makeMove(theBoard, playerLetter, move) 158 | 159 | if isWinner(theBoard, playerLetter): 160 | drawBoard(theBoard) 161 | print('おめでとう! あなたの勝ち!') 162 | gameIsPlaying = False 163 | else: 164 | if isBoardFull(theBoard): 165 | drawBoard(theBoard) 166 | print('引き分け!') 167 | break 168 | else: 169 | turn = 'コンピューター' 170 | 171 | else: 172 | # コンピューターの番 173 | move = getComputerMove(theBoard, computerLetter) 174 | makeMove(theBoard, computerLetter, move) 175 | 176 | if isWinner(theBoard, computerLetter): 177 | drawBoard(theBoard) 178 | print('コンピューターの勝ち!') 179 | gameIsPlaying = False 180 | else: 181 | if isBoardFull(theBoard): 182 | drawBoard(theBoard) 183 | print('引き分け!') 184 | break 185 | else: 186 | turn = 'プレーヤー' 187 | 188 | if not playAgain(): 189 | break 190 | -------------------------------------------------------------------------------- /ch05/picnic.py: -------------------------------------------------------------------------------- 1 | all_guests = {'アリス': {'リンゴ': 5, 'プレッツェル': 12}, 2 | 'ボブ': {'ハムサンド': 3, 'リンゴ': 2}, 3 | 'キャロル': {'コップ': 3, 'アップルパイ': 1}} 4 | 5 | def total_brought(guests, item): 6 | num_brought = 0 7 | for k, v in guests.items(): # ❶ 8 | num_brought = num_brought + v.get(item, 0) # ❷ 9 | return num_brought 10 | 11 | print('持ち物の数:') 12 | print(' - リンゴ ' + str(total_brought(all_guests, 'リンゴ'))) 13 | print(' - コップ ' + str(total_brought(all_guests, 'コップ'))) 14 | print(' - ケーキ ' + str(total_brought(all_guests, 'ケーキ'))) 15 | print(' - ハムサンド ' + str(total_brought(all_guests, 'ハムサンド'))) 16 | print(' - アップルパイ ' + str(total_brought(all_guests, 'アップルパイ'))) 17 | -------------------------------------------------------------------------------- /ch05/prettyCharacterCount.py: -------------------------------------------------------------------------------- 1 | import pprint 2 | message = 'It was a bright cold day in April, and the clocks were striking thirteen.' 3 | count = {} 4 | 5 | for character in message: 6 | count.setdefault(character, 0) 7 | count[character] = count[character] + 1 8 | 9 | pprint.pprint(count) 10 | -------------------------------------------------------------------------------- /ch05/ticTacToe.py: -------------------------------------------------------------------------------- 1 | the_board = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ', 2 | 'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ', 3 | 'low-L': ' ', 'low-M': ' ', 'low-R': ' '} 4 | 5 | the_board2 = {'top-L': 'O', 'top-M': 'O', 'top-R': 'O', 6 | 'mid-L': 'X', 'mid-M': 'X', 'mid-R': ' ', 7 | 'low-L': ' ', 'low-M': ' ', 'low-R': 'X'} 8 | 9 | def print_board(board): 10 | print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R']) 11 | print('-+-+-') 12 | print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R']) 13 | print('-+-+-') 14 | print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R']) 15 | 16 | print_board(the_board) 17 | print_board(the_board2) 18 | -------------------------------------------------------------------------------- /ch05/ticTacToe2.py: -------------------------------------------------------------------------------- 1 | the_board = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ', 2 | 'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ', 3 | 'low-L': ' ', 'low-M': ' ', 'low-R': ' '} 4 | 5 | def print_board(board): 6 | print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R']) 7 | print('-+-+-') 8 | print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R']) 9 | print('-+-+-') 10 | print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R']) 11 | 12 | turn = 'X' 13 | for i in range(9): 14 | print_board(the_board) # ❶ 15 | print(turn + 'の番です。どこに打つ?') 16 | move = input() # ❷ 17 | the_board[move] = turn # ❸ 18 | if turn == 'X': # ❹ 19 | turn = 'O' 20 | else: 21 | turn = 'X' 22 | 23 | print_board(the_board) 24 | 25 | -------------------------------------------------------------------------------- /ch06/bulletPointAdder.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # bulletPointAdder.py - クリップボードのテキストの各行に 3 | # 点を打って、Wikipediaの箇条書きにする 4 | 5 | import pyperclip 6 | text = pyperclip.paste() 7 | 8 | # 行を分割して、'*'を追加する 9 | lines = text.split('\n') 10 | for i in range(len(lines)): # "lines"リストの各要素をループする 11 | lines[i] = '* ' + lines[i] # "lines"の要素に"* "を追加する 12 | 13 | text = '\n'.join(lines) 14 | 15 | pyperclip.copy(text) 16 | 17 | -------------------------------------------------------------------------------- /ch06/catnapping.py: -------------------------------------------------------------------------------- 1 | print('''Dear Alice, 2 | 3 | Eve's cat has been arrested for catnapping, cat burglary, and extortion. 4 | 5 | Sincerely, 6 | Bob''') 7 | -------------------------------------------------------------------------------- /ch06/picnicTable.py: -------------------------------------------------------------------------------- 1 | def print_picnic(items_dict, left_width, right_width): 2 | print('PICNIC ITEMS'.center(left_width + right_width, '-')) 3 | for k, v in items_dict.items(): 4 | print(k.ljust(left_width, '.') + str(v).rjust(right_width)) 5 | 6 | picnic_items = {'sandwiches': 4, 'apples': 12, 'cups': 4, 'cookies': 8000} 7 | print_picnic(picnic_items, 12, 5) 8 | print_picnic(picnic_items, 20, 6) 9 | -------------------------------------------------------------------------------- /ch06/pw.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # pw.py - パスワード管理プログラム(脆弱性あり) 3 | 4 | PASSWORDS = {'email': 'F7minlBDDuvMJuxESSKHFhTxFtjVB6', 5 | 'blog': 'VmALvQyKAxiVH5G8v01if1MLZF3sdt', 6 | 'luggage': '12345'} 7 | 8 | import sys 9 | import pyperclip 10 | 11 | if len(sys.argv) < 2: 12 | print('使い方: python pw.py [アカウント名]') 13 | print('パスワードをクリップボードにコピーします') 14 | sys.exit() 15 | 16 | account = sys.argv[1] # 最初のコマンドライン引数がアカウント名 17 | 18 | if account in PASSWORDS: 19 | pyperclip.copy(PASSWORDS[account]) 20 | print(account + 'のパスワードをクリップボードにコピーしました') 21 | else: 22 | print(account + 'というアカウント名はありません') 23 | 24 | -------------------------------------------------------------------------------- /ch06/validateInput.py: -------------------------------------------------------------------------------- 1 | while True: 2 | print('年齢を入力してください:') 3 | age = input() 4 | if age.isdecimal(): 5 | break 6 | print('年齢は数字で入力してください。') 7 | 8 | while True: 9 | print('新しいパスワードを入力してください(英数字のみ):') 10 | password = input() 11 | if password.isalnum(): 12 | break 13 | print('パスワードは英数字で入力してください。') 14 | -------------------------------------------------------------------------------- /ch07/JPhoneAndEmail.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # JPhoneAndEmail.py - クリップボードから電話番号とメアドを検索する(日本語版) 3 | 4 | import pyperclip, re 5 | 6 | #phone_regex = re.compile(r'''( 7 | # (\d{3}|\(\d{3}\))? # 市外局番 8 | # (\s|-|\.)? # 区切り 9 | # (\d{3}) # 3桁の番号 10 | # (\s|-|\.) # 区切り 11 | # (\d{4}) # 4桁の番号 12 | # (\s*(ext|x|ext.)\s*(\d{2,5}))? # 内線番号 13 | # )''', re.VERBOSE) 14 | 15 | # 日本の電話番号パターンにしたもの 16 | phone_regex = re.compile(r'''( 17 | (0\d{0,3}|\(\d{0,3}\)) # 市外局番 18 | (\s|-) # 区切り 19 | (\d{1,4}) # 市内局番 20 | (\s|-) # 区切り 21 | (\d{3,4}) # 加入者番号 22 | (\s*(ext|x|ext.)\s*(\d{2,5}))? # 内線番号 23 | )''', re.VERBOSE) 24 | 25 | 26 | # 電子メールの正規表現を作る。 27 | email_regex = re.compile(r'''( 28 | [a-zA-Z0-9._%+-]+ # ユーザー名 ❶ 29 | @ # @ 記号 ❷ 30 | [a-zA-Z0-9.-]+ # ドメイン名 ❸ 31 | (\.[a-zA-Z]{2,4}) # ドットなんとか 32 | )''', re.VERBOSE) 33 | 34 | # クリップボードのテキストを検索する。 35 | text = str(pyperclip.paste()) 36 | matches = [] # ❶ 37 | for groups in phone_regex.findall(text): # ❷ 38 | phone_num = '-'.join([groups[1], groups[3], groups[5]]) 39 | if groups[8] != '': 40 | phone_num += ' x' + groups[8] 41 | matches.append(phone_num) 42 | for groups in email_regex.findall(text): # ❸ 43 | matches.append(groups[0]) 44 | 45 | # 検索結果をクリップボードに貼り付ける。 46 | if len(matches) > 0: 47 | pyperclip.copy('\n'.join(matches)) 48 | print('クリップボードにコピーしました:') 49 | print('\n'.join(matches)) 50 | else: 51 | print('電話番号やメールアドレスは見つかりませんでした。') 52 | 53 | -------------------------------------------------------------------------------- /ch07/isPhoneNumber.py: -------------------------------------------------------------------------------- 1 | def is_phone_number(text): 2 | if len(text) != 12: # ❶ 3 | return False 4 | for i in range(0, 3): 5 | if not text[i].isdecimal(): # ❷ 6 | return False 7 | if text[3] != '-': # ❸ 8 | return False 9 | for i in range(4, 7): 10 | if not text[i].isdecimal(): # ❹ 11 | return False 12 | if text[7] != '-': # ❺ 13 | return False 14 | for i in range(8, 12): 15 | if not text[i].isdecimal(): # ❻ 16 | return False 17 | return True # ❼ 18 | 19 | print('415-555-4242 は電話番号:') 20 | print(is_phone_number('415-555-4242')) 21 | print('Moshi moshi は電話番号:') 22 | print(is_phone_number('Moshi moshi')) 23 | 24 | #message = 'Call me at 415-555-1011 tomorrow. 415-555-9999 is my office.' 25 | message = '明日415-555-1011に電話してください。オフィスは415-555-9999です。' 26 | for i in range(len(message)): 27 | chunk = message[i:i+12] # ❶ 28 | if is_phone_number(chunk): # ❷ 29 | print('電話番号が見つかりました: ' + chunk) 30 | print('完了') 31 | 32 | -------------------------------------------------------------------------------- /ch07/phoneAndEmail.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # phoneAndEmail.py - クリップボードから電話番号とメアドを検索する 3 | 4 | import pyperclip, re 5 | 6 | phone_regex = re.compile(r'''( 7 | (\d{3}|\(\d{3}\))? # 市外局番 8 | (\s|-|\.)? # 区切り 9 | (\d{3}) # 3桁の番号 10 | (\s|-|\.) # 区切り 11 | (\d{4}) # 4桁の番号 12 | (\s*(ext|x|ext.)\s*(\d{2,5}))? # 内線番号 13 | )''', re.VERBOSE) 14 | 15 | # 電子メールの正規表現を作る。 16 | email_regex = re.compile(r'''( 17 | [a-zA-Z0-9._%+-]+ # ユーザー名 ❶ 18 | @ # @ 記号 ❷ 19 | [a-zA-Z0-9.-]+ # ドメイン名 ❸ 20 | (\.[a-zA-Z]{2,4}) # ドットなんとか 21 | )''', re.VERBOSE) 22 | 23 | # クリップボードのテキストを検索する。 24 | text = str(pyperclip.paste()) 25 | matches = [] # ❶ 26 | for groups in phone_regex.findall(text): # ❷ 27 | phone_num = '-'.join([groups[1], groups[3], groups[5]]) 28 | if groups[8] != '': 29 | phone_num += ' x' + groups[8] 30 | matches.append(phone_num) 31 | for groups in email_regex.findall(text): # ❸ 32 | matches.append(groups[0]) 33 | 34 | # 検索結果をクリップボードに貼り付ける。 35 | if len(matches) > 0: 36 | pyperclip.copy('\n'.join(matches)) 37 | print('クリップボードにコピーしました:') 38 | print('\n'.join(matches)) 39 | else: 40 | print('電話番号やメールアドレスは見つかりませんでした。') 41 | 42 | -------------------------------------------------------------------------------- /ch08/mcb.pyw: -------------------------------------------------------------------------------- 1 | #! python3 2 | # mcb.pyw - クリップボードのテキストを保存・復元 3 | # Usage: 4 | # py.exe mcb.pyw save - クリップボードをキーワードに紐づけて保存 ❶ 5 | # py.exe mcb.pyw - キーワードに紐づけられたテキストをクリップボードにコピー 6 | # py.exe mcb.pyw list - 全キーワードをクリップボードにコピー 7 | 8 | import shelve, pyperclip, sys # ❷ 9 | 10 | mcb_shelf = shelve.open('mcb') # ❸ 11 | 12 | # クリップボードの内容を保存 13 | if len(sys.argv) == 3 and sys.argv[1].lower() == 'save': # ❶ 14 | mcb_shelf[sys.argv[2]] = pyperclip.paste() # ❷ 15 | elif len(sys.argv) == 2: 16 | # キーワード一覧と、内容の読み込み 17 | if sys.argv[1].lower() == 'list': # ❶ 18 | pyperclip.copy(str(list(mcb_shelf.keys()))) # ❷ 19 | elif sys.argv[1] in mcb_shelf: 20 | pyperclip.copy(mcb_shelf[sys.argv[1]]) # ❸ 21 | 22 | mcb_shelf.close() 23 | -------------------------------------------------------------------------------- /ch08/mcb.vbs: -------------------------------------------------------------------------------- 1 | cmd = "cmd /c pythonw.exe mcb.pyw" 2 | 3 | Set args = WScript.Arguments 4 | For i = 0 to args.Count - 1 5 | cmd = cmd & " " & args(i) 6 | Next 7 | 8 | Set s = CreateObject("Wscript.Shell") 9 | s.CurrentDirectory = "C:\mcb" 10 | s.Run cmd, 0, false 11 | -------------------------------------------------------------------------------- /ch08/randomQuizGenerator.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # randomQuizGenerator.py - ランダム順に問題と答えを並べ問題集と解答集を作る 3 | 4 | import random # ❶ 5 | 6 | # 問題のデータ。キーが都道府県で、値が県庁所在地 ❷ 7 | capitals = {'北海道': '札幌市', '青森県': '青森市', '岩手県': '盛岡市', 8 | '宮城県': '仙台市', '秋田県': '秋田市', '山形県': '山形市', '福島県': '福島市', 9 | '茨城県': '水戸市', '栃木県': '宇都宮市', '群馬県': '前橋市', 10 | '埼玉県': 'さいたま市', '千葉県': '千葉市', '東京都': '東京', 11 | '神奈川県': '横浜市', '新潟県': '新潟市', '富山県': '富山市', '石川県': '金沢市', 12 | '福井県': '福井市', '山梨県': '甲府市', '長野県': '長野市', '岐阜県': '岐阜市', 13 | '静岡県': '静岡市', '愛知県': '名古屋市', '三重県': '津市', '滋賀県': '大津市', 14 | '京都府': '京都市', '大阪府': '大阪市', '兵庫県': '神戸市', '奈良県': '奈良市', 15 | '和歌山県': '和歌山市', '鳥取県': '鳥取市', '島根県': '松江市', 16 | '岡山県': '岡山市', '広島県': '広島市', '山口県': '山口市', '徳島県': '徳島市', 17 | '香川県': '高松市', '愛媛県': '松山市', '高知県': '高知市', '福岡県': '福岡市', 18 | '佐賀県': '佐賀市', '長崎県': '長崎市', '熊本県': '熊本市', '大分県': '大分市', 19 | '宮崎県': '宮崎市', '鹿児島県': '鹿児島市', '沖縄県': '那覇市'} 20 | 21 | # 35個の問題集を作成する 22 | for quiz_num in range(35): 23 | # 問題と答えのファイルを作成する 24 | quiz_file = open('capitalsquiz{}.txt'.format(quiz_num + 1), 'w') 25 | answer_key_file = open('capitalsquiz_answers{}.txt'.format(quiz_num + 1), 'w') 26 | 27 | # 問題のヘッダを書く 28 | quiz_file.write('名前:\n\n日付:\n\n') 29 | quiz_file.write((' ' * 20) + '都道府県庁所在地クイズ (問題番号 {})'.format(quiz_num + 1)) 30 | quiz_file.write('\n\n') 31 | 32 | # 都道府県の順番をシャッフルする 33 | prefectures = list(capitals.keys()) 34 | random.shuffle(prefectures) 35 | 36 | for question_num in range(len(prefectures)): 37 | # 正解と誤答を取得する 38 | correct_answer = capitals[prefectures[question_num]] 39 | wrong_answers = list(capitals.values()) 40 | del wrong_answers[wrong_answers.index(correct_answer)] 41 | wrong_answers = random.sample(wrong_answers, 3) 42 | answer_options = wrong_answers + [correct_answer] 43 | random.shuffle(answer_options) 44 | 45 | # 問題文と回答選択肢を問題ファイルに書く 46 | quiz_file.write('{}. {}の都道府県庁所在地は?\n'.format(question_num + 1, 47 | prefectures[question_num])) 48 | for i in range(4): 49 | quiz_file.write(' {}. {}\n'.format('ABCD'[i], answer_options[i])) 50 | quiz_file.write('\n') 51 | 52 | # 答えの選択肢をファイルに書く 53 | answer_key_file.write('{}. {}\n'.format(question_num + 1, 'ABCD'[ 54 | answer_options.index(correct_answer)])) 55 | 56 | quiz_file.close() 57 | answer_key_file.close() 58 | 59 | -------------------------------------------------------------------------------- /ch09/backupToZip.py: -------------------------------------------------------------------------------- 1 | 2 | #! python3 3 | # backupToZip.py - フォルダ全体を連番付きZIPファイルにコピーする 4 | 5 | import zipfile, os # ❶ 6 | 7 | def backup_to_zip(folder): 8 | # フォルダ全体をZIPファイルにバックアップする 9 | 10 | folder = os.path.abspath(folder) # folderを絶対パスにする 11 | 12 | # 既存のファイル名からファイル名の連番を決める 13 | number = 1 # ❷ 14 | while True: # ❸ 15 | zip_filename = os.path.basename(folder) + '_' + str(number) + '.zip' 16 | print("zip = " + zip_filename) 17 | if not os.path.exists(zip_filename): 18 | break 19 | number = number + 1 20 | 21 | # ZIPファイルを作成する 22 | print('Creating %s...' % (zip_filename)) 23 | backup_zip = zipfile.ZipFile(zip_filename, 'w') # ❶ 24 | 25 | # フォルダのツリーを渡り歩いてその中のファイルを圧縮する 26 | for foldername, subfolders, filenames in os.walk(folder): # ❶ 27 | print('Adding files in {}...'.format(foldername)) 28 | # 現在のフォルダをZIPファイルに追加する 29 | backup_zip.write(foldername) # ❷ 30 | # 現在のフォルダの中の全ファイルをZIPファイルに追加する 31 | for filename in filenames: # ❸ 32 | new_base = os.path.basename(folder) + '_' 33 | if filename.startswith(new_base) and filename.endswith('.zip'): 34 | continue # バックアップ用ZIPファイルはバックアップしない 35 | backup_zip.write(os.path.join(foldername, filename)) 36 | backup_zip.close() 37 | print('Done.') 38 | 39 | backup_to_zip('C:\\delicious') 40 | 41 | 42 | -------------------------------------------------------------------------------- /ch09/delicious/cats/catnames.txt: -------------------------------------------------------------------------------- 1 | Selena 2 | Apollo 3 | Mittens 4 | Chairman Meow 5 | Leo 6 | Snowball 7 | Miss Cleo 8 | Pooka 9 | Simon 10 | Zophie 11 | Oliver 12 | Milo 13 | Toby 14 | Tigger 15 | Jasper 16 | Sushi 17 | Smudge 18 | Lily 19 | Felix 20 | Amber 21 | Oreo 22 | Loki 23 | Simba 24 | Macbeth 25 | Bridge 26 | Garfield 27 | Nibbles 28 | Glacier -------------------------------------------------------------------------------- /ch09/delicious/cats/zophie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch09/delicious/cats/zophie.jpg -------------------------------------------------------------------------------- /ch09/delicious/spam.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch09/delicious/spam.txt -------------------------------------------------------------------------------- /ch09/delicious/walnut/waffles/butter.txt: -------------------------------------------------------------------------------- 1 | This is 'butter.txt'. -------------------------------------------------------------------------------- /ch09/example.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch09/example.zip -------------------------------------------------------------------------------- /ch09/renameDates.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # renameDates.py - 米国式日付MM-DD-YYYYのファイル名を欧州式DD-MM-YYYYに書き換える 3 | 4 | import shutil, os, re # ❶ 5 | 6 | # 米国式日付のファイル名にマッチする正規表現を作る 7 | 8 | date_pattern = re.compile(r"""^(.*?) # 日付の前の全テキスト ❷ 9 | ((0|1)?\d)- # 月を表す1,2桁の数字 10 | ((0|1|2|3)?\d)- # 日を表す1,2桁の数字 11 | ((19|20)\d\d) # 年を表す4桁の数字 12 | (.*?)$ # 日付の後の全テキスト 13 | """, re.VERBOSE) # ❸ 14 | 15 | # カレントディレクトリの全ファイルをループする。 16 | for amer_filename in os.listdir('.'): 17 | mo = date_pattern.search(amer_filename) 18 | 19 | # 日付のないファイルをスキップする。 20 | if mo == None: # ❶ 21 | continue # ❷ 22 | 23 | # ファイル名を部分分解する。❸ 24 | before_part = mo.group(1) 25 | month_part = mo.group(2) 26 | day_part = mo.group(4) 27 | year_part = mo.group(6) 28 | after_part = mo.group(8) 29 | 30 | # 欧州式の日付のファイル名を作る。 31 | euro_filename = before_part + day_part + '-' + month_part + '-' + \ 32 | year_part + after_part # ❶ 33 | 34 | # ファイル名を変更する。 35 | print('Renaming "{}" to "{}"...'.format(amer_filename, euro_filename)) # ❷ 36 | #shutil.move(amer_filename, euro_filename) # テスト後にコメントをはずす ❸ 37 | -------------------------------------------------------------------------------- /ch10/boxPrint.py: -------------------------------------------------------------------------------- 1 | def box_print(symbol, width, height): 2 | if len(symbol) != 1: 3 | raise Exception('symbolは1文字の文字列でなければならない。') # ❶ 4 | if width <= 2: 5 | raise Exception('widthは2より大きくなければならない。') # ❷ 6 | if height <= 2: 7 | raise Exception('heightは2より大きくなければならない。') # ❸ 8 | print(symbol * width) 9 | for i in range(height - 2): 10 | print(symbol + (' ' * (width - 2)) + symbol) 11 | print(symbol * width) 12 | 13 | for sym, w, h in (('*', 4, 4), ('O', 20, 5), ('x', 1, 3), ('ZZ', 3, 3)): 14 | try: 15 | box_print(sym, w, h) 16 | except Exception as err: # ❹ 17 | print('例外が起こりました: ' + str(err)) # ❺ 18 | -------------------------------------------------------------------------------- /ch10/buggyAddingProgram.py: -------------------------------------------------------------------------------- 1 | print('足しあわせる第1の数を入力してください:') 2 | first = input() 3 | print('足しあわせる第2の数を入力してください:') 4 | second = input() 5 | print('足しあわせる第3の数を入力してください:') 6 | third = input() 7 | print('合計は' + first + second + third) 8 | -------------------------------------------------------------------------------- /ch10/buggyAddingProgramFixed.py: -------------------------------------------------------------------------------- 1 | print('足しあわせる第1の数を入力してください:') 2 | first = input() 3 | print('足しあわせる第2の数を入力してください:') 4 | second = input() 5 | print('足しあわせる第3の数を入力してください:') 6 | third = input() 7 | print('合計は' + str(int(first) + int(second) + int(third))) 8 | -------------------------------------------------------------------------------- /ch10/coinFlip.py: -------------------------------------------------------------------------------- 1 | import random 2 | heads = 0 3 | for i in range(1, 1001): 4 | if random.randint(0, 1) == 1: # ❶ 5 | heads = heads + 1 6 | if i == 500: 7 | print('半分完了!') # ❷ 8 | print('表は' + str(heads) + '回出ました。') 9 | -------------------------------------------------------------------------------- /ch10/errorExample.py: -------------------------------------------------------------------------------- 1 | def spam(): 2 | bacon() 3 | 4 | def bacon(): 5 | raise Exception('これはエラーメッセージです。') 6 | 7 | spam() 8 | -------------------------------------------------------------------------------- /ch10/factorialLog.py: -------------------------------------------------------------------------------- 1 | import logging 2 | logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s - %(message)s') 3 | logging.debug('プログラム開始') 4 | 5 | def factorial(n): 6 | logging.debug('factorial({})開始'.format(n)) 7 | total = 1 8 | for i in range(n + 1): 9 | total *= i 10 | logging.debug('i = {}, total = {}'.format(i, total)) 11 | logging.debug('factorial({})終了'.format(n)) 12 | return total 13 | 14 | print(factorial(5)) 15 | logging.debug('プログラム終了') 16 | -------------------------------------------------------------------------------- /ch10/factorialLogFixed.py: -------------------------------------------------------------------------------- 1 | import logging 2 | logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s - %(message)s') 3 | logging.debug('プログラム開始') 4 | 5 | def factorial(n): 6 | logging.debug('factorial({})開始'.format(n)) 7 | total = 1 8 | for i in range(1, n + 1): 9 | total *= i 10 | logging.debug('i = {}, total = {}'.format(i, total)) 11 | logging.debug('factorial({})終了'.format(n)) 12 | return total 13 | 14 | print(factorial(5)) 15 | logging.debug('プログラム終了') 16 | -------------------------------------------------------------------------------- /ch11/downloadXkcd.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # downloadXkcd.py - XKCDコミックをひとつずつダウンロードする 3 | 4 | import requests, os, bs4 5 | import time 6 | 7 | url = 'http://xkcd.com/' # 開始URL 8 | os.makedirs('xkcd', exist_ok=True) # ./xkcdに保存する 9 | 10 | while not url.endswith('#'): 11 | # ページをダウンロードする 12 | print('ページをダウンロード中 {}...'.format(url)) 13 | res = requests.get(url) 14 | res.raise_for_status() 15 | 16 | soup = bs4.BeautifulSoup(res.text) 17 | 18 | # コミック画像のURLを見つける 19 | comic_elem = soup.select('#comic img') 20 | if comic_elem == []: 21 | print('コミック画像が見つかりませんでした。') 22 | else: 23 | comic_url = 'http:' + comic_elem[0].get('src') 24 | # 画像をダウンロードする 25 | print('画像をダウンロード中 {}...'.format(comic_url)) 26 | res = requests.get(comic_url) 27 | res.raise_for_status() 28 | 29 | # 画像を./xkcdに保存する 30 | image_file = open(os.path.join('xkcd', os.path.basename(comic_url)), 'wb') 31 | for chunk in res.iter_content(100000): 32 | image_file.write(chunk) 33 | image_file.close() 34 | 35 | # PrevボタンのURLを取得する 36 | prev_link = soup.select('a[rel="prev"]')[0] 37 | url = 'http://xkcd.com' + prev_link.get('href') 38 | 39 | time.sleep(20) 40 | 41 | print('完了') 42 | -------------------------------------------------------------------------------- /ch11/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | The Website Title 4 | 5 |

Download my Python book from my website.

6 |

Learn Python the easy way!

7 |

By Al Sweigart

8 | -------------------------------------------------------------------------------- /ch11/lucky.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # lucky.py - Google検索結果をいくつか開く 3 | 4 | import requests, sys, webbrowser, bs4 5 | 6 | print('Googling...') # Googleページをダウンロード中にテキストを表示 7 | res = requests.get('http://google.com/search?q=' + ' '.join(sys.argv[1:])) 8 | res.raise_for_status() 9 | 10 | # 上位の検索結果のリンクを取得する 11 | soup = bs4.BeautifulSoup(res.text) 12 | link_elems = soup.select('.r a') 13 | 14 | # 各結果をブラウザのタブで開く 15 | num_open = min(5, len(link_elems)) 16 | for i in range(num_open): 17 | webbrowser.open('http://google.com' + link_elems[i].get('href')) 18 | 19 | -------------------------------------------------------------------------------- /ch11/mapIt.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # mapIt.py - コマンドラインやクリップボードに指定した住所の地図を開く 3 | 4 | import webbrowser, sys, pyperclip 5 | if len(sys.argv) > 1: 6 | # コマンドラインから住所を取得する 7 | address = ' '.join(sys.argv[1:]) 8 | else: 9 | # クリップボードからから住所を取得する 10 | address = pyperclip.paste() 11 | 12 | webbrowser.open('https://www.google.com/maps/place/' + address) 13 | 14 | -------------------------------------------------------------------------------- /ch12/censuspopdata.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch12/censuspopdata.xlsx -------------------------------------------------------------------------------- /ch12/example.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch12/example.xlsx -------------------------------------------------------------------------------- /ch12/produceSales.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch12/produceSales.xlsx -------------------------------------------------------------------------------- /ch12/readCensusExcel.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # readCensusExcel.py - 郡ごとに人口と人口調査標準地域の数を集計する 3 | 4 | import openpyxl, pprint # ❶ 5 | print('ワークブックを開いています...') 6 | wb = openpyxl.load_workbook('censuspopdata.xlsx') # ❷ 7 | sheet = wb.get_sheet_by_name('Population by Census Tract') # ❸ 8 | county_data = {} 9 | 10 | # county_dataに郡の人口と地域数を格納する 11 | print('行を読み込んでいます...') 12 | for row in range(2, sheet.max_row + 1): # ❹ 13 | # スプレッドシートの1行に、ひとつの人口調査標準地域のデータがある 14 | state = sheet['B' + str(row)].value 15 | county = sheet['C' + str(row)].value 16 | pop = sheet['D' + str(row)].value 17 | 18 | # この州のキーが確実に存在するようにする 19 | county_data.setdefault(state, {}) # ❶ 20 | # この州のこの郡のキーが確実に存在するようにする 21 | county_data[state].setdefault(county, {'tracts': 0, 'pop': 0}) # ❷ 22 | 23 | # 各行が人口調査標準地域を表すので、数を1つ増やす 24 | county_data[state][county]['tracts'] += 1 # ❸ 25 | # この人口調査標準地域の人口だけ郡の人口を増やす 26 | county_data[state][county]['pop'] += int(pop) # ❹ 27 | 28 | # 新しいテキストファイルを開き、county_dataの内容を書き込む 29 | print('結果を書き込み中...') 30 | result_file = open('census2010.py', 'w') 31 | result_file.write('all_data = ' + pprint.pformat(county_data)) 32 | result_file.close() 33 | print('完了') 34 | 35 | 36 | -------------------------------------------------------------------------------- /ch12/updateProduce.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # updateProduce.py - 農産物スプレッドシートの価格を訂正する 3 | 4 | import openpyxl 5 | 6 | wb = openpyxl.load_workbook('produceSales.xlsx') 7 | sheet = wb.get_sheet_by_name('Sheet') 8 | 9 | # 農産物の種類と、更新する価格 10 | PRICE_UPDATES = {'Garlic': 3.07, 11 | 'Celery': 1.19, 12 | 'Lemon': 1.27} 13 | 14 | # 行をループして価格を更新する 15 | for row_num in range(2, sheet.max_row): # 先頭行をスキップ ❶ 16 | produce_name = sheet.cell(row=row_num, column=1).value # ❷ 17 | if produce_name in PRICE_UPDATES: # ❸ 18 | sheet.cell(row=row_num, column=2).value = PRICE_UPDATES[produce_name] 19 | 20 | wb.save('updatedProduceSales.xlsx') # ❹ 21 | 22 | -------------------------------------------------------------------------------- /ch13/combinePdfs.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # combinePdfs.py - カレントディレクトリの全PDFをひとつのPDFに結合する 3 | 4 | import PyPDF2, os # ❶ 5 | 6 | # すべてのPDFファイル名を取得する 7 | pdf_files = [] 8 | for filename in os.listdir('.'): 9 | if filename.endswith('.pdf'): 10 | pdf_files.append(filename) # ❷ 11 | pdf_files.sort(key=str.lower) # ❸ 12 | 13 | pdf_writer = PyPDF2.PdfFileWriter() # ❹ 14 | 15 | # すべてのPDFファイルをループする 16 | for filename in pdf_files: 17 | pdf_file_obj = open(filename, 'rb') 18 | pdf_reader = PyPDF2.PdfFileReader(pdf_file_obj) 19 | # 先頭を除くすべてのページをループして追加する 20 | for page_num in range(1, pdf_reader.numPages): # ❶ 21 | page_obj = pdf_reader.getPage(page_num) 22 | pdf_writer.addPage(page_obj) 23 | 24 | # 結合したPDFをファイルに保存する 25 | pdf_output = open('allminutes.pdf', 'wb') 26 | pdf_writer.write(pdf_output) 27 | pdf_output.close() 28 | 29 | -------------------------------------------------------------------------------- /ch13/demo.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch13/demo.docx -------------------------------------------------------------------------------- /ch13/encrypted.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch13/encrypted.pdf -------------------------------------------------------------------------------- /ch13/guests.txt: -------------------------------------------------------------------------------- 1 | Prof. Plum 2 | Miss Scarlet 3 | Col. Mustard 4 | Al Sweigart 5 | Robocop -------------------------------------------------------------------------------- /ch13/meetingminutes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch13/meetingminutes.pdf -------------------------------------------------------------------------------- /ch13/meetingminutes2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch13/meetingminutes2.pdf -------------------------------------------------------------------------------- /ch13/readDocx.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | 3 | import docx 4 | 5 | def get_text(filename): 6 | doc = docx.Document(filename) 7 | full_text = [] 8 | for para in doc.paragraphs: 9 | full_text.append(para.text) 10 | return '\n'.join(full_text) 11 | -------------------------------------------------------------------------------- /ch13/watermark.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch13/watermark.pdf -------------------------------------------------------------------------------- /ch13/zophie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch13/zophie.png -------------------------------------------------------------------------------- /ch14/example.csv: -------------------------------------------------------------------------------- 1 | 4/5/2014 13:34,Apples,73 2 | 4/5/2014 3:41,Cherries,85 3 | 4/6/2014 12:46,Pears,14 4 | 4/8/2014 8:59,Oranges,52 5 | 4/10/2014 2:07,Apples,152 6 | 4/10/2014 18:10,Bananas,23 7 | 4/10/2014 2:40,Strawberries,98 8 | -------------------------------------------------------------------------------- /ch14/excelSpreadsheets.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch14/excelSpreadsheets.zip -------------------------------------------------------------------------------- /ch14/quickWeather.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # quickWeather.py - コマンドラインに指定した地名の天気予報を表示する 3 | 4 | import json, requests, sys 5 | 6 | # コマンドライン引数から地名を組み立てる 7 | if len(sys.argv) < 2: 8 | print('Usage: quickWeather.py location') 9 | sys.exit() 10 | location = ' '.join(sys.argv[1:]) 11 | 12 | # openweathermap.orgから取得したAPIキーを定義しておく 13 | APPID='' 14 | assert APPID != '', 'APPIDを定義してください。' 15 | 16 | # OpenWeatherMap.orgのAPIからJSONデータをダウンロードする 17 | url ='http://api.openweathermap.org/data/2.5/forecast/daily?q={}&cnt=3&appid={}'.format(location, APPID) 18 | response = requests.get(url) 19 | response.raise_for_status() 20 | 21 | # JSONデータからPython変数に読み込む 22 | weather_data = json.loads(response.text) 23 | # 天気予報の情報を表示する 24 | w = weather_data['list'] # ❶ 25 | print('{}の現在の天気:'.format(location)) 26 | print(w[0]['weather'][0]['main'], '-', w[0]['weather'][0]['description']) 27 | print() 28 | print('明日:') 29 | print(w[1]['weather'][0]['main'], '-', w[1]['weather'][0]['description']) 30 | print() 31 | print('明後日:') 32 | print(w[2]['weather'][0]['main'], '-', w[2]['weather'][0]['description']) 33 | 34 | 35 | -------------------------------------------------------------------------------- /ch14/removeCsvHeader.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # removeCsvHeader.py - カレントディレクトリの全CSVファイルから見出しを削除する 3 | 4 | import csv, os 5 | 6 | os.makedirs('headerRemoved', exist_ok=True) 7 | 8 | # カレントディレクトリの全ファイルをループする 9 | for csv_filename in os.listdir('.'): 10 | if not csv_filename.endswith('.csv'): 11 | continue # CSVファイルでなければスキップ ❶ 12 | 13 | print('見出し削除中 ' + csv_filename + '...') 14 | 15 | # CSVファイルを読み込む(最初の行をスキップする) 16 | csv_rows = [] 17 | csv_file_obj = open(csv_filename) 18 | reader_obj = csv.reader(csv_file_obj) 19 | for row in reader_obj: 20 | if reader_obj.line_num == 1: 21 | continue # 最初の行をスキップする 22 | csv_rows.append(row) 23 | csv_file_obj.close() 24 | 25 | # CSVファイルを書き出す 26 | csv_file_obj = open(os.path.join('headerRemoved', csv_filename), 'w', 27 | newline='') 28 | csv_writer = csv.writer(csv_file_obj) 29 | for row in csv_rows: 30 | csv_writer.writerow(row) 31 | csv_file_obj.close() 32 | 33 | -------------------------------------------------------------------------------- /ch14/removeCsvHeader.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch14/removeCsvHeader.zip -------------------------------------------------------------------------------- /ch15/alarm.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch15/alarm.wav -------------------------------------------------------------------------------- /ch15/calcProd.py: -------------------------------------------------------------------------------- 1 | import time 2 | def calc_prod(): # ❶ 3 | # 1~99,999の積を求める 4 | product = 1 5 | for i in range(1, 100000): 6 | product = product * i 7 | return product 8 | 9 | start_time = time.time() # ❷ 10 | prod = calc_prod() 11 | end_time = time.time() # ❸ 12 | print('計算結果は{}桁です。'.format(len(str(prod)))) # ❹ 13 | print('計算時間は{}秒でした。'.format(end_time - start_time)) # ❺ 14 | -------------------------------------------------------------------------------- /ch15/countdown.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # countdown.py - シンプルなカウントダウンスクリプト 3 | 4 | import time, subprocess 5 | 6 | timeLeft = 60 7 | while timeLeft > 0: 8 | print(timeLeft, end='') 9 | time.sleep(1) 10 | timeLeft = timeLeft - 1 11 | 12 | # カウントダウン後に音声ファイルを再生する 13 | subprocess.Popen(['start','alarm.wav'], shell=True) 14 | -------------------------------------------------------------------------------- /ch15/multidownloadXkcd.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # multidownloadXkcd.py - XKCDコミックをマルチスレッドでダウンロードする 3 | 4 | import requests, os, bs4, threading 5 | import time 6 | os.makedirs('xkcd', exist_ok=True) # ./xkcdにコミックを保存 ❶ 7 | 8 | def download_xkcd(start_comic, end_comic): # ❷ 9 | for url_number in range(start_comic, end_comic): # ❸ 10 | # ページをダウンロードする 11 | print('ページをダウンロード中 http://xkcd.com/{}...'.format(url_number)) 12 | res = requests.get('http://xkcd.com/{}'.format(url_number)) # ❹ 13 | res.raise_for_status() 14 | 15 | soup = bs4.BeautifulSoup(res.text) # ❺ 16 | 17 | # コミック画像のURLを見つける 18 | comic_elem = soup.select('#comic img') # ❻ 19 | if comic_elem == []: 20 | print('コミック画像が見つかりませんでした。') 21 | else: 22 | comic_url = 'http:' + comic_elem[0].get('src') # ❼ 23 | # 画像をダウンロードする 24 | print('画像をダウンロード中 {}...'.format(comic_url)) 25 | res = requests.get(comic_url) # ❽ 26 | res.raise_for_status() 27 | 28 | # 画像を./xkcdに保存する 29 | image_file = open(os.path.join('xkcd', os.path.basename(comic_url)), 'wb') 30 | for chunk in res.iter_content(100000): 31 | image_file.write(chunk) 32 | image_file.close() 33 | time.sleep(20) 34 | 35 | # `Thread`オブジェクトを生成して開始する 36 | download_threads = [] # 全Threadオブジェクトのリスト 37 | for i in range(1, 1400, 100): # 14回ループし、14個のスレッドを生成 38 | download_thread = threading.Thread(target=download_xkcd, args=(i, i + 100)) 39 | download_threads.append(download_thread) 40 | download_thread.start() 41 | 42 | # すべてのスレッドが終了するのを待つ 43 | for download_thread in download_threads: 44 | download_thread.join() 45 | print('完了') 46 | 47 | -------------------------------------------------------------------------------- /ch15/stopwatch.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # stopwatch.py - シンプルなストップウォッチプログラム 3 | 4 | import time 5 | 6 | # プログラムの説明を表示する 7 | print('Enterを押すと開始します。その後、Enterを押せば経過時間を表示します。Ctrl-Cで終了します。') 8 | input() # Enterを押すと開始 9 | print('スタート') 10 | start_time = time.time() # プログラムと最初のラップの開始時間 11 | last_time = start_time 12 | lap_num = 1 13 | 14 | # ラップタイムを計測する 15 | try: # ❶ 16 | while True: # ❷ 17 | input() 18 | now = time.time() 19 | lap_time = round(now - last_time, 2) # ❸ 20 | total_time = round(now - start_time, 2) # ❹ 21 | print('ラップ #{}: {} ({})'.format(lap_num, total_time, lap_time), end='') # ❺ 22 | lap_num += 1 23 | last_time = now # ラップタイムをリエット 24 | except KeyboardInterrupt: # ❻ 25 | # Ctrl-C例外を処理してエラーメッセージを表示しないようにする 26 | print('\n終了.') 27 | 28 | -------------------------------------------------------------------------------- /ch15/threadDemo.py: -------------------------------------------------------------------------------- 1 | import threading, time 2 | print('プログラム開始') 3 | 4 | def take_a_nap(): # ❶ 5 | time.sleep(5) 6 | print('起きた!') 7 | 8 | thread_obj = threading.Thread(target=take_a_nap) # ❷ 9 | thread_obj.start() # ❸ 10 | 11 | print('プログラム終了') 12 | -------------------------------------------------------------------------------- /ch16/duesRecords.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch16/duesRecords.xlsx -------------------------------------------------------------------------------- /ch16/jimapsample.py: -------------------------------------------------------------------------------- 1 | import imapclient 2 | from backports import ssl 3 | context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) 4 | imap_obj = imapclient.IMAPClient('imap.gmail.com', ssl=True, ssl_context=context) 5 | imap_obj.login('your_address@gmail.com', 'YOUR_PASSWORD') 6 | 7 | imap_obj.select_folder('INBOX', readonly=True) 8 | 9 | UIDs = imap_obj.search('(SINCE 05-Jul-2014)') 10 | 11 | print(UIDs) 12 | -------------------------------------------------------------------------------- /ch16/jsmtpsample.py: -------------------------------------------------------------------------------- 1 | import smtplib 2 | from email.header import Header 3 | from email.mime.text import MIMEText 4 | 5 | charset = 'iso-2022-jp' 6 | 7 | msg = MIMEText('日本語の本文', 'plain', charset) 8 | msg['Subject'] = Header('日本語の件名'.encode(charset), charset) 9 | 10 | smtp_obj = smtplib.SMTP('smtp.gmail.com', 587) 11 | smtp_obj.ehlo() 12 | smtp_obj.starttls() 13 | smtp_obj.login('your_address@gmail.com', 'YOUR_PASSWORD') 14 | smtp_obj.sendmail('your_address@gmail.com', 'to_address@gmail.com', msg.as_string()) 15 | smtp_obj.quit() 16 | -------------------------------------------------------------------------------- /ch16/sendDuesReminders.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # sendDuesReminders.py - スプレッドシートの支払い状況に基づきメールを送信 3 | 4 | import openpyxl, smtplib, sys 5 | 6 | # スプレッドシートを開き最近の支払い状況を取得 7 | wb = openpyxl.load_workbook('duesRecords.xlsx') # ❶ 8 | sheet = wb.get_sheet_by_name('Sheet1') # ❷ 9 | 10 | last_col = sheet.get_highest_column() # ❸ 11 | latest_month = sheet.cell(row=1, column=last_col).value # ❹ 12 | 13 | # 会員の支払い状況を調べる 14 | unpaid_members = {} 15 | for r in range(2, sheet.get_highest_row() + 1): # ❶ 16 | payment = sheet.cell(row=r, column=last_col).value # ❷ 17 | if payment != 'paid': 18 | name = sheet.cell(row=r, column=1).value # ❸ 19 | email = sheet.cell(row=r, column=2).value # ❹ 20 | unpaid_members[name] = email # ❺ 21 | 22 | # メールアカウントにログインする 23 | smtp_obj = smtplib.SMTP('smtp.gmail.com', 587) 24 | smtp_obj.ehlo() 25 | smtp_obj.starttls() 26 | smtp_obj.login('my_email_address@gmail.com', sys.argv[1]) 27 | 28 | # TODO: リマインダーメールを送信する 29 | for name, email in unpaidMembers.items(): 30 | body = """Subject: {} dues unpaid. 31 | Dear {}, 32 | Records show that you have not paid dues for {}. Please make this payment as soon as possible. Thank you! 33 | """.format(latest_month, name, latest_month) # ❶ 34 | print('メール送信中 {}...'.format(email)) # ❷ 35 | sendmail_status = smtp_obj.sendmail('my_email_address@gmail.com', email, body) # ❸ 36 | 37 | if sendmail_status != {}: # ❹ 38 | print('{}へメール送信中に問題が起こりました: {}'.format(email, endmail_status)) 39 | 40 | smtp_obj.quit() 41 | 42 | -------------------------------------------------------------------------------- /ch16/textmyself.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # textmyself.py - 引数の文字列をSMSで送信するtextmyself()関数を定義する 3 | 4 | # 設定値 5 | account_SID = 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' 6 | auth_token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' 7 | twilio_number = '+12345678901' # 購入した米国の電話番号 8 | my_number = '+819012345678' # 自分の携帯電話番号 9 | 10 | try:from twilio.rest import TwilioRestClient 11 | except: from twilio.rest import Client as TwilioRestClient 12 | 13 | def textmyself(message): 14 | twilio_cli = TwilioRestClient(account_SID, auth_token) 15 | twilio_cli.messages.create(body=message, from_=twilio_number, to=my_number) # ? 16 | -------------------------------------------------------------------------------- /ch16/torrentStarter.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # メール経由で命令をチェックして実行します。 3 | # ここでは、BitTorrentの"magnet"リンクをチェックし、 4 | # Torrentプログラムを実行します。 5 | 6 | import smtplib, imapclient, pyzmail, logging, traceback, time, subprocess 7 | from backports import ssl # gmailに必要 8 | 9 | logging.basicConfig(filename='torrentStarterLog.txt', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') 10 | 11 | # 以下の変数を設定してください。 12 | MY_EMAIL = 'my_commander@gmail.com' # このメールからのみ応答する 13 | BOT_EMAIL = 'my_bot@gmail.com' # ボットのメールアドレス 14 | BOT_EMAIL_PASSWORD = 'my_bot_password' 15 | IMAP_SERVER = 'imap.gmail.com' 16 | SMTP_SERVER = 'imap.gmail.com' 17 | SMTP_PORT = 465 18 | # Torrentのプログラムパス 19 | TORRENT_PROGRAM = 'C:\\Program Files (x86)\\qBittorrent\\qbittorrent.exe' 20 | 21 | assert BOT_EMAIL != MY_EMAIL, "ボットのアドレスは別にしてください." 22 | 23 | 24 | def getInstructionEmails(): 25 | # imapClientでログイン 26 | logging.debug('Connecting to IMAP server at %s...' % (IMAP_SERVER)) 27 | context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) 28 | imapCli = imapclient.IMAPClient(IMAP_SERVER, ssl=True, ssl_context=context) 29 | imapCli.login(BOT_EMAIL, BOT_EMAIL_PASSWORD) 30 | imapCli.select_folder('INBOX') 31 | logging.debug('Connected.') 32 | 33 | # メールから命令を取得 34 | instructions = [] 35 | UIDs = imapCli.search(['FROM', MY_EMAIL]) 36 | rawMessages = imapCli.fetch(UIDs, ['BODY[]']) 37 | for UID in rawMessages.keys(): 38 | # 生のメッセージを解析 39 | message = pyzmail.PyzMessage.factory(rawMessages[UID][b'BODY[]']) 40 | if message.html_part != None: 41 | body = message.html_part.get_payload().decode(message.html_part.charset) 42 | if message.text_part != None: 43 | # HTMLとテキストの両方があればテキストの方を用います 44 | body = message.text_part.get_payload().decode(message.text_part.charset) 45 | 46 | # メール本文から命令を抽出 47 | instructions.append(body) 48 | 49 | # 受信したメールを削除する。 50 | if len(UIDs) > 0: 51 | imapCli.delete_messages(UIDs) 52 | imapCli.expunge() 53 | 54 | imapCli.logout() 55 | 56 | return instructions 57 | 58 | 59 | def parseInstructionEmail(instruction): 60 | # 命令を実行し、応答メールを送ります 61 | responseBody = 'Subject: Instruction completed.\nInstruction received and completed.\nResponse:\n' 62 | 63 | # メール本文を解析し、命令を取り出します。 64 | lines = instruction.split('\n') 65 | for line in lines: 66 | if line.startswith('magnet:?'): 67 | # Torrentプログラムを実行 68 | subprocess.Popen(TORRENT_PROGRAM + ' ' + line) 69 | print(TORRENT_PROGRAM + ' ' + line) 70 | responseBody += 'Downloading magnet link.\n' 71 | 72 | # ボットが命令を実行したことを確認する応答文をメールする 73 | logging.debug('Connecting to SMTP server at %s to send confirmation email...' % (SMTP_SERVER)) 74 | 75 | smtpCli = smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT) 76 | smtpCli.ehlo() 77 | smtpCli.starttls() 78 | smtpCli.login(BOT_EMAIL, BOT_EMAIL_PASSWORD) 79 | logging.debug('Connected.') 80 | smtpCli.sendmail(BOT_EMAIL, MY_EMAIL, responseBody) 81 | logging.debug('Confirmation email sent.') 82 | smtpCli.quit() 83 | 84 | 85 | # メールをチェックし命令実行する無限ループを開始します。 86 | print('メールボットが始動しました。停止するにはCtrl-Cを押してください。') 87 | logging.debug('Email bot started.') 88 | while True: 89 | try: 90 | logging.debug('Getting instructions from email...') 91 | instructions = getInstructionEmails() 92 | for instruction in instructions: 93 | logging.debug('Doing instruction: ' + instruction) 94 | parseInstructionEmail(instruction) 95 | except Exception as err: 96 | logging.error(traceback.format_exc()) 97 | 98 | # 15分待って、再度チェックします。 99 | logging.debug('Done processing instructions. Pausing for 15 minutes.') 100 | time.sleep(60 * 15) 101 | -------------------------------------------------------------------------------- /ch17/catlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch17/catlogo.png -------------------------------------------------------------------------------- /ch17/fontlist.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from PIL import Image, ImageDraw, ImageFont 5 | import os 6 | 7 | FONT_DIR = r'C:\Windows\Fonts' 8 | EXAMPLE = '愛Uniqフォント' 9 | FONT_SIZE = 16 10 | 11 | fonts = [] 12 | for fname in os.listdir(FONT_DIR): 13 | if fname.endswith('.ttf') or fname.endswith('.ttc'): 14 | fonts.append(fname) 15 | 16 | im = Image.new('RGBA', (750, len(fonts) * FONT_SIZE), 'white') 17 | draw = ImageDraw.Draw(im) 18 | 19 | y = 0 20 | for font in fonts: 21 | draw.text((0, y), font, fill='red') 22 | for i in range(0, 4): 23 | try: 24 | f = ImageFont.truetype(font, FONT_SIZE, index=i) 25 | draw.text((150 + 150*i, y), EXAMPLE, fill='black', font=f) 26 | except: 27 | pass 28 | y += FONT_SIZE 29 | 30 | im.save('fontlist.png') 31 | -------------------------------------------------------------------------------- /ch17/guests.txt: -------------------------------------------------------------------------------- 1 | Prof. Plum 2 | Miss Scarlet 3 | Col. Mustard 4 | Al Sweigart 5 | Robocop -------------------------------------------------------------------------------- /ch17/resizeAndAddLogo.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # resizeAndAddLogo.py - カレントディレクトリのすべての画像を300x300に収まる 3 | # ようにサイズ変更し、catlogo.pngを右下に追加する。 4 | 5 | import os 6 | from PIL import Image 7 | 8 | SQUARE_FIT_SIZE = 300 # ❶ 9 | LOGO_FILENAME = 'catlogo.png' # ❷ 10 | 11 | logo_im = Image.open(LOGO_FILENAME) # ❸ 12 | logo_width, logo_height = logo_im.size # ❹ 13 | 14 | os.makedirs('withLogo', exist_ok=True) 15 | 16 | # カレントディレクトリの全画像をループする 17 | for filename in os.listdir('.'): # ❶ 18 | if not (filename.endswith('.png') or filename.endswith('.jpg')) \ 19 | or filename == LOGO_FILENAME: # ❷ 20 | continue # 画像以外とロゴ画像はスキップする # ❸ 21 | im = Image.open(filename) # ❹ 22 | 23 | # 画像をサイズ変更する 24 | im.thumbnail((SQUARE_FIT_SIZE, SQUARE_FIT_SIZE)) 25 | width, height = im.size 26 | 27 | # ロゴを追加する 28 | print('ロゴを追加中 {}...'.format(filename)) # ❶ 29 | im.paste(logo_im, (width-logo_width, height-logo_height), logo_im) # ❷ 30 | 31 | # 変更を保存する 32 | im.save(os.path.join('withLogo', filename)) # ❸ 33 | 34 | 35 | -------------------------------------------------------------------------------- /ch17/zophie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/ch17/zophie.png -------------------------------------------------------------------------------- /ch18/formFiller.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # formFiller.py - フォームの自動入力 3 | 4 | import pyautogui, time 5 | 6 | # 以下の値は実際に調べたものに置き換えること。 7 | name_field = (648, 319) 8 | submit_button = (651, 817) 9 | submit_button_color = (75, 141, 249) 10 | submit_another_link = (760, 224) 11 | 12 | form_data = [{'name': 'Alice', 'fear': 'eavesdroppers', 'source': 'wand', 13 | 'robocop': 4, 'comments': 'Tell Bob I said hi.'}, 14 | {'name': 'Bob', 'fear': 'bees', 'source': 'amulet', 'robocop': 4, 15 | 'comments': 'n/a'}, 16 | {'name': 'Carol', 'fear': 'puppets', 'source': 'crystal ball', 17 | 'robocop': 1, 'comments': 'Please take the puppets out of the 18 | break room.'}, 19 | {'name': 'Alex Murphy', 'fear': 'ED-209', 'source': 'money', 20 | 'robocop': 5, 'comments': 'Protect the innocent. Serve the public 21 | trust. Uphold the law.'}, 22 | ] 23 | 24 | pyautogui.PAUSE = 0.5 25 | 26 | for person in form_data: 27 | # ユーザーがスクリプトを中断する機会を与える 28 | print('>>> 5秒間一時停止中。中断するにはCtrl-Cを押してください。<<<') 29 | time.sleep(5) # ❶ 30 | 31 | # フォームページが読み込まれるのを待つ 32 | while not pyautogui.pixelMatchesColor(submit_button[0], submit_button[1], submit_button_color): # ❷ 33 | time.sleep(0.5) 34 | 35 | print('{}の情報を入力中...'.format(person['name'])) # ❶ 36 | pyautogui.click(name_field[0], name_field[1]) # ❷ 37 | 38 | # Name欄を入力する 39 | pyautogui.typewrite(person['name'] + '\t') # ❸ 40 | 41 | # Greatest Fear(s)欄を入力する 42 | pyautogui.typewrite(person['fear'] + '\t') # ❹ 43 | 44 | # Source of Wizard Powers欄を選択する 45 | if person['source'] == 'wand': # ❶ 46 | pyautogui.typewrite(['down', '\t']) # ❷ 47 | elif person['source'] == 'amulet': 48 | pyautogui.typewrite(['down', 'down', '\t']) 49 | elif person['source'] == 'crystal ball': 50 | pyautogui.typewrite(['down', 'down', 'down', '\t']) 51 | elif person['source'] == 'money': 52 | pyautogui.typewrite(['down', 'down', 'down', 'down', '\t']) 53 | 54 | # RoboCop欄を選択する 55 | if person['robocop'] == 1: # ❸ 56 | pyautogui.typewrite([' ', '\t']) # ❹ 57 | elif person['robocop'] == 2: 58 | pyautogui.typewrite(['right', '\t']) 59 | elif person['robocop'] == 3: 60 | pyautogui.typewrite(['right', 'right', '\t']) 61 | elif person['robocop'] == 4: 62 | pyautogui.typewrite(['right', 'right', 'right', '\t']) 63 | elif person['robocop'] == 5: 64 | pyautogui.typewrite(['right', 'right', 'right', 'right', '\t']) 65 | 66 | # Additional Comments欄を入力する 67 | pyautogui.typewrite(person['comments'] + '\t') 68 | 69 | # Submitをクリックする 70 | pyautogui.press('enter') 71 | 72 | # 次のページが読み込まれるのを待つ 73 | print('送信ボタンを押しました。') 74 | time.sleep(5) 75 | 76 | # Submit another responseリンクをクリックする 77 | pyautogui.click(submit_another_link[0], submit_another_link[1]) 78 | 79 | -------------------------------------------------------------------------------- /ch18/mouseNow.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # mouseNow.py - マウスカーソル座標を表示する 3 | 4 | import pyautogui 5 | print('中断するにはCtrl-Cを押してください。') 6 | 7 | try: 8 | while True: 9 | # マウス座標を取得して表示する 10 | x, y = pyautogui.position() 11 | position_str = 'X: ' + str(x).rjust(4) + ' Y: ' + str(y).rjust(4) 12 | print(position_str, end='') 13 | print('\b' * len(position_str), end='', flush=True) # ❶ 14 | 15 | except KeyboardInterrupt: # ❶ 16 | print('\n終了。') # ❷ 17 | -------------------------------------------------------------------------------- /ch18/mouseNow2.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # mouseNow.py - マウスカーソル座標を表示する 3 | 4 | import pyautogui 5 | print('中断するにはCtrl-Cを押してください。') 6 | 7 | try: 8 | while True: 9 | # マウス座標を取得して表示する 10 | x, y = pyautogui.position() 11 | position_str = 'X: ' + str(x).rjust(4) + ' Y: ' + str(y).rjust(4) 12 | pixel_color = pyautogui.screenshot().getpixel((x, y)) 13 | position_str += ' RGB: (' + str(pixel_color[0]).rjust(3) 14 | position_str += ', ' + str(pixel_color[1]).rjust(3) 15 | position_str += ', ' + str(pixel_color[2]).rjust(3) + ')' 16 | print(position_str, end='') 17 | print('\b' * len(position_str), end='', flush=True) # ❶ 18 | 19 | except KeyboardInterrupt: # ❶ 20 | print('\n終了。') # ❷ 21 | -------------------------------------------------------------------------------- /ch18/spiralDraw.py: -------------------------------------------------------------------------------- 1 | import pyautogui, time 2 | time.sleep(5) # ❶ 3 | pyautogui.click() # お絵かきアプリをクリックしてフォーカスする ❷ 4 | distance = 200 5 | while distance > 0: 6 | pyautogui.dragRel(distance, 0, duration=0.2) # 右へ移動 ❸ 7 | distance = distance - 5 # ❹ 8 | pyautogui.dragRel(0, distance, duration=0.2) # 下へ移動 ❺ 9 | pyautogui.dragRel(-distance, 0, duration=0.2) # 左へ移動 ❻ 10 | distance = distance - 5 11 | pyautogui.dragRel(0, -distance, duration=0.2) # 上へ移動 12 | 13 | -------------------------------------------------------------------------------- /practice_pj/practice_projects_20170727.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/automatestuff-ja/e6dfd4d51609da5307a1be8ac8c7cd4c99a99e37/practice_pj/practice_projects_20170727.zip --------------------------------------------------------------------------------