#ソースコード
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の中に"
(ダブルクオーテーション)があってややこしくなるから。
##タイトルを得る
どの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
という属性を持っているので直接参照できる。また、title
はbs4.element.Tag
というクラスであるが、これはtext
によってタグには中の文字だけを狙い撃ちできる。
Global Rankings | ScoreSaber!
Global Rankings | ScoreSaber!
##一覧を得る
この例では、プレイヤーとスコアの一覧を取得しているが、もし活用したい場合は各自ソースコードを眺めて目的の情報を得られるように改造されたい。
このあたりの情報を抽出したい。
正規表現を使う場合も、基本的には先程と同様である。目的の情報周辺のソースコードをコピーして、ターゲット部分を()
でグループ分けする。ここでも逐一.*?
にして最短一致にしないとカオスになるから注意。
##正規表現
pattern = r'font-weight: 700;">(.*?)</span>.*?<span class="scoreTop ppValue">(.*?)</span>'
result = re.findall(pattern,t)
print(result)
bsで行う場合、タグでうまい感じに分離されていないかを確認する。ソースコードを見ると、songTop pp
、scoreTop 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)
今回の例ではこれで上手くいく。もし単純なクラス抽出等で上手くいかない場合は、正規表現と格闘する必要があるかもしれない。
[('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')]
#なぜこれを書いたか
スクレイピングする度にぐぐっている気がしたので、自分の中での定石を定めたかった。あと備忘録。