文系男子がPython3で画像収集するスクリプトを作ってみた。bs4・requests編
なにを隠そう僕は今pythonにはまっている。
なぜpythonなのかと言うと特に理由はない。
ただプログラミング初心者でも簡単にできるって書かれていたからpythonにしてみた。
仕事でもpythonは使わない。
でも己のスキルアップのために勉強しているのだ!
なんてね。
本当はね、pythonでtwitterの操作をしたかったからなんだ。
いきなりAPIがどうのこうのkeyがどうのこうの言われてもわからないからさ
とりあえず前提にあるスクレイピングやクローリングを勉強しようと考え
試しにサイト上の画像を収集するスクリプトを作ってみたわけだよ。
いやー難しかった。
2週間は同じこと考え続けた。
なんなら掲示板に投稿しようかとも思ったくらい考え続けた。
そしてそれは突然完成した。
栄えある第一号である。
環境
VirtualBox(以下VB)でUbuntuを起動して使ってます。
エディタはAtom
pythonは3.5.2
他はデフォルトのままです。
VBは重いといわれていますがビデオメモリを最大にして
3Dアクセラレーションを有効化にチェックを入れると改善されます。
Downloads – Oracle VM VirtualBox
Homepage | Ubuntu Japanese Team
Scraping.py起動
実際に動かしてみた。
とりあえずみんな大好き乃木坂46のまとめサイトでスクレイピングしました。
ありがとうございます。
コードはこちら
# -*- coding='utf-8' -*- import requests # urlを読み込むためrequestsをインポート from bs4 import BeautifulSoup # htmlを読み込むためBeautifulSoupをインポート URL = '' # ここにサイトのurlを貼る images = [] # 画像を入れるためにリストを作る soup = BeautifulSoup(requests.get(URL).content,'lxml') # .contentでバイナリにする? for link in soup.find_all("img"): # imgタグを取得しlinkに入れる if link.get("src").endswith(".jpg"): # imgタグの中の.jpgであるsrcタグを取得する images.append(link.get("src")) # imagesリストに入れる for target in images: # リストはrequests.getで読めないようなので一旦targetに入れる resp = requests.get(target) with open('img/' + target.split('/')[-1], 'wb') as f: # splitでファイル名を短縮する f.write(resp.content) # 重要!.contentを入れないと画像データではなくtextデータになってしまう。
urllibでもできるようだけど最初に成功したのがrequestsなのでこんな形となりました。
注意点
クローリングやスクレイピングをする前に、そもそもしてもいいか否かの規約みたいなものがありまして、
それにやっちゃだめだよって書かれているとできません。
これをrobots.txtと言います。
サイトトップページのurlに/robots.txtと打ち込むと見ることができます。
コードは使ってもいいですけどちゃんと確認してから使ってくださいね。
ざっと見たところNAVERや公式サイトなどはダメみたいです。
あと別問題ですけどtwitterの画像はこのスクリプトでは取れません。
おそらく形式が違うんでしょうね、javascriptかなんなのか。
これから勉強するね。
普通に”乃木坂 画像”で検索するとスクレイピングできないところが結構でてくるので絞ります。
例えばですけどGoogleで ”乃木坂46 画像 -naver -twitter”で検索すると
naverとtwitter以外の乃木坂の画像を表示してくれます。
Google先生は除きたいキーワードの手前に-を付けるとそれ以外の検索結果を表示してくれるんですね。
コード解説
僕と同じ初心者の方がつまづかないように一応僕なりに解説しておきます。
間違ってるかもしれないんですけど、大目に見てくだされ。
まず images =
これは画像が複数あるのでリストに入れて見やすく?わかりやすく?しています。
次、soupを作ります。
ここでlxmlを使っているのはパースが早いらしいから、です。
デフォルトのhtml.parserでも結果は変わりません。
次、for文でimgタグを取得します。
bs4はいろいろと書き方があるようで、例えばsoup.a.get('href')だったり
soup.find_all('p', class_='main-contents')だったり。
for文が一番しっくりきたのでこうしました。
次のif文とendswith('.jpg')とappendで末尾が.jpgで終わる場合リストに追加するって意味になります。
一旦ここでprint(images)で画像データが取れてるか確認してもいいかと思います。
最後がローカルフォルダに画像データを保存する方法です。
まずリストに入れた画像データを読み込む必要があるのでrequests.getを使うんですけど
images = のリストそのままだと読んでくれません。
なんかよくわからないけどサポートしてないだとかなんかエラーが出たような?
なのでfor文を使って新しい変数targetに入れます。
そしてtargetをrequests.getで読み込みそれをさらにrespに入れます。
で、with openファイルを作ります。
このまま保存すると**************.jpgってくそ長いファイル名になってしまうので
split('/')[-1]で後ろから数えて最初に出現した/までを残し、それ以前の文字を消し去ります。
でf.writeで書き込むわけですが、ここでcontentを入れないとテキストデータになってしまいます。
テキストデータの拡張子を.jpgにしただけのファイルが作成されるので当然壊れていて開けませんなんてことになります。
ぼくはこれで2週間悩みました。
躓いたポイント
やっぱりpython2の情報が多い。
書き方が微妙に違うからそのままpython3で書くとエラーがでます。
urllib2って書かれていたらpython3では使えません。
python3ではurllib.requestsに変更されたようです。
こういうところも初心者からするとすごい大変です。
あとブログにコードが書いてあったとしても解説がない。
そしてそれをそのままコピペしても使えない場合がほとんどでした。
なんかf.write(image.read())って書いてあるのを見かけるんですけど、
そのまま書くとread使えないよってエラーがでます。
imgタグやsrcタグを取得するのにbs4を使うわけですが、bs4でとったデータはstr型になります。
strであればreadは使えるんですけど、これだと上に書いたようにテキストデータになっちゃいます。
かと言ってencode='utf-8'でbytes型にするとreadが使えません。
でもbytesにしないとbytes like, not strって怒られます。
どこを探してもf.writeに.read()を使っていたのでもうダメかと思いましたね。
たまたまどこかでf.write(image.text)って書かれていたのとそれ以外のデータは.contnetを使うって文字を見たので
試してみたら成功したって感じです。
まとめ
最後ちょっと愚痴っぽくなりましたが、すごい楽しかった。
成功したときはやったーって達成感がすごかったです笑
ちなみに僕はまったくpythonを知らない状態でクローリングとスクレイピングの本を買いました。
これからスクレイピングをしたいって人
悪いことは言わない、まず基本的なところから勉強しましょう。
やっぱりね、文法がわからないとなんでエラーがでてるのかもわからないです笑
みんなdefとか使ってるんですけど、defってなんだ?って状態でしたからね。
python超入門って本を買いましたけど、いまだによくわかってません。
基本がないとあとあと苦労しますよ。