1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

NIJIBOXAdvent Calendar 2018

Day 21

BeautifulSoup4 であるページが参照している内部のimg,js,cssをピックアップする(+おまけ)

Last updated at Posted at 2018-12-21

目的

http://example.com というサイトがあって、レスポンス内容に含まれている

  • imgタグで呼んでいる画像URL
  • scriptタグで呼んでいるスクリプトのURL
  • linkタグで呼んでいるスタイルシートのURL

をピックアップした後に、 同じホスト名から取るもの をリストアップするだけのもの。

コード

import requests
from bs4 import BeautifulSoup

def _fetch_targets(url: str) -> list:
    """指定したURLにアクセスした際に、直後に取る画像,JS,CSSファイルのURLリストを作る
    """
    session = requests.Session()
    resp = session.get(f"{url}/")
    soup = BeautifulSoup(resp.content, 'html.parser')
    # Image
    img_tags = soup.find_all('img')
    img_tags_origin = [
        s.attrs['src'] for s in img_tags
        if s.attrs['src'].startswith(url)
    ]
    # JS
    script_tags = [
        s for s in soup.find_all('script')
        if 'src' in s.attrs
    ]
    script_tags_origin = [
        s.attrs['src'] for s in script_tags
        if s.attrs['src'].startswith(url)
    ]
    # CSS
    css_tags = [
        s for s in soup.find_all('link')
        if 'stylesheet' in s.attrs['rel']
    ]
    css_tags_origin = [
        s.attrs['href'] for s in css_tags
        if s.attrs['href'].startswith(url)
    ]
    # 一括加工
    targets = img_tags_origin + script_tags_origin + css_tags_origin
    return [t.replace(url, '') for t in targets]

# 使い方
if __name__ == '__main__':
    _fetch_targets('http://example.com')

中身概要

基本的にはBeautifulSoup4の機能に乗っかり、「タグを探索→特定属性によるフィルタ→全部を束ねて整形」というシンプルな構成

imgタグは、「いくらなんでもsrcはあるだろう」と決めつける
scriptタグは、タグ内に直接書くことも可能なので、「src属性を持っていること」でフィルタリング

  • linkタグは、いろいろと入り乱れているので、「rel属性がstylesheetであること」でフィルタリング

linkタグの属性について

CSSのものだけをlinkタグから抽出するときにrel属性を使うのだけれど、リストであることを初めて知った気がする。

本当の目的

Locust.ioを使ったサーバーへの負荷テストをしたくて、アクセス先だけでなくそのページが参照しているリソースも含めて処理したほうが負荷テストになるかなと思い実装してみた。

残りのコード

locustfile.py
from locust import HttpLocust, Task, TaskSet

# --------
# この辺に、さっきの_fetch_targetsが入る
# --------

def generate_get_task(task: Task, url: str) -> callable:
    """タスクジェネレータです
    """
    def _get_task(task: Task):
        task.client.get(url)
    return _get_task


class TopPageWithResources(TaskSet):
    """テスト振る舞いの定義セット
    """
    def __init__(self, parent):
        """アクセス対象のテストをまるごとセットする
        """
        super().__init__(parent)
        self.tasks = []
        self.tasks.append(generate_get_task(self, '/'))
        for t in _fetch_targets(self.locust.host):
            self.tasks.append(generate_get_task(self, t, counter))


class WebsiteUser(HttpLocust):
    """テスト実行の実設定
    """
    task_set = TopPageWithResources
    min_wait = 500
    max_wait = 900

動的に増やすときの正しい位置を調べるのが面倒がわからなかったので、
タスクを定義する関数を別途用意して、テスト対象 + _fetch_targetsで見つけたURL全てを、tasksに突っ込んでみた。

こんな感じになる

スクリーンショット 2018-12-18 16.59.20.png

トップページの構成が変わっても、そのままシナリオとして使えそうで、個人的には便利かなと思っている。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?