【コピペOK】Pythonで楽々スクレイピング!SimpleScraper自作クラスで簡単Webページ解析

Webスクレイピングは、データ収集や情報分析に欠かせない技術ですが、HTMLの構造やタグに煩わされることが多いのが難点です。

そこで登場するのが、今回ご紹介する自作クラス「SimpleScraper」です。SimpleScraperは、複雑なHTMLを意識することなく、Webページの情報を簡単に文字列のリストとして取得、解析できます。

このクラスを使えば、初学者でも気軽にWebスクレイピングに挑戦でき、効率的にデータを収集できるようになります。

この記事では、SimpleScraperの基本的な使い方と、その特長を詳しく解説していきます。

目次

Webスクレイピングの基本概念

1. Webスクレイピングとは?

Webスクレイピングは、Webページ上のデータを自動的に抽出する技術のことです。通常、Webページに表示される情報は人が視覚的に読み取りますが、スクレイピングではPythonのようなプログラムを使用して、HTMLソースから必要なデータを取得します。

  • 実際の用途:
    • データ収集: 商品価格の調査やニュース記事の自動収集。
    • 分析: Web上のレビューやコメントを集計してトレンドを分析。
    • 自動化: 煩雑なデータ収集作業の自動化。

2. スクレイピングの仕組み

Webページは、HTML、CSS、JavaScriptなどのファイルで構成されています。スクレイピングでは、これらの構造を解析し、特定のタグや要素にアクセスして、そこに含まれるデータを抽出します。スクレイピングを行うために、Pythonでは一般的に以下のライブラリが使用されます。

  • Requests: HTTPリクエストを送信し、WebページのHTMLを取得する。
  • BeautifulSoup: 取得したHTMLを解析し、特定のデータを抜き出す。

SimpleScraperとは?

SimpleScraperは、Pythonを使って手軽にWebページのデータを取得・解析するために自作したクラスです。

一般的に、Pythonでスクレイピングを行う場合、HTMLのタグやクラス名を使って抽出したい情報を特定します。
この方法はWebページの構造に合わせて柔軟に対応できますが、それなりのHTMLの知識が必要であり、少なからずHTMLの解析を伴います。

これは、HTMLに慣れない方にとっては非常に敷居が高くなります。

SimpleScraperは、Webページを単純なテキストに変換(HTMLのタグを削除)した結果に対して、「特定の条件に一致する部分を抽出する」というアプローチを採用しています。

これにより、スクレイピング初心者や簡単なWeb解析が目的の方は、HTMLの構造やタグを意識しなくても、単純に文字列に対して検索するだけで欲しい情報が取り出せるようになります。

url = 'https://kakaku.com/item/K0001521843/spec/#tab'
ss = SimpleScraper()
ss.request(url,folder="p:/html")

print(ss.get_text())
ss.seek("前週比")
res = ss.get(sword="<>",eword="円")
print(res)

1. 特徴

SimpleScraperは、初心者でも使いやすいように設計されたPythonクラスで、以下の特徴があります。

  • HTMLからタグを削除し、普通のテキストに変換することが可能
  • テキストの中から指定したキーワードを取得するための便利なメソッドを用意
  • ページを読み込む際、指定した秒数だけウェイトさせることが可能
  • ページに含まれるリンク、画像については、一括してタグの中身やURLをリストとして取得が可能
  • ページに含まれるTableについては、一括して行の値をリスト形式で取得が可能
  • 指定したURLのページを指定フォルダに保存することで、そのファイルからスクレイピングが可能
  • 指定したURLの画像をダウンロードすることが可能

SimpleScraper使い方

下記URLからlibs.zip をダウンロードし、 libs というフォルダ内に解凍してください。

libs フォルダ内の SimpleScraperを利用するために、あなたのプログラムに次のコードを追加してください。

import os
import sys
sys.path.append(os.path.join(os.path. dirname(__file__), '../libs'))
from simple_scrape import SimpleScraper

Webページの取得

指定したURLからWebページを取得します。取得に成功すると BeautifulSoup オブジェクトが返されます。
尚、 ここで戻り値を受け取らなくても、soup プロパティを参照することで、後から取得できます。

ss = SimpleScraper(wait=(2,5))
soup = ss.request('https://kakaku.com/item/K0001521843/spec/#tab')

SimpleScraper() の引数 wait は 1ページ取得するごとに発生させたいウェイト時間です。
wait=(2,5) と記述しているので、下限2秒~上限5秒の間で乱数を発生させ、その間だけウェイトします。
引数を省略した場合、無条件に1秒のウェイトが入ります。

Webページの保存

request の folder 引数にフォルダを指定すると、取得したWebページを保存することができます。その際、encoding引数で文字コードを指定( 省略時は utf-8 )することができます。

ss = SimpleScraper(wait=(2,5))
soup = ss.request("https://kakaku.com/item/K0001521843/spec/#tab",folder="d:/myfolder")

Webページにインデックスが使われている場合、指定した範囲のWebページを自動でファイルに保存することができます。URLのインデックス部分を {0} に置き換えて指定してください。

ss = SimpleScraper(wait=(2,5))
ss.site_save("p:/url","https://kakaku.com/pc/note-pc/itemlist.aspx?pdf_pg={0}",1,10)

この例の場合、{0} の部分が 1から10 まで変化させながら、Webページを "p:/url" フォルダに保存します。

ファイル保存されたWebページの取得

request_from_file() を使うと、保存したWebページを読み込み、 BeautifulSoup オブジェクトとして返してくれます。
こちらも request() メソッドと同じく、soup プロパティを参照することで、後からBeautifulSoup オブジェクトを取得できます。

ss = SimpleScraper()
soup = ss.request_from_file("p:/html/https___kakaku.com_item_K0001521843_spec_#tab.html")

Webページに含まれる画像の一括取得

img タグの alt属性(=テキスト)とsrc 属性(=画像のURL)を取り出し、タプルのリストとして返します。
また、rowsプロパティを参照することで、後から結果を取り出すことが可能です。

ss = SimpleScraper(wait=(2,5))
ss.request('https://kakaku.com/item/K0001521843/spec/#tab')
for tag in ss.get_image_tags():
    print(tag)

('', 'https://kakaku.com/cpc/cpcclickcertificate/us/clear.gif')
('', 'https://kakaku.com/cv/cvclickcertificate/us/clear.gif')
('価格.com', 'https://img1.kakaku.k-img.com/images/logo.gif')
('パソコン', 'https://img1.kakaku.k-img.com/images/icon_pc.gif')

画像のURLだけを取得したい場合は、get_image_urls() メソッドを使います。

ss = SimpleScraper(wait=(2,5))
ss.request('https://kakaku.com/item/K0001521843/spec/#tab')
for url in ss.get_image_urls():
    print(url)

https://kakaku.com/cv/cvclickcertificate/us/clear.gif
https://img1.kakaku.k-img.com/images/icon_pc.gif
https://img1.kakaku.k-img.com/images/productimage/l/K0001521843.jpg
https://img1.kakaku.k-img.com/images/productimage/ts/K0001521843_0001.jpg

Webページに含まれるリンクの一括取得

a タグの テキスト部分とhref属性(リンクURL)を取り出し、タプルのリストとして返します。
また、rowsプロパティを参照することで、後から結果を取り出すことが可能です。

ss = SimpleScraper(wait=(2,5))
ss.request('https://kakaku.com/item/K0001521843/spec/#tab')
for tag in ss.get_link_tags():
    print(tag)

('ノートパソコン', 'https://kakaku.com/pc/note-pc/')
('ノートパソコン', 'https://kakaku.com/pc/note-pc/')
('ゲーミングノートPC', 'https://kakaku.com/pc/gaming-note/')
('モバイルノート', 'https://kakaku.com/pc/mobile-note/')
('ASUS(エイスース)', 'https://kakaku.com/pc/note-pc/itemlist.aspx?pdf_ma=10')

リンクのURLだけを取得したい場合は、get_link_urls() メソッドを使います。

ss = SimpleScraper(wait=(2,5))
ss.request('https://kakaku.com/item/K0001521843/spec/#tab')
for url in ss.get_link_urls():
    print(url)

https://kakaku.com/pc/note-pc/itemlist.aspx?pdf_ma=10
https://kakaku.com/pc/gaming-note/
https://kakaku.com/pc/gaming-note/itemlist.aspx?pdf_ma=10
https://kakaku.com/pc/mobile-note/

Webページに含まれるTableの取得

table タグの tr(行),th(ヘッダ),td(カラム) を解析し、次のフォーマットのリストとして返します。

[ <テーブル番号行番号ヘッダ識別子>,'値1','値2','値3',・・・ ]

例えば、<1:2:H> は Webページ内の1番目のテーブルの2行目で、かつ th タグで定義されてたことを表します。
尚、この解析結果は、rowsプロパティを参照することで、後から取り出すことが可能です。

ss = SimpleScraper(wait=(2,5))
ss.request('https://kakaku.com/item/K0001521843/spec/#tab')
for table in ss.get_tables():
    print(table)

['<1:1:D>', '', '', '', '', '', '', '']
['<2:1:H>', '基本スペック']
['<2:2:H>', 'CPU', '画面サイズ']
['<2:3:D>', 'AMD Ryzen 9 7940HS4GHz/8コア', '13.4 型(インチ)']
['<2:4:H>', '画面種類', '解像度']

画像ファイルのダウンロード

download_image() メソッドの第一引数に画像ファイルのURL、第2引数にダウンロード先のフォルダを指定します。
第3引数はダウンロード対象となる最小のピクセル数、第4引数は画像ファイルの拡張子が指定できますが、いずれも省略可能です。

ss = SimpleScraper(wait=(2,5))
ss.request('https://kakaku.com/item/K0001521843/spec/#tab')
urls = ss.get_image_urls() # 画像のリンク取得
ss.download_image(urls ,"d:/img",min_size=(100, 100),file_extension="jpg")

Webページをテキストとして取得

get_text() メソッドを使うと、Webページ全体を改行コードで分割してリストを作成し、更にHTMLタグの中身を連番(行番号:列番号)に置き換えて戻り値として返します。
また、戻り値は rows プロパティにも格納されます。

ss = SimpleScraper(wait=(1,5))
ss.request('https://kakaku.com/item/K0001521843/spec/#tab')
print(ss.get_text())

['<1:0>', '<2:0>', '<3:0>', '<4:0>価格.com - ASUS ROG Flow X13 GV302XI GV302XI-R9R4070 [オフブラック] スペック・仕様<4:1>', '<5:0>', '<6:0>', '<7:0>', '<8:0>', '<9:0>', '<10:0>', '<11:0>', '<12:0>', '<13:0>', '<14:0>', '<15:0>', '<16:0>', '<17:0>', ROG Flow X13 GV302XI GV302XI-R9R4070 [オフブラック] スペック・仕様・特長', '<18:0>', '<19:0>', '<20:0>',<21:0>ゲーミングノートPC<21:1>', '<22:0>モバイルノート<22:1>'

テキストを検索し、中身を取り出す

get_text() メソッドで rowsプロパティに格納されたテキストに対して、seek() と get() メソッドを組み合わせることで、指定した条件に一致する箇所を抽出すことができます。

seek() メソッドは、引数に指定された文字列を rows プロパティの先頭から検索し、ヒットした行位置をカレント行として保持します。
get () メソッドは、カレント行に対して、開始と終了で指定した文字列に囲まれた範囲を抽出します。

ss = SimpleScraper(wait=(1,5))
ss.request('https://kakaku.com/item/K0001521843/spec/#tab')
ss.get_text()                      # テキストに変換
ss.seek("<22:0>")                  # <22:0> という文字列を検索
res = ss.get(start=">",end="<")    # ">" を始点、"<" を終点として、その範囲内の文字列を取得 
print(res)

get_text() によって全てのHTMLタグが <行番号:列番号> に置き換えられるため、これをマーカーとして利用することで、特定範囲の抽出が簡単に行えます。

上記のサンプルでは 、seek() で "<22:0>" を含む行にカレントを移動し、get () で '>' と '<' に囲まれた部分を抽出しています。そして、その結果 "モバイルノート" という文字列が取得できます。

モバイルノート

get() メソッドには、start と end 以外に、pos、offset、omit、trim が引数が用意されています。

get(pos=None, offset=0, limit=1, start=None, end=None, omit="",trim="<>"):

pos : 抽出対象の行番号(抽出位置)を指定するもので、省略時は seek() で移動後のカレント位置が使われます。
offset :pos(省略時はカレント位置)に対する行番号のオフセット値で、実際には pos+offset が抽出位置となります。
limit :抽出位置から何行取得するかを指定するもので、省略時は1行を取得します。
start:抽出の起点となるキーワードです。
end: 抽出の終点となるキーワードです。
omit:抽出される文字列に対して、消したい文字(又は文字列)を1つだけ指定できます。
trim:抽出される文字列の前後にある特定の文字を削除します。ここで指定した値は、stripに渡されます。

SimpleScraperのリファレンス

メソッド名概要引数戻り値
__init__(
url=None,
wait=(1,1)
)
クラスのコンストラクタ。スクレイピング対象のURLと待機時間を設定します。- url (str, optional): スクレイピング対象のURL。なし
- wait (tuple): 待機する秒数の最小値と最大値。
request(
url,
folder=None,
encoding='utf-8',
wait=None
)
指定したURLからコンテンツを取得し、オプションでHTMLファイルを保存します。- url (str): 取得するコンテンツのURL。BeautifulSoupオブジェクトまたはエラーメッセージ
- folder (str, optional): 保存先フォルダーのパス。
- encoding (str): 保存するファイルのエンコーディング。
- wait (tuple): 取得前に待機する秒数の最小と最大。
sleep(wait=(1, 1))指定した値の範囲で乱数を発生させ、その秒数だけ待機します。- wait (int, tuple): 待機するランダム値の最小値と最大値。なし
site_save(
folder,
url,
start,
end,
encoding="shift-jis",
wait=None
)
指定されたURL範囲のHTMLをダウンロードし、指定されたフォルダに保存します。- folder (str): 保存先のフォルダパス。なし
- url (str): ダウンロードするURLのフォーマット。
- start (int): 開始番号。
- end (int): 終了番号。
- encoding (str, optional): エンコーディング。
- wait (float, optional): 各リクエスト間の待ち時間 (秒)。
request_from_file(
file_path,
encoding='utf-8'
)
指定したファイルからHTMLを読み込みます。- file_path (str): 読み込むHTMLファイルのパス。BeautifulSoupオブジェクトまたはエラーメッセージ
- encoding (str): ファイルのエンコーディング。
get_image_tags()imgタグを取得します。なしimgタグのリスト
get_link_tags()リンクタグを取得します。なしリンクタグのリスト
get_image_urls()画像のURLを取得します。なし画像のURLのリスト
get_link_urls()リンクの文字列とURLをタプルで取得します。なしリンクの文字列とURLのタプルのリスト
get_tables()テーブルデータを取得します。なしテーブルの行とカラムのリスト
get_html()HTMLを取得します。なしHTMLの行ごとのリスト
get_text(
separator="<>"
)
テキストを取得します。- separator (str, optional): 置き換える文字列。デフォルトは"<>"。テキストの行ごとのリスト
_replace_separator(
lines,
separator="<>"
)
文字列のリストに含まれseparator に対して、要素番号と出現頻度数からなる連番を付与します。- lines(list): 文字列リスト連番が付与された文字列のリスト
- separator(str): 置き換える文字列。デフォルトは"<>"
download_image(
image_urls,
output_folder,
min_size=(100, 100),
file_extension=None
)
画像をダウンロードします。- image_urls (list): 画像のURLのリスト。なし
- output_folder (str): 画像を保存するフォルダーのパス。
- min_size (tuple): 最小サイズ (width, height)。
- file_extension (str): 保存する画像の拡張子。
_make_path(url)URLを元にファイル名を生成します。- url (str): URL。不正な文字を置換したファイル名
seek(
keyword,
pos=None
)
指定した行番号を起点にキーワードを含む行を検索します。- keyword (str, optional): 検索するキーワード。キーワードが見つかった行番号
- pos (int, optional): 検索を開始する位置。
省略時はカレント行。
get(
pos=None,
offset=0,
limit=1,
start=None,
end=None,
omit="",
trim="<>"
)
指定した位置に対して、指定した範囲のテキストを取り出します。- pos (int, optional): 起点となる行番号。指定した範囲のテキスト
- offset (int): 行数のオフセット。
- limit (int): 取得する行数。
- start(str, optional): 開始キーワード。
- end(str, optional): 終了キーワード。
- omit (str): 除外する文字列。
- trim (str): 置き換える文字列。

SimpleScraperのソースコード

import os
import re
import random
import time
from PIL import Image
from io import BytesIO
from str_util import StrUtil
import pandas as pd
import requests
from bs4 import BeautifulSoup

class SimpleScraper():
    def __init__(self, url=None,wait=(1,1)):
        """
        SimpleScraperクラスのコンストラクタ

        Parameters:
        url (str, optional): スクレイピング対象のURL。指定しない場合はNone。デフォルトはNone。
        """
        self.url = url
        self.soup = None
        self.html = None
        self.rows = []
        self.current = 0
        self.folder = None
        self.wait = wait

    def request(self, url,folder=None,encoding='utf-8',wait=None):
        """
        指定したURLからコンテンツを取得する関数

        Parameters:
        url (str, optional): 取得するコンテンツのURL。指定する場合はNone以外を指定。
        folder (str, optional): HTMLファイルを保存するフォルダーのパス。
        encoding (str): 保存するファイルのエンコーディング。
        wait (tubpe): 取得前に待機する秒数、または秒数の最小と最大。

        Returns:
        BeautifulSoup: 取得したコンテンツのBeautifulSoupオブジェクト
        str: 取得に失敗した場合はエラーメッセージを返す
        None: 例外が発生した場合はNoneを返す
        """
        try:
            self.sleep(self.wait if wait is None else wait)

            if folder != None:
                if not os.path.exists(folder):
                    os.makedirs(folder)
                    self.folder = folder

            print(f"reauest->{url}")

            self.url = url
            response = requests.get(url)
            if response.status_code == 200:
                response.encoding = response.apparent_encoding
                self.html = response.text
                self.soup = BeautifulSoup(response.content, 'html.parser', from_encoding=response.apparent_encoding)
                if folder != None:
                    path = os.path.join(folder,self._make_path(self.url)) + ".html"
                    with open(path, 'w', encoding=encoding) as f:
                        f.write(self.html)
                return self.soup
            else:
                return "Failed to retrieve the content from the URL"
        except Exception as e:
            return None

    def sleep(self,wait=(1,1)):
        """
        指定した値の範囲で乱数を発生させ、その秒数だけ待機する
        Parameters:
        wait (int,int): 待機するランダム値の最小値と最大値。

        Returns:
        """
        # 最小値から最大値までの乱数を生成
        if isinstance(wait, int):
            random_seconds = wait
        else:
            if wait[0] == wait[1]:
                random_seconds = wait[0]
            else:
                random_seconds = random.uniform(wait[0], wait[1])

        print(f"sleep {random_seconds} sec")
        # 乱数の秒数だけ待機する
        time.sleep(random_seconds) 

    def site_save(self,folder,url,start,end,encoding="utf-8",wait=None):
        """
        指定されたURL範囲のHTMLをダウンロードし、指定されたフォルダに保存します。

        Parameters:
            folder (str): 保存先のフォルダパス
            url (str): ダウンロードするURLのフォーマット (例: "https://example.com/page_{}.html")
            start (int): 開始番号
            end (int): 終了番号
            encoding (str, optional): ファイルのエンコーディング. Defaults to "shift-jis".
            wait (float, optional): 各リクエスト間の待ち時間 (秒). Defaults to None.
        """
        for no in range(start,end + 1):
            self.request(url.format(no),folder,encoding=encoding,wait=wait)

    def request_from_file(self, file_path, encoding='utf-8'):
        """
        指定したファイルからHTMLを読み込む関数

        Parameters:
        file_path (str): 読み込むHTMLファイルのパス。
        encoding (str): ファイルのエンコーディング。

        Returns:
        BeautifulSoup: 読み込んだHTMLのBeautifulSoupオブジェクト
        str: 読み込みに失敗した場合はエラーメッセージを返す
        None: 例外が発生した場合はNoneを返す
        """
        try:
            if os.path.exists(file_path):
                with open(file_path, 'r', encoding=encoding) as f:
                    self.html = f.read()
                    self.soup = BeautifulSoup(self.html, 'html.parser')
                    self.url = file_path  # URLの代わりにファイルパスを設定
                    return self.soup
            else:
                return "File does not exist."
        except Exception as e:
            print(f"An error occurred: {e}")
            return None

    def get_image_tags(self):
        """
        Imageタグを取得するメソッド
        Returns:
        list: Imageタグのリスト
              例: [('パソコン', 'https://img1.kakaku.k-img.com/images/icon_pc.gif'),~]
        """
        # BeautifulSoupのfind_allメソッドでImageタグを取得
        image_tags = self.soup.find_all('img')
        # テキストとイメージのURLをタプルのリストに変換
        self.rows = [(tag.get('alt', ''), tag['src']) for tag in image_tags]
        return self.rows

    def get_link_tags(self):
        """
        リンクの文字列とURLをタプルで取得するメソッド
        Returns:
        list: リンクの文字列とURLのタプルのリスト
              例:[('ノートパソコン', 'https://kakaku.com/pc/note-pc/'),~]
        """
        link_tags = self.soup.find_all('a')
        self.rows = [(tag.text, tag.get('href')) for tag in link_tags]
        return self.rows
    
    def get_image_urls(self):
        """
        画像のURLを取得するメソッド
        Returns:
        list: 画像のURLのリスト
              例:['https://img1.kakaku.k-img.com/images/icon_pc.gif',~]
        """
        image_tags = self.soup.find_all('img')
        self.rows = [tag['src'] for tag in image_tags]
        return self.rows

    def get_link_urls(self):
        """
        リンクタグを取得するメソッド
        Returns:
        list: リンクタグのリスト
              例:['https://kakaku.com/pc/gaming-note/',~]
        """
        # BeautifulSoupのfind_allメソッドでリンクタグを取得
        link_tags = self.soup.find_all('a')
        # リンクタグを文字列のリストに変換
        self.rows = [tag.get('href') for tag in link_tags]
        return self.rows

    def get_tables(self):
        """
        テーブルデータを取得するメソッド
        Returns:
        list: テーブルデータのリストのリスト
              例:[['<1:1:D>', '', '', '', '', '', '', ''],~]
        """
        tables = self.soup.find_all('table')
        all_data = []
        table_number = 1

        for table in tables:
            rows = table.find_all('tr')
            row_number = 1
            for row in rows:
                th_tags = row.find_all('th')
                td_tags = row.find_all('td')

                # HTMLタグや改行を取り除く
                if th_tags:
                    all_data.append([f"<{table_number}:{row_number}:H>"] + [ele.get_text(strip=True) for ele in th_tags])
                    row_number += 1
                if td_tags:
                    all_data.append([f"<{table_number}:{row_number}:D>"] + [ele.get_text(strip=True) for ele in td_tags])
                    row_number += 1

            table_number += 1

        self.rows = all_data
        return self.rows

    def get_html(self):
        """
        HTMLを取得するメソッド
        Returns:
        list: HTMLの行ごとのリスト
        """
        self.rows = StrUtil.split_newline(self.html)
        return self.rows

    def get_text(self, separator="<>",is_number=True):
        """
        テキストを取得するメソッド
        Parameters:
        separator (str, optional): HTMLタグを処理しやすいよう、置き換える文字列。デフォルトは"<>"
        Returns:
        list: テキストの行ごとのリスト
              例:[['<1:0>', '<2:0>', '<3:0>', '<4:0>価格',<4:1>','<5:0>スペック',~ ]
        """
        text = self.soup.get_text(separator=separator)
        lines = StrUtil.split_newline(text)
        self.rows = self._replace_separator(lines,separator) if is_number else lines
        return self.rows

    def _replace_separator(self, lines, separator="<>"):
        """
        セパレータで区切られた部分を、行番号とカウント数を付加する

        Args:
            lines (list): テキストの行のリスト
            separator (str, optional): セパレータ文字列. Defaults to "<>".

        Returns:
            list: 変換後の行のリスト
        """

        return [
            "".join(
                f"{separator[0]}{index}:{count}{separator[1]}{part}"
                for count, part in enumerate(line.split(separator)[1:])
            )
            for index, line in enumerate(lines)
            if line
        ]
    
    def download_image(self, image_urls, output_folder, min_size=(100, 100), file_extension=None):
        """
        画像をダウンロードするメソッド

        Parameters:
        image_urls (list): ダウンロードする画像のURLのリスト
        output_folder (str): 画像を保存するフォルダーのパス
        min_size (tuple): ダウンロードする画像の最小サイズ (width, height)
        file_extension (str): 保存する画像の拡張子
        """
        # 出力先フォルダーが存在しない場合は作成
        if not os.path.exists(output_folder):
            os.makedirs(output_folder)

        for url in image_urls:
            try:
                # ファイルの拡張子を取得
                if file_extension is not None:
                    url_extension = url.split('.')[-1].lower()
                    if url_extension != file_extension.lower().replace('.',''):
                        continue

                # 画像を取得
                response = requests.get(url)
                if response.status_code == 200:
                    # 画像のサイズをチェック
                    image = Image.open(BytesIO(response.content))
                    width, height = image.size

                    # 指定サイズを超える場合のみダウンロード
                    if width >= min_size[0] and height >= min_size[1]:
                        # ファイル名を作成
                        filename = self._make_path(url)  # 不正な文字を'_'に置換
                        filename = f"{filename}.{file_extension}"  # 拡張子を追加
                        file_path = os.path.join(output_folder, filename)

                        # 画像を保存
                        image.save(file_path)
                        print(f"Downloaded: {file_path}")
                    else:
                        print(f"Skipped (too small): {url} (size: {width}x{height})")
                else:
                    print(f"Failed to download: {url}")
            except Exception as e:
                print(f"Error downloading {url}: {e}")

    def _make_path(self,url):
        return re.sub(r'[<>:"/\\|?*]', '_', url)  # 不正な文字を'_'に置換

    def seek(self, keyword,pos=None):
        """
        posで指定した行番号を起点にキーワードを含む行を検索し、見つかった行番号を返を返すと共にカレント行番号を更新する。
        pos を None にすると、直前の検索位置から検索を開始する。
        Keywordを None にすると、前回検索したキーワードを引き継ぐ。
        Keywordが見つからない場合、-1 を返す

        Parameters:
        keyword (str, optional): 検索するキーワード。
        pos (int, optional): 検索を開始する位置。指定しない場合はNone。

        Returns:
        int: キーワードが見つかった行番号(カレント行番号)。
        """
        pos = self.current if pos is None else pos

        for i in range(pos, len(self.rows) - pos):
            if keyword in self.rows[i]:
                self.current = i
                return i
        return -1

    def get(self, pos=None, offset=0, limit=1, start=None, end=None, omit="",trim="<>"):
        """
        pos + offset で指定した行位置に対して、sword と eword で囲まれた範囲を取り出す関数。
        pos に Noneを指定すると、カレント位置が指定される。
        limit を指定すると、指定した行位置を起点に limit で指定した行を1つの文字列に結合して返す。

        Parameters:
        pos (int, optional): 取得を開始する位置。指定しない場合はNone。
        offset (int, optional): 取得位置のオフセット。デフォルトは0。
        limit (int, optional): 取得する行数の制限。デフォルトは1。
        start (str, optional): 開始キーワード。指定しない場合はNone。
        end (str, optional): 終了キーワード。指定しない場合はNone。
        omit (str, optional): 除外する条件。指定しない場合はNone。
        trim (str, optional): 削除したい前後の文字。(stripに渡す文字列と同じ)
    
        Returns:
        str: 取得した行の文字列
        """
        res = ""
        pos = self.current if pos is None else pos
        pos += offset
        count = len(self.rows)
        if pos < count:
            if start is None:
                return self.rows[pos]
            limit += pos
            limit = limit if limit < count else count
            for line in self.rows[pos:limit]:
                res += StrUtil.between(line.strip(trim), start, end)

        return res.replace(omit,"")

まとめ

この記事では、Pythonで簡単にスクレイピングを行うための SimpleScraper 自作クラスの紹介と使い方をサンプルコード付きで解説しました。

SimpleScraperは、初心者にも優しい設計で、手軽にWebスクレイピングを実現できるPythonクラスです。

Webページに含まれる HTML タグやID、クラス名を解析しなくとも、文字列操作だけでWebページから特定の条件に一致する箇所を取り出すことができます。

更に、リンクや画像のURLだけを抜き出したり、画像ファイルをダウンロードする機能も実装しています。

「ちょっとしたデータを取得したい」「面倒な解析は省きたい」といった場面で、SimpleScraper はとても便利です。手軽にスクレイピングを試してみたい方は、ぜひこのクラスを使ってみてください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

目次