クラウドワークスの案件を自動で監視してメールで通知するプログラムを作ってみました。もちろん、cronで動くので、設定後はほったらかしでも問題はありません。

クラウドワークスはJavaScriptを動かさなくても案件を表示してくれるので、今回はBeautifulSoupとrequestsモジュールだけで実装してみました。

プログラムの概要

フローチャート図

フローチャート図

クラウドワークスのページをrequestsモジュールでダウンロード、BeautifulSoupで解析、文章を整形、メールで送信。

そういう、オーソドックスな流れです。

ソースコード

#! /usr/bin/python3
import bs4,requests,time,os,sys,re,random

#メール送信関係
import smtplib
from email.mime.text import MIMEText
from email.header import Header

#メールの文字コード、送信元、パスワード、宛先の指定
mail_data_file          = open("./mail_data.txt")
raw_mail_data           = mail_data_file.readlines()
mail_data_file.close()

for i in range(len(raw_mail_data)):
    raw_mail_data[i] = re.sub(r"\n","",raw_mail_data[i])

charset         = raw_mail_data[0]
from_email      = raw_mail_data[1]
from_email_pass = raw_mail_data[2]
to_email        = raw_mail_data[3]

#拒否URLリストの読み込み
URL_LIST_FILE        = open('./crowdworkd_url_list.txt')
URL_LIST             = URL_LIST_FILE.readlines()
URL_LIST_FILE.close()

TIMEOUT         = 10
WAIT_TIME       = 0.5 
HEADERS         = {'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:63.0) Gecko/20100101 Firefox/63.0'}
ORIGINAL_URL    = "https://crowdworks.jp"

STABLE_TIME     = 1
RANDOM_TIME     = 2

#リストから抽出したURLがクラウドワークスかをチェックする
def url_check(URL_LIST):

    regular_url = []

    for i in range(len(URL_LIST)):
        if ORIGINAL_URL in URL_LIST[i]:
            url = re.sub("\n","",str(URL_LIST[i]))
            regular_url.append(url)

    return regular_url

#掲載されている案件をチェックする
def check_work(url_list):

    job_list = []

    for i in range(len(url_list)):

        try:
            site_data       = requests.get(url_list[i] , timeout=TIMEOUT , headers=HEADERS)
            site_data.raise_for_status()

        except Exception as e:
            print("ERROR_DOWNLOAD:{}".format(e))

        else:
            parsed_data             = bs4.BeautifulSoup(site_data.content,"lxml")
            raw_job_list_data       = parsed_data.select('.search_results > ul > li')

            job_list_title = []
            title                   = parsed_data.select('h1')

            if type(title) == list and title != []:
                job_list_title.append("================" + str(title[0].text) + "===============")
                job_list.append(job_list_title)

            #募集終了が除外されたリスト
            edited_job_list_data   = ""


            for i in range (len(raw_job_list_data)):
                if "募集終了" not in  raw_job_list_data[i].text:
                    edited_job_list_data    = edited_job_list_data + str(raw_job_list_data[i])

            parsed_data             = bs4.BeautifulSoup(edited_job_list_data,"lxml")

            #タイトル
            title_list              = parsed_data.select('h3')
            #報酬
            reward_list             = parsed_data.select('.payment')
            #募集人数と契約数
            entry_list              = parsed_data.select('.entries')
            #期限
            deadline_list           = parsed_data.select('.expires')
            #リンク
            link_list               = parsed_data.select('h3 > a')

            if len(title_list) == len(reward_list) == len(entry_list) == len(deadline_list) == len(link_list):
                for i in range(len(title_list)):
                    inner_list = []
                    inner_list.append(re.sub("\s\s+"," ",title_list[i].text.strip()))
                    inner_list.append(re.sub("\s\s+"," ",reward_list[i].text.strip()))
                    inner_list.append(re.sub("\s\s+"," ",entry_list[i].text.strip()))
                    inner_list.append(re.sub("\s\s+"," ",deadline_list[i].text.strip()))
                    inner_list.append(ORIGINAL_URL + link_list[i].get("href").strip())

                    job_list.append(inner_list)

        timer = random.random() * RANDOM_TIME + STABLE_TIME
        time.sleep(timer)

    #募集中の二次元リストを返却する
    return job_list 


def send_mail(content):

    string = ""

    for i in range(len(content)):
        for j in range(len(content[i])):
            string  = string + str(content[i][j]) + "\n"
        string  = string + "\n"

    msg = MIMEText("クラウドワークスの案件募集情報を送ります。\n\n" + string ,"plain",charset)
    msg["Subject"] = Header("クラウドワークス案件募集状況".encode(charset),charset)

    smtp_obj =  smtplib.SMTP("smtp.gmail.com", 587)
    smtp_obj.ehlo()

    smtp_obj.starttls()

    smtp_obj.login(from_email , from_email_pass)
    smtp_obj.sendmail(from_email, to_email , msg.as_string())
    smtp_obj.quit()

    return True

if __name__ == "__main__":
    try:
        url_list    = url_check(URL_LIST)
        content     = check_work(url_list)
        if send_mail(content):
            print("True")
        else:
            print("False")

    except KeyboardInterrupt:
        print("\nprogram was ended.\n")
        sys.exit()

Seleniumでブラウザを操作する必要はないので、ソースコードは短めです。

このプログラムの解説

まず、crowdworkd_url_list.txtの中に格納されている、クラウドワークスで案件が表示されているページのURLを抜き出します。

そこからrequestsモジュールでクラウドワークスへアクセスし、BeautifulSoupでHTMLを解析。案件の内容を抽出して文字列を整形、メールで送信します。

工夫したところ

二次元リストを使って案件の情報を取得していく

1つの案件に、報酬や締め切り等の複数の要素があるので、二次元リストを使用して情報を取得していきます。

こうすることで、その後の処理が簡単になります。二次元リストなので、メール送信だけでなくCSVへの記録などにも応用できるでしょう。

動作環境

  • BeautifulSoup、requestsなどのサードパーティ製モジュールがインストール済み
  • カレントディレクトリにcrowdworkd_url_list.txtmail_data.txtが存在し、必要事項が記入されている

実際に動かしてみる

mail_data.txtに文字コード、送信元のメールアドレスとアプリパスワード、送信先のアドレスを格納します。

mail_dataの中身

crowdworkd_url_listの中に、クラウドワークスのURLを入力します。今回はPythonの検索結果であるhttps://crowdworks.jp/public/jobs/?utf8=%E2%9C%93&search[keywords]=Pythonを格納しました。

そして、プログラムを実行すると、以下のメールが送信されます。クラウドワークスのPythonの案件が全部表示されます。

Pythonの案件がメールで通知された

URL付きなので、メールソフトからすぐに案件へアクセス可能です。募集が終了した案件は除外しているので見やすくなっています。

メリットとデメリット

メリット

  • URLを指定すれば後は放置でOK
  • Seleniumは使わないので、低スペックマシンでも動く
  • 監視頻度はcronから指定可能

URLを指定すれば後は放置でOK

これまでの監視プログラムと同様に、ユーザーがやることはチェックするページのURLを指定するだけです。

後はcronが発動して自動的に監視してくれます。

Seleniumは使わないので、低スペックマシンでも動く

クラウドワークスがJavaScriptを使用して案件を表示するタイプではないので、Seleniumを使う必要がありません。

おそらく、Raspberry Piでも動作するでしょう。

監視頻度はcronから指定可能

監視頻度はcronから指定可能です。例えば、1時間ごとに調べてメールで送信してほしいのであれば以下のように記述します。

00 * * * * user ./check_works.py

デメリット

  • 前回のメールで通知した内容と同じものが送信されてしまう
  • ブックマークに登録して、直接確認する場合と手間が変わらない

前回のメールで通知した内容と同じものが送信されてしまう

前回のメールで送信した内容と、同じものが送信されてしまうことがあります。

この問題は、前回送信した内容をCSVに記録して、一致しないものだけ(新着の案件だけ)を送信することで対策できます。

現状ではそこまでする必要は無いので、あえて実装していません。

ブックマークに登録して、直接確認する場合と手間が変わらない

ブラウザから直接クラウドワークスの案件を見る場合と、大して手間は変わりません。

とはいえ、うっかり確認を忘れてしまうこともあるので、メールで送信してくれれば、良い案件を見逃さずに済むと思います。

結論

これでクラウドワークスへ案件確認のためだけにアクセスする必要はなくなりました。

cronで動作するプログラムは放置しておいても、勝手にやってくれるのでとても便利です。これからもcronで動かすことを前提としたプログラムを開発していきたいところです。

関連記事

cronを使用したウェブスクレイピング

【内部リンク】cronで動作させ、テキストを連続で抽出して保存するウェブスクレイピングをPythonで作ってみた

昔作った、cronを利用したテキストのウェブスクレイピングプログラムです。

自然言語処理を利用して、検索ワードの書き込みすら自動化してしまおうと考えていた、野心的な作品です。

cronの使い方について

【内部リンク】cronの基本的な使い方と活用例について

cronの使い方をまとめた記事です。

cronは指定した時刻にコマンドを実行するので、覚えておいて損はありません。