Amazon S3 and boto3
忘れそうなのでメモついでにAWS CLIとPythonでS3の使い方をまとめる。
S3の設定
- バケット作成
aws s3 mb s3://バケット名
- バケットにコピー
aws s3 sync images s3://バケット名/images/
- バケットデータ削除
aws s3 rm s3://バケット名/ --include 'img*.jpg' --recursive --dryrun でためして aws s3 rm s3://バケット名/ --include 'img*.jpg' --recursive
- バケットのデータ表示
aws s3 ls s3://バケット名/images/
バケットを作るときに以下のNGが出た場合はAWS内で重複しているのでバケット名を替える必要がある
make_bucket failed: s3://imagebucket An error occurred (BucketAlreadyExists) when calling the CreateBucket operation: The requested bucket name is not available. The bucket namespace is shared by all users of the system. Please select a different name and try again.
「/」のつけ忘れでフォルダ、ファイルが混同するので注意が必要
最後にpythonでimg-catの中身をS3に転送して結果をCSVに出力するプログラムを書いてみた。
import boto3, pathlib s3 = boto3.client('s3') files = pathlib.Path('img-cat/').glob('*.jpg') for file in files: s3.upload_file('img-cat/'+ file.name, 'バケット名', 'img-cat/' + file.name) objs = s3.list_objects_v2(Bucket='バケット名') csv = '{},{}\n'.format('file', 'size') for o in objs.get('Contents'): key = o.get('Key') size = o.get('Size') if key[-4:]=='.jpg': csv += '{},{}\n'.format(key, int(size)) f = open('uplist.csv', 'w') f.write(csv)
python スクレイピング その7
はじめに
今回はScrapyを使用してNPBの種類ごとの歴代最高記録のURLを取得してみた。
準備
Scrapyのインストール
$ pip install scrapy
Scrapyプロジェクトの作成
$ scrapy startproject npb $ cd npb $ tree . ├── npb │ ├── __init__.py │ ├── items.py │ ├── middlewares.py │ ├── pipelines.py │ ├── settings.py │ └── spiders │ └── __init__.py └── scrapy.cfg 2 directories, 7 files
設定は「settings.py」にまとまっている模様。
item.pyに「url = scrapy.Field()」を追加する。
- items.py
# Define here the models for your scraped items # # See documentation in: # https://docs.scrapy.org/en/latest/topics/items.html import scrapy class NpbItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() url = scrapy.Field() pass
# -*- coding: utf-8 -*- import scrapy class NpbSpiderSpider(scrapy.Spider): name = "npb_spider" allowed_domains = ["npb.jp"] start_urls = ["https://npb.jp/bis/history/"] def parse(self, response): for url in response.css('td.allTimeBatBmn a::attr("href")'): yield response.follow(url) for url in response.css('td.allTimePitBmn a::attr("href")'): yield response.follow(url) pass
- 実行方法&結果
$ scrapy crawl npb_spider 2021-05-15 13:53:42 [scrapy.utils.log] INFO: Scrapy 2.5.0 started (bot: npb) 2021-05-15 13:53:42 [scrapy.utils.log] INFO: Versions: lxml 4.6.1.0, libxml2 2.9.10, cssselect 1.1.0, parsel 1.6.0, w3lib 1.22.0, Twisted 21.2.0, Python 3.8.5 (default, Sep 4 2020, 07:30:14) - [GCC 7.3.0], pyOpenSSL 19.1.0 (OpenSSL 1.1.1h 22 Sep 2020), cryptography 3.1.1, Platform Linux-5.8.0-45-generic-x86_64-with-glibc2.10 2021-05-15 13:53:42 [scrapy.utils.log] DEBUG: Using reactor: twisted.internet.epollreactor.EPollReactor 2021-05-15 13:53:42 [scrapy.crawler] INFO: Overridden settings: 〜〜中略〜〜 2021-05-15 13:53:43 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://npb.jp/bis/history/> (referer: None) 2021-05-15 13:53:45 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://npb.jp/bis/history/ltb_g.html> (referer: https://npb.jp/bis/history/) 2021-05-15 13:53:46 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://npb.jp/bis/history/ltp_l.html> (referer: https://npb.jp/bis/history/) 2021-05-15 13:53:48 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://npb.jp/bis/history/ltp_w.html> (referer: https://npb.jp/bis/history/) 2021-05-15 13:53:49 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://npb.jp/bis/history/ltp_nbb.html> (referer: https://npb.jp/bis/history/) 2021-05-15 13:53:50 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://npb.jp/bis/history/ltp_sho.html> (referer: https://npb.jp/bis/history/) 〜〜中略〜〜
- 簡単な解説
下記でscrapyのプロジェクトを作成している。
$ scrapy startproject npb
$ cd npb
ここでclass名に合致したすべてのaタグのhrefを取得している。
打者成績と投手成績があるので2回取得する処理にしている。
class NpbSpiderSpider(scrapy.Spider): name = "npb_spider" allowed_domains = ["npb.jp"] start_urls = ["https://npb.jp/bis/history/"] def parse(self, response): for url in response.css('td.allTimeBatBmn a::attr("href")'): yield response.follow(url) for url in response.css('td.allTimePitBmn a::attr("href")'): yield response.follow(url) pass
まとめ
簡単かつ大規模なスクレイピングの機能が作れそう。
プロジェクト単位で管理できるので、なにかしらの素材集めをプロジェクトとして管理できそう。
python スクレイピング その6
はじめに
今回は巨人の藤田元司監督のWikiページを画像でコピーする処理を作成した。
準備
- sele_headless.py
from selenium import webdriver from selenium.webdriver.chrome.options import Options from webdriver_manager.chrome import ChromeDriverManager import time url = 'https://ja.wikipedia.org/wiki/%E8%97%A4%E7%94%B0%E5%85%83%E5%8F%B8' options = Options() options.add_argument('--headless') driver = webdriver.Chrome(ChromeDriverManager().install(), options=options) driver.get(url) width = driver.execute_script("return document.body.scrollWidth") height = driver.execute_script("return document.body.scrollHeight") driver.set_window_size(width, height) time.sleep(3) driver.save_screenshot("screenshot.png") time.sleep(3) driver.quit()
- 実行方法
$ python sele_screenshot.py
- 簡単な解説
下記で現行のChromeバージョンから最適なドライバを選択してくれる。
driver = webdriver.Chrome(ChromeDriverManager().install(), options=options)
ここがこの処理の肝だと感じる。
スクリプト実行を意図的に行える。
width = driver.execute_script("return document.body.scrollWidth") height = driver.execute_script("return document.body.scrollHeight") driver.set_window_size(width, height)
まとめ
「ChromeDriverManager().install()」はパス名記載しないので、すごくスタイリッシュで格好良い。
execute_script()の応用の幅がありそう。
python スクレイピング その4
はじめに
今回はseleniumでSlashdotを開いて、ブラウザを操作して結果を表示するプログラムを書いてみた。
準備
最初にpipで以下をインストールしておく。
$ pip install selenium $ pip install chromedriver $ pip install chromedriver-binary==90.0.4430.93
※chromedriver-binaryは自分の環境のバージョンを記載。
- sele.py
from selenium import webdriver from bs4 import BeautifulSoup import time url = 'https://srad.jp/' keyword = 'Linux' driver = webdriver.Chrome(executable_path="chromedriverのパス") driver.get(url) search = driver.find_element_by_name("fhfilter") search.send_keys(keyword) search.submit() soup = BeautifulSoup(driver.page_source, "html.parser") results = soup.find_all("h2", attrs={"class": "story"}) for i, result in enumerate(results): tag = result.find("a") print("%d: %s : %s" % (i + 1, tag.get_text(), "https:"+tag["href"])) driver.quit()
- 実行方法と結果
$ python sele.py 1: Windows 10 Insider Preview ビルド21376、WSLのLinux GUIアプリサポートで発生していた問題を解決 : https://linux.srad.jp/story/21/05/08/0455217/ 2: Vine Linuxの現状は?(追記: リリース版終了が宣言される) : https://linux.srad.jp/story/21/05/04/0459248/ 3: Windows 10 Insider PreviewのLinux GUIアプリ実行サポート、現時点ではHaswell世代以降のCPUが必要 : https://linux.srad.jp/story/21/04/29/0430220/ 4: ミネソタ大研究者、研究のためとしてLinuxカーネルに意図的に脆弱性コードをコミット : https://linux.srad.jp/story/21/04/25/1954223/ 5: M1 Mac正式対応のOS仮想化ソフト「Parallels Desktop 16.5」が公開 : https://linux.srad.jp/story/21/04/19/0129216/ 6: SCO Linux FUDは滅びぬ、何度でも蘇るさ! : https://linux.srad.jp/story/21/04/04/1616223/ 7: FSF運営チーム、執行役員をはじめ3名が辞任へ : https://opensource.srad.jp/story/21/04/03/0837231/ 8: Linux Foundation、Linux 30周年記念ロゴ画像を公開 : https://linux.srad.jp/story/21/03/21/058252/ 9: 7-Zip 20.01 alpha、Linuxに対応 : https://linux.srad.jp/story/21/03/16/1622251/ 10: 火星の空飛ぶLinux : https://linux.srad.jp/story/21/03/11/1655218/ 11: Linux 5.12 rc2、重大なバグ修正のため予定より数日早くリリース : https://linux.srad.jp/story/21/03/07/0039241/ 12: Linus Torvalds、6日間の電源喪失を切り抜ける : https://linux.srad.jp/story/21/03/02/1653247/ 13: アップデート適用が進まないLinux Mint、より多くのユーザーに迅速なアップデート適用を実行してもらうための改善計画 : https://linux.srad.jp/story/21/02/28/1940228/ 14: M1 Mac上でUbuntuデスクトップの起動に成功との発表。ネットワーク機能も動作可能 : https://linux.srad.jp/story/21/01/24/1511237/
- 注意
sele.py を実行しても、Chromeのバージョンとドライバのバージョンが違うと動かない。
下記でバージョン一覧が取れるのでChromeのバージョンが90版台なら、chromedriver-binaryも同じ番号帯を選んで再インストールする。
$ pip install chromedriver-binary==
- 簡単な解説
executable_pathは未指定でも良いらしいが自分の環境だとNGだったので指定している。
from selenium import webdriver from bs4 import BeautifulSoup import time url = 'https://srad.jp/' keyword = 'Linux' driver = webdriver.Chrome(executable_path="chromedriverのパス") driver.get(url)
「fhfilter」が検索ボックスでそこにsend_keys()でキーワードを送信してsubmit()している。
search = driver.find_element_by_name("fhfilter")
search.send_keys(keyword)
search.submit()
「driver.page_source」でまるっと結果を取得している。
その結果をBeautifulSoupで解析して、enumerateで回して表示している。
題名はaタグの内容をget_text()で取得し、アトリビュートのhrefを取得して表示している。
soup = BeautifulSoup(driver.page_source, "html.parser") results = soup.find_all("h2", attrs={"class": "story"}) for i, result in enumerate(results): tag = result.find("a") print("%d: %s : %s" % (i + 1, tag.get_text(), "https:"+tag["href"])) driver.quit()
まとめ
forの表示は最初はダラダラ3行くらい書いてたけど、今の書き方のほうがスッキリして良い。
この癖を付けていきたい。
python スクレイピング その3
はじめに
今回はYahoo画像検索で猫の画像をかき集めて保存するプログラムを書いてみた。
準備
- imageDownload.py
import requests, os, json, traceback from bs4 import BeautifulSoup path = './img-cat/' maxPage = 2 searchText = '猫' def imageDownload(searchWord, index): url = 'https://search.yahoo.co.jp/image/search?p={searchWord}&ei=UTF-8&b={index}' resp = requests.get(url.format(searchWord=searchWord, index=index)) soup = BeautifulSoup(resp.content, 'html.parser') #分析 script = soup.find("script", attrs={"id": "__NEXT_DATA__"}) # タグの内容を取り出してJSON変換 # <script>なので中身はstringで取るみたい。 scriptJson = json.loads(script.string) imageUrlList = [] for algos in scriptJson['props']['initialProps']['pageProps']['algos']: imageUrlList.append(algos['main']['url']) #画像取得 imageObjectList = [] for imageUrl in imageUrlList: resp = requests.get(imageUrl) imageObjectList.append(resp.content) #画像リスト返却 return imageObjectList def imageWrite(imageList): for i, image in enumerate(imageList): fileName = path + 'img' + '{:04}'.format(i+1) + '.jpg' with open(fileName, 'wb') as saveImage: saveImage.write(image) return if __name__ == '__main__': try: if os.path.exists(path) != True: os.mkdir(path) imageList = [] # Page * 20ループで画像取得 for i in range(int(maxPage)): #画像収集 index = (i * 20) + 1 imageList.extend(imageDownload(searchText, index)) #画像書き込み imageWrite(imageList) except Exception as e: print(e) print(traceback.format_exc())
- 実行方法と結果
$ python imageDownload.py
$ ls img-cat/
img0001.jpg img0003.jpg img0005.jpg img0007.jpg img0009.jpg img0011.jpg img0013.jpg
:
- 簡単な解説
Yahoo画像検索が1ページに付き20枚画像が取れる。
それを前提に以下で20ずつindexを勧めて画像を取得してリストに追加している。
リストにリストを追加する処理はextend(appendでやって1敗)
# Page * 20ループで画像取得 for i in range(int(maxPage)): #画像収集 index = (i * 20) + 1 imageList.extend(imageDownload(searchText, index))
ここでリストを全部渡して、カレントの下のディレクトリにファイルに出力している。
#画像書き込み
imageWrite(imageList)
- 関数の解説
以下の処理でリクエストを投げて、結果を受け取っている。
特筆すべきはscriptにJSONでURLなどを保持しているところ。
def imageDownload(searchWord, index): url = 'https://search.yahoo.co.jp/image/search?p={searchWord}&ei=UTF-8&b={index}' resp = requests.get(url.format(searchWord=searchWord, index=index)) soup = BeautifulSoup(resp.content, 'html.parser') #分析 script = soup.find("script", attrs={"id": "__NEXT_DATA__"})
あとは、JSONを分析+URLのリストを作って画像をダウンロードしてimageObjectListに保存している。
scriptJson = json.loads(script.string) imageUrlList = [] for algos in scriptJson['props']['initialProps']['pageProps']['algos']: imageUrlList.append(algos['main']['url']) #画像取得 imageObjectList = [] for imageUrl in imageUrlList: resp = requests.get(imageUrl) imageObjectList.append(resp.content) #画像リスト返却 return imageObjectList
imageWriteはリストを吐き出しているだけなので省略
まとめ
スクレイピングをやっていると、どんどんこれやっていいのかってなってくるので方向性がぶれてくる。
だけど、やりたいことはできたので良しとする。
python スクレイピング その2
はじめに
前回の続きでスクレイピングについて書いていく。
準備
これまた本通りだと芸がないので、はてなブログの「注目」のタイトルとURLを抽出してCSVに出力する処理を作成した。
- entryTitle.py
import requests from bs4 import BeautifulSoup import csv url = 'https://hatenablog.com/' resp = requests.get(url) soup = BeautifulSoup(resp.content, 'html.parser') # <class 'bs4.element.Tag'> serviceTopRecommendList = soup.find("div", attrs={"class": "serviceTop-recommend-list"}) # <class 'bs4.element.ResultSet'> entryTitleList = serviceTopRecommendList.find_all("h3", attrs={"class": "entry-title"}) entryTitleCsvList = [] for i, entryTitle in enumerate(entryTitleList): # <class 'bs4.element.Tag'> entryTitleCsvList.append([i + 1, entryTitle.find("a").get_text(), entryTitle.find("a").get("href")]) with open("entryTitleCsvList.csv", "w") as file: writer = csv.writer(file, lineterminator='\n') writer.writerows(entryTitleCsvList)
- 実行方法と結果
$ python entryTitle.py $ cat entryTitleCsvList.csv 1,ぼのこと女社会2【第92話-1】,https://www.bonogura.com/entry/bonoko-onnasyakai2-92-1 2,「はてなブックマーク」からいくらなんでも人が減りすぎではないかという話,https://www.tyoshiki.com/entry/2021/05/05/224428 3,FLoCとはなにか,https://jovi0608.hatenablog.com/entry/2021/05/06/160046 4,子の泣いてる時間を観察したくてM5StickCで泣き声モニタを作った,https://blog.sushi.money/entry/2021/05/06/100840 5,制作開放席システムは人生を狂わせる。,http://161cm910kg.hatenablog.com/entry/2021/05/05/205408 6,その粉、まさにメイクアップフォーエバー。【ウルトラHDセッティングパウダー】,https://www.konataro.blog/entry/2021/05/06/190000 7,JAPAN JAM終了後にテレビ局のインタビューに答えたけど、印象操作する偏った報道をされた件について,https://www.ongakunojouhou.com/entry/2021/05/06/125604 8,弱者男性論者を見ていると、「社会適応という名の悪」に対してあまりにも潔癖すぎると強く感じてしまう,https://ta-nishi.hatenablog.com/entry/2021/05/05/155031 9,フロントエンドのパフォーマンスチューニングを俯瞰する,https://numb86-tech.hatenablog.com/entry/2021/05/05/224611
- 簡単な解説
divタグでclassがserviceTop-recommend-listの要素を取り出す。
これが、注目のClassのよう。
serviceTopRecommendList = soup.find("div", attrs={"class": "serviceTop-recommend-list"})
取得したserviceTopRecommendListの中データから、h3タグで、classがentry-titleの要素を取得する。
entryTitleList = serviceTopRecommendList.find_all("h3", attrs={"class": "entry-title"})
最後にentryTitleListのenumerateで回して、indexと要素を設定する。
そのあと、リストにインデックス値とaタグの内容、aタグのhrefを取り出して設定する。
あとはそのままCSVに吐き出す。
for i, entryTitle in enumerate(entryTitleList): # <class 'bs4.element.ResultSet'> entryTitleCsvList.append([i + 1, entryTitle.find("a").get_text(), entryTitle.find("a").get("href")]) with open("entryTitleCsvList.csv", "w") as file: writer = csv.writer(file, lineterminator='\n') writer.writerows(entryTitleCsvList)
enumerate便利
まとめ
言葉遣いが難しい。要素なのか?データなのか?このあたりはもう少し勉強が必要かな。
あとは、<class 'bs4.element.ResultSet'>
と<class 'bs4.element.Tag'>
の関係がイマイチ読み解けなかったな。
メソッドに応じてどちらが取れるかを把握しとけば役に立ちそうだ。
それにしても、プロのHTMLだからすごくキレイに作られているな。ちょっと感動。