好きとか嫌いとか投票してるあれなサイトの投票スクリプト

好きとか嫌いとか投票してる治安の悪い海外サーバーのサイトがありますよね?
ああいうの本当に不毛だと思っていて、存在意義なくなっちゃえばいいのにと思っているんですよ。

何が気に入らないって、好きな人からも嫌いな人からもヘイトを巻き上げて広告で稼ぐビジネスモデルなんですよね。しかもユニーク数が増えるように設計されている。よくできていると思います。

そういうサイトで一番しらけるのって何かって言うと自動投票のスクリプトなんですね。
でも、このサイトはよくできていて、IPアドレスを見つつtorのような公開されているプロキシーサーバのIPは事前に弾いてきます。
なので普通は携帯の電波を切り替えまくるとか、一生懸命プロキシーを探すとかが限度かと思います。

AWSつかいませんか?

このサイト、ロボット検知が優れているわけではありません。所詮、みているのはIPアドレスです。
ならば、AWSやGCPの環境をうまく借りることで無数のIPアドレスを得ることができます。

ロボットのコードは文末に置いておきます。このコードを1行変更し、lambdaで起動すればあいつに定期的に投票できるでしょう。

AWS Lambdaはおおよそ10分間に1回の起動であれば高確率で別のIPアドレスからアクセスを実行してくれます。そのため、1日に複数回の投票が可能になります。
複数リージョンを使うなど、使用するipを分散すれば1日数千票を投票可能と思います。
(ただ、Lambdaをたくさん起動するほどAWSの無料枠を食い潰すのでどこかで有料になります)

AWSの初期登録などは自分で調べられる方が行ってください。あまりに無茶な設定をすると高額課金の要因になります。
普通に動かす分には一ヶ月あたりの無料枠に収まるはずです。
コンピュータに疎い人は痛い目を見る可能性が高いためこの記事は具体的な方法を詳細には解説しません。ChatGPTに聞きながら、調べながら、などで実行できる人を対象にしています。

プログラム

Dockerfile

このコンテナはlambdaで実行するように調整されています。

FROM python:3.11

RUN apt-get update && apt-get install -y gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget chromium
RUN apt-get install -y fonts-ipafont fonts-ipaexfont

RUN mkdir /function && \
  pip install --target /function awslambdaric pyppeteer requests

COPY ./app.py /function/
WORKDIR /function
ENV PYTHONPATH "${PYTHONPATH}:/function"
ENV PYPPETEER_HOME=/tmp/
ENV CHROME_PROFILE_PATH=/tmp/

ENTRYPOINT [ "/usr/bin/python3", "-m", "awslambdaric" ]

# フローの実行
CMD [ "app.lambda_handler" ]

app.py

URLは置き換えてください。
好きに投票したい人は追加で1行変更する必要があります。

import asyncio
import traceback
import requests
from pyppeteer import launch

async def execute_browser_tasks():
    print("start execute_browser_tasks")
    browser = await launch(args=[
        '--no-sandbox',
        '--single-process',
        '--disable-dev-shm-usage',
        '--disable-gpu',
        '--no-zygote'
    ],
    executablePath='/usr/bin/chromium',
    #headless =False
    )
    print("end launch")
    page = await browser.newPage()

    await page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36')
    custom_headers = {
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "ja,en-US;q=0.9,en;q=0.8",
        "Sec-Ch-Ua": '"Chromium";v="118", "Google Chrome";v="118", "Not=A?Brand";v="99"',
        "Sec-Ch-Ua-Mobile": "?0",
        "Sec-Ch-Ua-Platform": '"macOS"',
        "Sec-Fetch-Dest": "document",
        "Sec-Fetch-Mode": "navigate",
        "Sec-Fetch-Site": "none",
        "Sec-Fetch-User": "?1",
        "Upgrade-Insecure-Requests": "1"

    }
    await page.setExtraHTTPHeaders(custom_headers)
    await page.setViewport({'width': 1920, 'height': 1280})

    print("start page.goto")
    await page.goto('https://<!!!あいつのドメイン!!!>/people/vote/<!!!投票したい人!!!>')

    # urlにpeople/resultを含む様になるまで実行
    try:
        while not 'people/result' in page.url:
            await asyncio.sleep(8)
            button = await page.xpath('//button[contains(@class, "vote-submit") and contains(text(), "嫌い!")]')
            if not button:
                print("button not found")
                break
            else:
                print("button found")
                form_elements = await page.querySelectorAll('form')
                last_form = form_elements[-1]
                await page.evaluate('(form) => form.submit()', last_form)
                await asyncio.sleep(5)
    except Exception as e:
        print(e)
        print(f"traceback: {traceback.format_exc()}")
        pass

    # page_urlと現在実行環境のIPアドレスをログに出力
    response = requests.get('http://ipinfo.io/json')

    # urlにresultを含むか
    isResult = 'people/result' in page.url

    print(f"isResult: {isResult}, ip: {response.json()['ip']}")

    await browser.close()

def lambda_handler(event, context):
    try:
        loop = asyncio.get_event_loop()
        loop.run_until_complete(execute_browser_tasks())

        # 必要に応じて適切なレスポンスを返す
        return {
            'statusCode': 200,
            'body': 'Function executed successfully!'
        }
    except Exception as e:
        print(e)
        print(f"traceback: {traceback.format_exc()}")
        return {
            'statusCode': 500,
            'body': 'Function executed failed!'
        }

ディレクトリ構造は以下のようになります。
Dockerfileを見て分かる通り、app.pyだけイメージにふくめるだけの構造です。

.
├── Dockerfile
├── app.py

手順

  1. 上記のDockerFileを、lambdaを登録する環境のAWS ECRにdocker pushしてください。

  2. lambdaを作成してください。このとき、コンテナイメージを選択し、1で登録したイメージを使用するようにします。
    なお、関数名Aを10分より短い間隔で起動するとIPアドレスが重複しやすいですが、関数名A,Bを同時に起動する場合異なるIPアドレスで起動するようです。

  3. lambdaの設定を変更してください。使用するメモリは512MB、タイムアウトは1分30秒とします

  4. テストを実行してください。テストデータは必要としないので、何を投げても大丈夫です。成功したら次に進みます

  5. EventBridgeをlambdaの画面から設定してください。これについては詳細を後に書きます。

画像
設定完了の状態

EventBridge

lambdaの画面からトリガーを追加、を選択して具体的な項目を入力していきます。
EventBridgeは決められたスケジュールに基づいてlambdaを起動するトリガーを担います。例えば、下記は5,15,25,35…のようにx5分ごとに実行されるcronと呼ばれるスケジュールマネージャの設定を行っています。

画像

注意事項

  1. どれくらいの頻度でアクセスすることになるかについてはよく留意してください。秒間アクセスを上げすぎるとDDoSのような攻撃とみなされても仕方がありません。
    また、このような自動投票自体褒められるものではありませんが、個人的にはそれ以上にあんなサイトは存在意義を失えばいいのにと思っている次第です。

  2. コードの動作確認、lambdaにおけるIPアドレスの振る舞いなどは確認済みです。

  3. コードの保守自体は行う予定はありません

  4. 実行するリージョンはどこでも良いです。リージョンをまたぐほど引けるIPは増えます。ただ、アメリカとかになるとアクセスに時間がかかってくるとかはあります。

投票のための設定が難しいんだけど?

トラブルを防ぐためにも、あきらめてください。(知識のある人向けには、そこまで難しいことはしていないのです)
またはプログラミングとクラウド技術を少し勉強してみましょう。

続きをみるには

残り 0字

¥ 300

この記事が気に入ったら、サポートをしてみませんか?
気軽にクリエイターの支援と、記事のオススメができます!
コメントを投稿するには、 ログイン または 会員登録 をする必要があります。
好きとか嫌いとか投票してるあれなサイトの投票スクリプト|DWU