見出し画像

文系マーケターの私がWebサイトから画像を一括取得するスクレイピングに失敗した原因の考察

スクレイピングのゴール

  • Webサイトから,あるディレクトリの配下にあるページの情報を取得し,ページ内にある画像を全て取得してローカル環境にフォルダ分けして保存する

取得先のWebサイトのディレクトリ構造をご紹介しておきます.「item」ページを全て取得し,各itemの写真をすべてローカルに保管していくことを目指します.

.
├── root
│   ├── Women
│   │   └── Brand_A
│   │       ├── item no.1
│   │       ├── item no.2
│   │       └── item no.3 ...
│   └── Men

i) requests,beautifulsoupライブラリを使用してみる

下記の処理を行いました.

  • エージェント偽装(fake_useragent)

  • 変数urlに/Women/Brand_AまでのURLを格納

  • requests.get(url)で変数urlからデータ(Responseオブジェクト)を取得

  • オブジェクトからtext属性を使ってhtmlの文字列を取り出し

  • htmlの文字列からBeautifulSoupオブジェクトを作成してページから欲しい要素を探せるようにする

  • itemページのリンクを取り出してリスト化

  • itemページのリストを1つずつ取り出し,imgタグを全て取得していく

  • imgタグに設定されている画像ファイルのURLから画像ファイルの名前を抽出し,出力先のフォルダ名として使用,出力先のフォルダを作成してファイルを保存する

!pip install requests
import requests
!pip install fake-useragent

from fake_useragent import UserAgent
ua = UserAgent()
ua.ie

from fake_useragent import UserAgent
ua = UserAgent()
header = {'User-Agent': str(ua.chrome)}

url = "https://***/Woman/Brand_A"
res = requests.get(url, headers=header)
!pip install bs4
!pip install lxml
import re
import requests
from pathlib import Path
from bs4 import BeautifulSoup
import time

# あとで検索結果ページから画像ページのリンクを取り出して、このリストに格納していきます。
linklist = []

# requests.get(url)で、変数urlからデータ(Responseオブジェクト)を取得します。
# そして、オブジェクトのtext属性を使って、htmlの文字列で取り出します。
# そして取得した検索結果ページのhtmlを、変数htmlとします。
html = requests.get(url, headers=header).text

# 変数htmlからBeautifulSoupオブジェクトを作成して、変数soupとします。
soup = BeautifulSoup(html, 'lxml')

# itemページのurlが格納されたaタグを全て取得
a_list = soup.select('div > a')

# 画像リンクを1つずつ取り出す
for a in a_list:
# 画像ページのURLを抽出
    link_url = a.attrs['href']
# 画像ページのURLをリストに追加
    linklist.append(link_url)
    time.sleep(1.0)
linklist
# デバッグのためmy_listから1行分だけを取り出してsample_my_listに格納し以降のコードにかける
sample_my_list = []
sample_my_list = my_list[0:1]

# ●各画像ページから画像ファイルのURLを特定  
# 画像ページのURLを1つずつ取り出す
for page_url in sample_my_list:
# 画像ページのhtmlを取得
    page_html = requests.get(page_url, headers=header).text
# 画像ページのオブジェクトを作成
    page_soup = BeautifulSoup(page_html, 'lxml')
# 画像ファイルのタグをすべて取得
    img_list = page_soup.select('div > picture > img')
# imgタグを1つずつ取り出す
    for img in img_list:
# 画像ファイルのURLを抽出
        img_url = (img.attrs['src'])
# 画像ファイルの名前を抽出
        filename = img_url[-4:] + ".jpeg"
# 出力先のフォルダを作成
        dummy_output_folder = Path('site_title' + filename)
        dummy_output_folder.mkdir(exist_ok=True)
# 保存先のファイルパスを生成
        save_path = dummy_output_folder.joinpath(filename)
        time.sleep(1.0)
# ●画像ファイルのURLからデータをダウンロード
        try:
# 画像ファイルのURLからデータを取得
          image = requests.get(img_url)
# 保存先のファイルパスにデータを保存
          open(save_path, 'wb').write(image.content)
# 保存したファイル名を表示
          print(save_path)
          time.sleep(1.0)
        except ValueError:
# 失敗した場合はエラー表示
                print("ValueError!")

問題発生

取得したい画像が全て保存できていないのです…私は原因として以下を考察しました.

  • 取得したい画像がslider設定されているのを考慮していなかったことが原因か?

    • でもsliderがどのように制御されているかわからない

    • もしjs制御されている場合,

      • requestsでjs動かす方法もなくはないが,成功するかどうかはsliderの実装方法に依存する.そのためrequestsでの試行は避けた方がいいかも.

      • chat-gptもseleniumを推奨してきた.seleniumでのスクレイピングにチャレンジしてみよう!

        • -->ここまで使ってきたdockerではseleniumを使える環境を構築するのに時間がかかりそうなのでcolabでやることに.

sliderに設定されている画像

ii) seleniumライブラリも使用してみる

おおまかに行ったことは以下の通り.

  • colabでseleniumが使用できるようにする

  • itemページのリンクを取り出してリスト化(requestsを使っている)

  • スライダーを探してハンドルを初期化

  • スライダーが設定されているdiv data-ndex 部分を1つずつクリックして

  • その配下にあるpictureタグからからimgを取り出す

  • 処理が終わったらスライダーのハンドルを距離分動かす

# colabでseleniumを使用できるようにする

%%shell
# Ubuntu no longer distributes chromium-browser outside of snap
#
# Proposed solution: https://askubuntu.com/questions/1204571/how-to-install-chromium-without-snap

# Add debian buster
cat > /etc/apt/sources.list.d/debian.list <<'EOF'
deb [arch=amd64 signed-by=/usr/share/keyrings/debian-buster.gpg] http://deb.debian.org/debian buster main
deb [arch=amd64 signed-by=/usr/share/keyrings/debian-buster-updates.gpg] http://deb.debian.org/debian buster-updates main
deb [arch=amd64 signed-by=/usr/share/keyrings/debian-security-buster.gpg] http://deb.debian.org/debian-security buster/updates main
EOF

# Add keys
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys DCC9EFBF77E11517
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 648ACFD622F3D138
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 112695A0E562B32A

apt-key export 77E11517 | gpg --dearmour -o /usr/share/keyrings/debian-buster.gpg
apt-key export 22F3D138 | gpg --dearmour -o /usr/share/keyrings/debian-buster-updates.gpg
apt-key export E562B32A | gpg --dearmour -o /usr/share/keyrings/debian-security-buster.gpg

# Prefer debian repo for chromium* packages only
# Note the double-blank lines between entries
cat > /etc/apt/preferences.d/chromium.pref << 'EOF'
Package: *
Pin: release a=eoan
Pin-Priority: 500


Package: *
Pin: origin "deb.debian.org"
Pin-Priority: 300


Package: chromium*
Pin: origin "deb.debian.org"
Pin-Priority: 700
EOF

# Install chromium and chromium-driver
apt-get update
apt-get install chromium chromium-driver

# Install selenium
pip install selenium
# fake-agentのdebugをする

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from fake_useragent import UserAgent

url = 'https://***/Woman/Brand_A'

options = Options()
ua = UserAgent()
user_agent = ua.random
options.add_argument(f'user-agent={user_agent}')
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')

driver = webdriver.Chrome(options=options)
driver.get(url)
print(driver.title)
# item_url_listに1つずつアクセスして,
# スライダーを探してハンドルを初期化.
# div data-ndex を1つずつ押して,
# 配下にあるpictureタグからからimgを取り出す.
# 処理が終わったらスライダーのハンドルを距離分動かす.

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time

from selenium.webdriver.common.action_chains import ActionChains

from selenium.webdriver.common.by import By


for url in item_url_list:
  driver.get(url)
  div_list = driver.find_elements(By.CSS_SELECTOR, 'div[data-index]')
  for div in div_list:
    text = driver.execute_script("return window.getComputedStyle(arguments[0], ':after').getPropertyValue('content');", div)
    print(text)

またまた問題が発生

Webサイトのhtmlをよくみてみると,スライダーのcurrent位置がどこにあるか(何を現在表示しているか)によって:before,:afterといった要素が出たり消えたりすることに気づきました.これらは疑似要素と呼ぶそうですが,chat-gptによればスクレイピングできる可能性が低そうということがわかってきました.これも加味した上でのコード実行をしてみたのですが,htmlやcssの知識もないのでこれ以上の追求は難しいと判断しました…残念.

今回のまとめ

sliderからの画像取得が難しい,というテーマにした方が適当だったかもしれません…
今回の収穫としては,request,beautiful soup,seleniumといったライブラリのセットアップ方法が理解できましたし,htmlの構造などもこれまでより理解が進みました.エージェント偽装やtime.sleep()なども実際にやってみなければわからないことでした.
今後,スクレイピングを成功させるためには,htmlやcssの知識が多少なりとも必要でしょうし,なにより初心者な私にとってはchat-gptの使いこなしも肝になってきそうだなと思いました.

同じような境遇にいる誰かの参考になることを祈っています.

参考にしたブログ:


この記事が気に入ったらサポートをしてみませんか?