となりのJohnの気まぐれ

気の向くままに

【Python】自動画像ダウンローダを作ったはなし

f:id:newbie29979:20201230195103p:plain

はじめに

サイトに更新があるとそのサイトに行って自動で画像をダウンロードするスクリプトを過去に書いたので紹介.毎日のネットサーフィンをする中で機械的収集する画像…なんとか自動化できないか…モジュールが充実するPythonRSSを使うと自動化できるんじゃないか,と考え実装した.

使用言語はPython3.

実装

サイトの指定

アクセスするサイトをjsonで指定する.

{
   "SITE_NAME":{
      "rss": "RSS_URL",
      "prev_link": "VISITED_URL",
      "tag_attributes": "^http://TAG_URL",
      "split_char": "/"
   }
}
プロパティ名 用途
SITE_NAME 複数のサイトへのアクセス時にサイトで区別するため
rss サイトのRSSのURLを記述するため
prev_link 前回アクセスしたサイトのURLを記録するため
tag_attributes 画像に付与されているURLの共通部分
split_char 画像のファイルネームを決めるため

よく見るとプロパティ名が不適切なものがあるが…

サイトへのアクセス

サイトへのアクセスにはseleniumを使用.

options = Options()
options.set_headless(True)
driver = webdriver.Chrome(chrome_options=options)
RSS

 完全な自動化(スクリプトを走らせるだけで画像が集まる状態)のために最初に必要なことは,対象のサイトの更新を知ること

そこで簡単な実装としてRSSを使ってサイトの更新を知るようにする.RSSにアクセスし,最新のURLがprev_linkと一致しなければ更新があるということ.

Pythonにはfeedparserというモジュールがあるので,これを使う.

コードはこんな感じ.

feed_result = feedparser.parse(site_prop['rss'])
    for entry in feed_result.entries:
        if entry.link != site_prop['prev_link']:
            print(entry.title)
            download_images(site_prop, entry.link)
        else:
            break

    if site_prop['prev_link'] != feed_result.entries[0].link:
        site_prop['prev_link'] = feed_result.entries[0].link

site_propは直前でjsonをマップとして読み込んでいるもの.

画像のダウンロード

画像のダウンロードはselenimのdriverと,driverから得られるhtmlをパースするBeautifulSoupの両方を使うことで行う. BeautifulSoupはwebページ中,指定した属性の指定したパラメータの情報だけを抜き取れる.

今回であればダウンロード対象の画像URLはその画像ファイルネーム以外共通していることが予想されるので,その共通部分を正規表現としてフィルタする.

予想される共通部分はjsontag_attributesに記録.

img_tags = soup.find_all('a', {'href':re.compile(_site_prop['tag_attributes'])})

ダウンロードしたい複数の画像で共通するurlを正規表現としてパーサに渡すことで得られるタグ情報のリストimg_tagsについてダウンロードしていく.

for tag in img_tags:
        img_url = tag.attrs['href']
        word_list = tag.attrs['href'].split(_site_prop['split_char'])
        res = requests.get(img_url)
        with open('YOUR_DIR'+word_list[len(word_list) - 1], 'wb') as f:
            f.write(res.content)

split_charでURLをsplitし,最後の要素をファイルネームとして保存する.

jsonの更新

最後に,次に走らせるときのためにjsonを更新する.

これにより,prev_linkが更新されるので実行の度に同じURLを見なくて済む.

with open('site_props.json', 'w') as target_file:
    json.dump(json_dict,target_file)
    target_file.close()

使い道

バッチファイル等でコンピュータ起動時に実行されるように登録すれば便利.

さいごに

この記事を読んでいただいた方への創作の刺激となればいいと思う.私は起動時に走るようにしているが,更新がある度に自動的に全ての画像が保存されていくので選別に苦労している.次は個人の好みで選別できる判別器を作らなければ…完全自動化には時間がかかりそうである.

github.com