クローラー開発で活用する知識
★やりたいこと:「次へ >」のボタンを押下して画面遷移したいです。※クリックしたい
以下が例です。
EC.element_to_be_clickable((By.LINK_TEXT, "Next"))
おそらく、「次へ >」でヒットするはずなのですが、スペースや半角・全角などを正しく設定してみてください。
それでダメなら、「次へ >」で試してみてください。
あるいは、以下のようにclass nameで取得してみてください。取得できない場合は、next以外でも試してください。
EC.element_to_be_clickable((By.CLASS_NAME, "next"))
②技術検証はなさらなくて大丈夫なのですが、ナンバリングの空白のところにナンバリングの数字を入れて、検索実行することって可能なのでしょうか??
これは実際のページで動作確認しないと検証できません。実行できないのであれば不可能かと思われます。
"★クエリパラメーターでページ遷移するには?
ページを開いたときに、URLにクエリパラメータとして、ページのNoのようなものはあるとページ遷移に活用できる。
もしあれば、その数字を変えてURLにアクセスすれば遷移できます。
※suumoを例にすると、page=2の部分がそれにあたります。
https://suumo.jp/jj/bukken/ichiran/JJ010FJ001/?ar=030&bs=011&ta=13&jspIdFlg=patternShikugun&sc=13106&sc=13107&sc=13108&sc=13118&sc=13121&sc=
13122&sc=13123&kb=1&kt=9999999&mb=0&mt=9999999&ekTjCd=&ekTjNm=&tj=0&cnb=0&cn=9999999&srch_navi=1&page=2
もしなければ、seleniumを使って、クリック動作を行う必要があります。
参考URL
https://www.seleniumqref.com/api/python/element_set/Python_click.html
★クエリパラメーターの補足情報メモ
「?term=」は、クエリパラーメタといって、Webサーバー側に渡す情報になります。
プログラムでいう、引数のようなものです。クエリパラーメタは、開発者側で定義します。そのため、URLにアクセスし、適当な文字を検索することで、「?term=」がついていることを確認しました。
■余談
URLにクエリパラメータを含めることで、誰がアクセスしても同じような結果が得られます。(GETメソッド)
一方、個人情報を入力するケースは、クエリパラメータにしてしまうと、情報が漏れてしまいます。情報を隠したいときは、POSTメソッドを使います。
ページネーション部分をスクレイピングする必要があるときは?
以下のプログラムを参考に、実装してみてください。
ページ番号リンクをすべて見つける
page_links = soup.find_all('a', class_='Pager_link__rnYFP')
ページ番号を抽出して整数に変換
page_numbers = [int(link.text) for link in page_links if link.text.isdigit()]
最大のページ番号を見つける
max_page_number = max(page_numbers)
print("最大のページ番号は:", max_page_number)
※レオパレス案件でページネーションの最大値の数字を読み取り実装したよ!メモゆきこ
:::note
タグの修正方法と確認方法について
"前回のサイトと使われているタグが違うので、修正が必要です。
修正前
#booklists = soup.find_all('p', class_='title') # p.title を全て取得
修正後
booklists = soup.find_all('div', class_='title') # div.title を全て取得
調べ方としては、以前にもお伝えしている通り、print文でデバッグしていくのがよいです。
今回だと、
dataで値が取れているか確認【93行目にprint(data)を追記してコンソールを確認】
→取れていない
a_tagで値が取れているか確認【67行目にprint(a_tag)を追記してコンソールを確認】
→取れていない
booklistsで値が取れているか確認【62行目にprint(booklists)を追記してコンソールを確認】
→取れていない、要素の取得から間違っている
という感じです。"
pyinstallerで音声を含めて配布したい
適宜、ファイル名などの変更が必要ですが、以下の流れでできると思います。
①音声ファイルをpythonのプログラムと同じフォルダに入れて、相対パスで参照させます。
import tkinter as tk
from tkinter import ttk, messagebox
import requests
from bs4 import BeautifulSoup
import pandas as pd
import os
import re
import pygame
import sys
音声ファイルの相対パスを設定
local_sound_file = os.path.join(sys._MEIPASS, '猫の鳴き声1.mp3')
②Specファイルを作成してます。
pyinstaller --name=my_application_name --onefile --windowed --noconsole your_script.py
③.specファイルを音声ファイルを指定するように修正します。
-- mode: python ; coding: utf-8 --
block_cipher = None
a = Analysis(
['your_script.py'],
pathex=[],
binaries=[],
datas=[('猫の鳴き声1.mp3', '.')],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='my_application_name',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False,
disable_windowed_traceback=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=None,
)
④ビルド
pyinstaller your_script.spec
参考リンク
https://msteacher.hatenablog.jp/entry/2020/06/27/170529
スクロールしたい①AP利用しない例
①スクロールを上から下までいき、<リストの下まで到達しました・・・>までデータ取得できる。
以下のコードの部分でfor i in range(5)としているため、5回しかスクロールしないようになっています。
while ループにして、<リストの下まで到達しました・・・>が表示されたらループを抜けるように修正が必要になります。
for i in range(5):
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
store_infomation = soup.select("div.bfdHYd.Ppzolf.OFBs3e")
if len(soup(class_="HlvSq")) > 0 or len(store_infomation) > 25:
return soup
else:
element = driver.switch_to.active_element
try:
element.send_keys(Keys.END)
except:
time.sleep(5)
element = driver.switch_to.active_element
time.sleep(0.5)
スクロールしたい②API利用する例
①作成いただきましたコードで、スクロールのところはどこが効いているのでしょうか?
スクロールをするには、API実行時にpage_tokenを設定してあげる必要があります。修正後のプログラムは以下になります。
import googlemaps
import pandas as pd
import time
APIキーを設定
API_KEY = 'YOUR_API_KEY'
gmaps = googlemaps.Client(key=API_KEY)
英語のtypesを日本語に変換する辞書
type_translation = {
"cafe": "カフェ",
"restaurant": "レストラン",
"bar": "バー",
"bakery": "ベーカリー",
"lodging": "宿泊施設",
"store": "店舗",
"point_of_interest": "観光スポット",
"establishment": "施設",
# 必要に応じて他のtypesを追加
}
def translate_types(types):
"""typesのリストを日本語に変換する関数"""
return [type_translation.get(t, t) for t in types]
def get_place_details(place_id):
"""指定されたplace_idの詳細情報を取得する関数"""
fields = ['name', 'formatted_address', 'formatted_phone_number', 'rating', 'user_ratings_total']
place_details = gmaps.place(place_id=place_id, fields=fields, language='ja')
return place_details.get('result')
def search_places(query):
"""指定されたクエリで店舗を検索し、各店舗の詳細情報を取得する関数"""
places_details = []
next_page_token = None
while True:
if next_page_token:
places_result = gmaps.places(query=query, page_token=next_page_token)
else:
places_result = gmaps.places(query=query)
for place in places_result.get('results', []):
place_id = place['place_id']
details = get_place_details(place_id)
types = place.get('types', [])
translated_types = translate_types(types)
places_details.append({
'店舗名': details.get('name'),
'住所': details.get('formatted_address'),
'電話番号': details.get('formatted_phone_number'),
'業種': ', '.join(translated_types),
'評価': details.get('rating'),
'口コミ数': details.get('user_ratings_total')
})
next_page_token = places_result.get('next_page_token')
if not next_page_token:
break
# 次のページのトークンが有効になるまで少し待つ
time.sleep(2)
return places_details
例として、「カフェ 赤羽駅」を検索
query = 'カフェ 赤羽駅'
places = search_places(query)
結果をデータフレームに変換
df = pd.DataFrame(places)
print(df)
エクセルファイルに出力
df.to_excel('places.xlsx', index=False)
print("エクセルファイル 'places.xlsx' に出力されました。")
なお、モジュールの仕様は、githubで確認できます。仕様を確認することでどのような引数が利用できるかわかります。
https://github.com/googlemaps/google-maps-services-python/blob/master/googlemaps/places.py
②こちらのコードにいろいろすることで、営業時間を取得することは可能でしょうか?
取得できるデータは、Place APIのレスポンスを確認して取得してみてください。
「opening_hours」を加工すれば、取れそうな気がします。
https://developers.google.com/maps/documentation/places/web-service/details?hl=ja#json