0
0

【自分の勉強用メモ】クローラー開発の備忘録2024/08/18ゆきこ

Posted at

クローラー開発で活用する知識

★やりたいこと:「次へ >」のボタンを押下して画面遷移したいです。※クリックしたい
以下が例です。

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

ナンバリングの空白のところにナンバリングの数字を入れて、検索実行する

要素を取得して、値を入力すればよいです。
イメージは、以下の通りです。

input要素を見つけて値を入力

input_element = driver.find_element(By.ID, 'input-id') # 実際のinput要素のIDに置き換える
input_element.clear() # 既存の値をクリア
input_element.send_keys('2') # 値を入力

スクロールしたい①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

0
0
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
0
0