4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

即席スクレイピングPythonコード(正規表現とBeautifulSoupの比較付)

Last updated at Posted at 2020-07-23

#ソースコード

import re
import requests
from bs4 import BeautifulSoup

#ソースを取得
url = 'hogehoge' #ここを検索したいページのURLに
r = requests.get(url)
t = r.text.replace('\n','').replace('\t','')
s = BeautifulSoup(t,'html.parser')

#タイトルを取得

##正規表現
pattern = r'title>(.*)</title'
result = re.findall(pattern,t)
print(result[0])

##BeautifulSoup
print(s.title.text)

#プレイヤー名とスコア一覧取得

##正規表現
pattern = r'font-weight: 700;">(.*?)</span>.*?<span class="scoreTop ppValue">(.*?)</span>' #ここを検索したい箇所のソースに
result = re.findall(pattern,t)
print(result)

##BeautifulSoup
sts = s.select('.songTop.pp') #うまくclassが分離されているならこれも可
scs = s.select('.scoreTop.ppValue')#うまくclassが分離されているならこれも可
result = []
for st,sc in zip(sts,scs):
    result.append((st.text,sc.text))
print(result)

#解説

 コピペで実行できる状態を防ぐために上では一応伏せているが、普段お世話になっているページを対象に

  • タイトルを取得する
  • プレイヤーとスコアの一覧を取得する

 という操作を、正規表現とBeautifulSoup(以下bs)の2種類でやってみる。

#ソースを取得
url = 'https://scoresaber.com/global' #ここを検索したいページのURLに
r = requests.get(url)
t = r.text.replace('\n','').replace('\t','')
s = BeautifulSoup(t,'html.parser')

 ここで対象のソースとそのテキストデータを得る。改行やタブが入っていると正規表現の邪魔になるのでreplaceでそれらを取り除く。また今後、strの括りは'(シングルクオーテーション)で行うことを推奨する。なぜならhtmlの中に"(ダブルクオーテーション)があってややこしくなるから。

##タイトルを得る

image.png

 どのhtmlもtitleタグは持っている(と思う。おそらく)ので、それを狙い撃ちする。

##正規表現
pattern = r'title>(.*)</title'
result = re.findall(pattern,t)
print(result[0])

 正規表現を使う場合は、find_allを使うと便利である。matchだと前後の文字も含めて一致させないといけないため。ソースから該当箇所をコピーして、タグに挟まれた部分だけ(.!?)でグループ分けすると目的の文字列が得られる。ここで?をつけるのは最短一致にさせるためである。

##BeautifulSoup
print(s.title.text)

 bsでやる場合は、bs4.BeautifulSoupクラスはtitleという属性を持っているので直接参照できる。また、titlebs4.element.Tagというクラスであるが、これはtextによってタグには中の文字だけを狙い撃ちできる。

output
Global Rankings | ScoreSaber!
Global Rankings | ScoreSaber!

##一覧を得る

 この例では、プレイヤーとスコアの一覧を取得しているが、もし活用したい場合は各自ソースコードを眺めて目的の情報を得られるように改造されたい。

image.png

 このあたりの情報を抽出したい。

 正規表現を使う場合も、基本的には先程と同様である。目的の情報周辺のソースコードをコピーして、ターゲット部分を()でグループ分けする。ここでも逐一.*?にして最短一致にしないとカオスになるから注意。

##正規表現
pattern = r'font-weight: 700;">(.*?)</span>.*?<span class="scoreTop ppValue">(.*?)</span>'
result = re.findall(pattern,t)
print(result)

 bsで行う場合、タグでうまい感じに分離されていないかを確認する。ソースコードを見ると、songTop ppscoreTop ppValueというクラスがそれぞれ1対1対応してそうなので、それでやってみる。クラスはselectで選択できる。タグの属性で検索する場合はfind_all()で検索できる。リンク先等を取得する場合はget。これは完全に自分用の備忘録である。

##BeautifulSoup
sts = s.select('.songTop.pp')
scs = s.select('.scoreTop.ppValue')
result = []
for st,sc in zip(sts,scs):
    result.append((st.text,sc.text))
print(result)

 今回の例ではこれで上手くいく。もし単純なクラス抽出等で上手くいかない場合は、正規表現と格闘する必要があるかもしれない。

output
[('Taichidesu', '13,018.10'), ('Astrella', '12,996.00'), ('Garsh', '12,952.00'), ('sphynx', '12,900.50'), ('cerret', '12,895.00'), ('Duh Hello', '12,872.50'), ('Silverhaze', '12,798.70'), ('bckill', '12,731.40'), ('ptotpa', '12,729.30'), ('Tseska', '12,616.70'), ('CoolingCloset', '12,587.40'), ('Zyrix', '12,573.10'), ('That_Duck56', '12,522.40'), ('SSnowy', '12,450.00'), ('�🔥BALL SACK GAMIN🔥🔥 | Schwak�😳😂😂😖😖', '12,432.90'), ('Squid', '12,416.80
Tyrits', '12,389.10'), ('Smallfox', '12,352.10'), ('epicmoo34', '12,276.90'), ('MOE-ShitMiss Jesus', '12,273.80'), ('Matt_LEBE', '12,255.20'), ('retarde  .', '12,249.10'), ('(づ ̄ ³ ̄)づ *:・Wizz', '12,235.00'), ('FOAA | wolfvr13', '12,229.30'), ('themoldylog', '12,228.00'), ('NDK | Electrostats', '12,179.70'), ('YEP_KingRazer', '12,170.50'), ('Flee', '12,117.20'), ('Reddek', '12,090.90'), ('Drakonno', '12,083.60'), ('samich', '12,054.00'), ('xXIamCzechXx', '12,041.30'), ('Cum Jar Gaming | harbgy', '12,031.60'), ('Silverpoint', '12,005.00'), ('ejiejidayo', '12,004.90'), ('klkk1603', '11,977.20'), ('noob', '11,965.00'), ('Wannrick', '11,962.80'), ('Skyggen', '11,943.30'), ('SHS_MonsterWook', '11,913.50'), ('BSFR - Brindille', '11,883.50'), ('Klow', '11,877.80'), ('CreeperDude425', '11,873.50'), ('Twitch.tv/LSToast', '11,862.90'), ('AtomicX', '11,846.60'), ('Jaack', '11,787.20'), ('Zurph', '11,778.60'), ('TN | FOAA | Monke', '11,777.80'), ('TheJumpingSheep', '11,767.70'), ('AlexTheCutie', '11,746.70')]
[('Taichidesu', '13,018.10'), ('Astrella', '12,996.00'), ('Garsh', '12,952.00'), ('sphynx', '12,900.50'), ('cerret', '12,895.00'), ('Duh Hello', '12,872.50'), ('Silverhaze', '12,798.70'), ('bckill', '12,731.40'), ('ptotpa', '12,729.30'), ('Tseska', '12,616.70'), ('CoolingCloset', '12,587.40'), ('Zyrix', '12,573.10'), ('That_Duck56', '12,522.40'), ('SSnowy', '12,450.00'), ('�🔥BALL SACK GAMIN🔥🔥 | Schwak�😳😂😂😖😖', '12,432.90'), ('Squid', '12,416.80
Tyrits', '12,389.10'), ('Smallfox', '12,352.10'), ('epicmoo34', '12,276.90'), ('MOE-ShitMiss Jesus', '12,273.80'), ('Matt_LEBE', '12,255.20'), ('retarde  .', '12,249.10'), ('(づ ̄ ³ ̄)づ *:・Wizz', '12,235.00'), ('FOAA | wolfvr13', '12,229.30'), ('themoldylog', '12,228.00'), ('NDK | Electrostats', '12,179.70'), ('YEP_KingRazer', '12,170.50'), ('Flee', '12,117.20'), ('Reddek', '12,090.90'), ('Drakonno', '12,083.60'), ('samich', '12,054.00'), ('xXIamCzechXx', '12,041.30'), ('Cum Jar Gaming | harbgy', '12,031.60'), ('Silverpoint', '12,005.00'), ('ejiejidayo', '12,004.90'), ('klkk1603', '11,977.20'), ('noob', '11,965.00'), ('Wannrick', '11,962.80'), ('Skyggen', '11,943.30'), ('SHS_MonsterWook', '11,913.50'), ('BSFR - Brindille', '11,883.50'), ('Klow', '11,877.80'), ('CreeperDude425', '11,873.50'), ('Twitch.tv/LSToast', '11,862.90'), ('AtomicX', '11,846.60'), ('Jaack', '11,787.20'), ('Zurph', '11,778.60'), ('TN | FOAA | Monke', '11,777.80'), ('TheJumpingSheep', '11,767.70'), ('AlexTheCutie', '11,746.70')]

#なぜこれを書いたか

 スクレイピングする度にぐぐっている気がしたので、自分の中での定石を定めたかった。あと備忘録。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?