はじめに
初めまして。おおのと申します。最近はパワポケやダンガンロンパ、月姫など昔のゲームのリメイクやら移植作品がたくさん出てうれしくて全部買ってしまったのに一つもやる時間が無くてやばいクレーマーと化しそうです。あと金もなくなりました。
そういえばポケモンのダイヤモンドとパールのリメイクも出ましたね。当然私も購入したのですが、バッジ一つも取れておらずもうパルキアにでもなってあたりを焼け野原にしたい気持ちでいっぱいです。切に中学時代の友達と会って対戦がしたいです。
ところで、突然ですが日常生活でスクレイピングしないといけない事態に陥ったことはありませんか?僕は無いですが、先日なんとなくスクレイピングをしてみたので、軽くまとめていきたいなと思います。自分も初心者でググりながら色々やってみたクチなのでより詳しい解説欲しい人はこんなばかみたいな「はじめに」書いてる記事なんか読んでないで別の記事探してください。(ごめんなさい)
ここでは大雑把に自分が書いたコードの説明だけします。
対象としてはスクレイピングやってみたいけどどうしたらいいかさっぱりわからない初心者さんでしょうか。一度読んでもらって処理の大雑把な流れを把握してもらえたらいいと思います。
それから、僕も普通にプログラミングも初心者なので、コードの可読性とか変数名とかゴミなのもある程度許容できる人でないとストレスで消化器官の67パーセントくらいが機能不全になるかもしれないのでマジで注意してください。
次のセクションから説明を書いていきます。
目次
小説家になろうの月間総合ランキング取得
まずは簡単なプログラムから載せていきます
from bs4 import BeautifulSoup
from urllib import request
novel_names = []
url = 'https://syosetu.com/'
res = request.urlopen(url)
soup = BeautifulSoup(res,'html.parser')
for i in range(10):
novel = soup.find(class_ = 'p-ranking__item p-ranking__item--col2 p-ranking__item--' + str(i+1) + ' c-novel-item c-novel-item--ranking')
url2 = novel.get('href')
res2 = request.urlopen(url2)
soup2 = BeautifulSoup(res2,'html.parser')
title = soup2.find('title')
novel_names.append(title.get_text())
print('第' + str(i+1)+ '位:' + title.get_text())
何をするプログラムかわかりますか?スクレイピングです。
すみません許してください。なんでもはしませんがこのプログラムについて説明します。
このプログラムは「小説家になろう」の月間総合ランキングを出力するプログラムです。import文を見るとわかるのですが、bs4(beautifulsoup4)というライブラリとurllibというライブラリを使っています。上から順にプログラムに現れる変数の説明をしていきます。
- 変数
- novel_names:実は何もしていません。それに気づけた貴方はえらい
- url:みんな大好き「小説家になろう」さんのトップページURLです
- res:requestのメソッドurlopenで指定URLをオープンします
- soup:BeautifulSoupオブジェクトです。resとオプション(今回はhtmlの解析を行うのでhtml.parser)を引数に渡してあげることでurlの先のhtmlを解析し、簡単にデータを取り出せる形にしてくれます
- novel:soupのfind関数を使って指定したclassの要素を得ています。小説家になろうのhtmlソースを読むとわかるのですが、月間総合ランキングは'p-ranking__item p-ranking__item--col2 p-ranking__item--' + str(i+1) + ' c-novel-item c-novel-item--ranking'というclassの(i+1)の部分が順位で、指定してあげるとhtmlからその順位のタイトルを含むhtmlの要素が取れるようになっています。for文で10位まで回していますね。
- url2:頭の悪い変数名でごめんなさい。本当は上のnovelの時点でタイトル情報は取得できているんですが、いかんせん最近のなろう小説はタイトルが長くて枠に入りきらず、この時点では折りたたまれてしまっていることがしょっちゅうなのです…(ソースでも折りたたまれていることを知らなかった情弱はこちら)。なのでget関数で取得したhtmlの要素からhref(リンク)を取得し、ランキングのページではなくその小説のページに飛んでから正式なクソ長ぇタイトル(投稿者さんごめんなさい)を取得することが必要になります。
- res2、soup2:resとsoupと同様です。小説のページにとんだ先でhtmlを取得します。
- title:飛んだ先のhtmlからtitleの要素をfindで取得します。ちなみに、この段階では出力してもまだゴミがついてくるためget_text関数を使って単なるテキストのみ取り出します。それを下でprintしています。
サラッとnovel_namesにtitleの追加を行っていますがこれ何しようとしてたんでしょうねははは。
そこそこ細かめに見ていくとこんな感じです。だらだら書くんじゃねぇよなぐるぞって言われたくないので簡単に流れを説明しなおすとまず小説家になろうのトップページのhtmlの内容取得、次にランキングの部分からトップ10の小説ページへのリンクを取得しそのページに飛ぶ、そしてタイトルを取得するという流れです。何気にページ遷移+タイトル取得という流れがめんどくさいですね。でも入門としては悪くないと思うのでまず最初にこんな感じで取れそうなものを取ってみるということをやるのがおすすめです。
ちなみに本プログラム実行結果はこんな感じ
時期によって当然変わるのですが半分くらいクソ長タイトルでしたね
取り敢えずまずはただプログラムを動かしてもらって、それからhtmlソースを実際に眺めてもらうのが良いと思います。
ポケモンの名前と適当な覚える技取得
最初の方に述べたのですが、私はポケモンそこそこ好きです。ポケモンは対戦ゲームなのですが、対戦するにあたってポケモンを育てる必要があります。しかし、どんなポケモンを育てたらいいか思いつかない!!!そんなときの強い味方みたいなプログラムを作りました。ランダムでポケモンの名前とそのポケモンが覚える技から4つ取得してくれます。これでもうポケモン選びにも技選びにも迷うことはありませんね!!!!早速プログラムを見ていきましょう。
import requests
from bs4 import BeautifulSoup
import random
pokemon =[]
i = 0
wazalist = []
for count in range(6):
url1 = "https://yakkun.com/swsh/zukan/n"
a = random.randint(1,898)
url = url1 + str(a)
r = requests.get(url)
soup = BeautifulSoup(r.content,'html.parser')
pokename1 = soup.find('title')
pokename2 = pokename1.get_text()
pokename = pokename2.split("|")
while pokename[0] in pokemon:
url = url1 + str(random.randint(1,898))
r = requests.get(url)
soup = BeautifulSoup(r.content,'html.parser')
pokename1 = soup.find('title')
pokename2 = pokename1.get_text()
pokename = pokename2.split("|")
print("ポケモン名:" + pokename[0])
pokemon.append(pokename[0])
wazamei = soup.find_all(class_ ='move_name_cell')
for k in wazamei:
c = k.find('a')
wazalist.append(c.get_text())
i = i + 1
if a == 235:
print("技:スケッチ")
i = 0
wazalist =[]
print()
elif a == 132:
print("技:へんしん")
i = 0
wazalist =[]
print()
elif a == 789:
print("技1:はねる")
print("技2:テレポート")
i = 0
wazalist =[]
print()
else:
num1 = random.randint(0,i-1)
num2 = random.randint(0,i-1)
while wazalist[num2] == wazalist[num1]:
num2 = random.randint(0,i-1)
num3 = random.randint(0,i-1)
while wazalist[num1] == wazalist[num3] or wazalist[num2] == wazalist[num3]:
num3 = random.randint(0,i-1)
num4 = random.randint(0,i-1)
while wazalist[num4] == wazalist[num1] or wazalist[num4] == wazalist[num2] or wazalist[num4] == wazalist[num3]:
num4 = random.randint(0,i-1)
print("技1:" + wazalist[num1])
print("技2:" + wazalist[num2])
print("技3:" + wazalist[num3])
print("技4:" + wazalist[num4])
i = 0
wazalist =[]
print()
スクレイピング元は「ポケモン徹底攻略」さんです。情報がとてもよくまとまっています。
ちなみにURLを開くのに使ったライブラリは先ほどのurllibではなくrequestsです。元々requestsを使っていたのですが、なぜかうまくいかずurllibで上手くいったということが何度かあったのでどっちかだめならどっちか試してみると良いと思います。
このサイトではポケモンの名前から覚えるポケモンの技名までしっかり一つのページで完結します。なんとかになろうとかとは違いますね(ごめんなさい)
ちなみにこのプログラムはパーティ単位で出力できるようにポケモン6体分出力してます(なのでfor文6回)最初に宣言した配列pokemonの中にすでに出たポケモンは入れて、その後に出たときにはじくようにしています。
urlの指定はhttps://yakkun.com/swsh/zukan/n
の後ろにそのポケモンのぜんこく図巻番号を入れるので、ランダムで1~898までの整数を入れてあげることでランダムに全てのポケモンのページを取得できるようにしています。今回は一つ一つ変数を解説しませんがsoupにfind_allという見たことない関数があると思います。これはhtmlの特定のclassを持つhtmlの要素を配列形式で上から順番に全部返してくれる関数です。先ほど出てきたfindはそのclassを持つ一番上のhtmlの要素だけ返します。
move_name_cellというclassにはそのポケモンが覚える技がすべて格納されています。終わりの方ではダブり処理をしながら中から技を4つ選んでいます。確実にもっといい処理はあるのでマネしないでください。普通に配列の中身参照すればよかったと思います。あとコード中にへんしんとかスケッチとか書いてあるのはポケモンやってる人ならわかると思うんですけど、技4つ覚えないポケモンが何匹かいるのでそのための例外処理です。
またやってることをまとめると、ポケモンのページをランダムで決定、覚える技全部取得、中から4つ選び出力という非常にシンプルな流れです。プログラム内でsplitとか使ってるのは手に入る文字列がそのまま扱えるわけではないので上手いこと切り出したりしています。
実行例はこんな感じ。(本当は6体出ます)なんか攻撃的なハピナスが生まれてしまいました。
終わりに
今回は初心者がスクレイピングをやってみたのを振り返るため一度記事を作ってみました。このプログラムが参考になるかはわかりませんがこれの動きを理解してもらってなんとなくスクレイピングのやり方を知ってもらえたらいいかなと思います。
最後まで読んでいただきありがとうございました。