Seleniumを使用しないスクレイピング

# はじめに：
普段スクレイピング作業を行う際にSeleniumを利用しておりましたが、requestsを使っても同じようなことができないのかな、とふと気になったので試してみました。

なお、以下の記事の内容はChatGPTを使って作成しました。
https://chat.openai.com/share/3d2cd98a-26ea-49d2-b166-3913230053e5

# 環境
mac 13.4.1
Python 3.10.0

# 実際の手順
### 1. Flaskを使用したテスト用Webサイトの作成 ###

既存のWebサイトに直接スクレイピングを行うのは気が引けたので、Flaskを使用してログイン機能を持つWebサイトを作成します。
ログイン後には、ユーザーによる任意の入力を受け付け、特定の入力に対して異なるレスポンスを返すようにします。

```app.py
from flask import Flask, request, render_template, redirect, url_for
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user

app = Flask(__name__)

# flask_loginの設定
app.config['SECRET_KEY'] = 'your_secret_key'
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = "login"

# ユーザー情報のハードコーディング
# 実際の環境ではDB等を使用して保存・取得します。
users = {"admin": {"password": "password"}}

class User(UserMixin):
    def __init__(self, id, active=True):
        self.id = id
        self.active = active

    def is_active(self):
        return self.active

@login_manager.user_loader
def load_user(user_id):
    return User(user_id)

@app.route('/')
@login_required
def home():
    return render_template('index.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        user_id = request.form.get('user_id')
        password = request.form.get('password')
        if user_id in users and users[user_id]['password'] == password:
            user = User(user_id)
            login_user(user)
            return redirect(url_for('home'))
        else:
            return "Invalid user_id or password"
    else:
        return render_template('login.html')

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('login'))

@app.route('/page', methods=['POST'])
@login_required
def page():
    input_value = request.form.get('input_value')
    if input_value == '1':
        return 'This Page is 1'
    elif input_value == '2':
        return 'This Page is 2'
    else:
        return 'Invalid value'

if __name__ == "__main__":
    app.run(debug=True)
```

```index.html
<!DOCTYPE html>
<html>
    <body>
        <h1>Welcome!</h1>
        <form action="/logout" method="post">
            <input type="submit" value="Logout">
        </form>
        <form action="/page" method="post">
            <input type="text" id="input_value" name="input_value">
            <input type="submit" value="Submit">
        </form>
    </body>
</html>
```

```login.html
<!DOCTYPE html>
<html>
    <body>
        <form method="post">
            <label for="user_id">User ID:</label><br>
            <input type="text" id="user_id" name="user_id"><br>
            <label for="password">Password:</label><br>
            <input type="password" id="password" name="password"><br>
            <input type="submit" value="Submit">
        </form> 
    </body>
</html>

```


### 2. Python requestsを使用したWebサイトの自動操作 ###

次に、Pythonの`requests`ライブラリを使用して、作成したWebサイトにログインし、入力を自動化する方法を解説します。ログインと入力の送信は、POSTリクエストを送信することで実現します。

```bot.py
import requests

# FlaskアプリのURLを指定します
url = 'http://localhost:5000'

# ログインページへのリクエストを作成します
s = requests.session()

login_payload = {
    'user_id': 'admin', 
    'password': 'password'
}

# ログインのPOSTリクエストを送信します
login_req = s.post(url + '/login', data=login_payload)

if login_req.status_code == 200:
    print('Successfully logged in.')
else:
    print('Failed to log in.')

# 新しく追加されたフォームへの値の送信を行います
form_payload = {
    'input_value': '1'  # 1または2を送信します
}

form_req = s.post(url + '/page', data=form_payload)  # 'page'という新しいエンドポイントにPOSTリクエストを送信します

if form_req.status_code == 200:
    print('Successfully submitted the form.')
    print('Response:', form_req.text)  # サーバからのレスポンスを表示します
else:
    print('Failed to submit the form.')

```

### 3. 実行結果 ###

```bot.py
form_payload = {
    'input_value': '1'  # 1または2を送信します
}
```

上記valueで1を入力すると以下のように返される（２を入力すると２が返される）
![スクリーンショット 2023-07-27 12.03.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694345/73e0064f-be26-45bc-e862-0de1f8f2053a.png)


```bot.py
form_payload = {
    'input_value': '3'  # 1または2を送信します
}
```

そのほかの値を入力すると以下のようになる
![スクリーンショット 2023-07-27 12.04.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694345/12f63767-0b56-fccd-93f4-1400604f4296.png)



# 終わりに

実際のWebサイトでは他にも考慮すべきことはあるとは思いますが、一旦seleniumを使わずにログイン状態の維持等ができることがわかりました。

気が向いたらこれをベースにJSによる動的なWebサイトの操作やWebサイト上のデータのダウンロードなどができるか試してみたいです。