8
2

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.

転職ドラフトでどの時間帯に指名が多いのか調べてみた

Last updated at Posted at 2018-04-03

転職ドラフトに参加してみたけど

いつ指名がくるんだろう、とそわそわ...
使ったことある人に聞くと最終日にどっと来るらしい
本当に?
調べてみよう

スクレイピングどうしよう

調べてみたけどNodeとかpythonとかGoがいいとか?
特にメリットはないんだろうけど最近ちょっと興味のあるGoでググりながらやってみる

Goでスクレイピング

goqueryを使ってみる


go get github.com/PuerkitoBio/goquery

過去の開催回全部集めるのは一旦あきらめて
第10回に絞ってみる

ソース

draft.go

package main

import (
    "github.com/PuerkitoBio/goquery"
    "fmt"
    "regexp"
    "time"
    "strings"
    "strconv"
)


func main() {
    baseUrl := "https://job-draft.jp"
    userOfferInfo := []string{}
    offerTimes := []string{}

    // リンク集取得
    // fesLinks := getFestivalLinks()
    // fmt.Println(fesLinks)

    // 要素取得
    // fesLinksをforeachしてbaseUrlとくっつける
    fesInfo := getFesInfo(baseUrl + "/festivals/10")
    fmt.Println(fesInfo[0])
    fmt.Println("参加人数 : " + fesInfo[1])
    fmt.Println("総指名数 : " + fesInfo[3])

    // ユーザ一覧取得
    userLinks := getUserInfo(baseUrl, "/festivals/10/users")
    fmt.Println("取得したユーザ数 : " + strconv.Itoa(len(userLinks)))

    // 各ユーザのオファー情報まとめて取得
    fmt.Println("各ユーザのオファー情報取得")
    for i := 0; i < len(userLinks); i++ {
        userOfferInfo_tmp := getUserOfferLinks(baseUrl + userLinks[i], "10")
        userOfferInfo = append(userOfferInfo, userOfferInfo_tmp...)
        fmt.Println(strconv.Itoa(i) + " / " + strconv.Itoa(len(userLinks)) + "人")
    }

    // オファー情報から時間取得
    for i := 0; i < len(userOfferInfo); i++ {
        offerTime := getOfferTime(baseUrl + userOfferInfo[i])
        offerTimes = append(offerTimes, offerTime)
        fmt.Println(strconv.Itoa(i) + " / " + strconv.Itoa(len(userOfferInfo)) + "オファー")
    }

    fmt.Println("全オファー時間")
    fmt.Println(offerTimes)
}

/**
 * 過去の転職ドラフト開催回リンク集配列を取得
 */
func getFestivalLinks() ([]string) {
    time.Sleep(1 * time.Second)
    url := "https://job-draft.jp/festivals/"
    fesLinks := []string{}

    doc, err := goquery.NewDocument(url)
    if err != nil {
        panic(err)
    }

    // /festivals/[数値] のリンクを探す
    reg := regexp.MustCompile(`\/festivals\/[0-9]+\z`)
    doc.Find("a").Each(func(_ int, sel *goquery.Selection) {
        url, _ := sel.Attr("href")
        if reg.MatchString(url) {
            fesLinks = append(fesLinks, url)
        }
    })
    
    return fesLinks
}

// 開催回の情報取得
func getFesInfo(fesUrl string) ([]string) {
    time.Sleep(1 * time.Second)
    fesInfo := []string{}

    doc, err := goquery.NewDocument(fesUrl)
    if err != nil {
        panic(err)
    }

    // 開催期間
    doc.Find(".ibox-content").Each(func(_ int, sel *goquery.Selection) {
        text := sel.Find("h3").Text()
        if (strings.Contains(text, "開催日")) {
            fesInfo = append(fesInfo, text)
        }
    })

    // 参加人数/参加者数/総指名数/提示年収総額
    doc.Find(".col-sm-3.col-with-border__col").Each(func(_ int, sel *goquery.Selection) {
        text := sel.Find("span.emphasis-40").Text()
        fesInfo = append(fesInfo, text)
    })
    
    return fesInfo
}

// ユーザ集取得
func getUserInfo(baseUrl string, usersPath string) ([]string) {
    time.Sleep(1 * time.Second)
    userLinks := []string{}
    nextUrl := ""
    usersUrl := baseUrl + usersPath
    fmt.Println(usersUrl)

    doc, err := goquery.NewDocument(usersUrl)
    if err != nil {
        panic(err)
    }

    // ユーザリンクと次ページリンクあれば再帰的に探す
    reg := regexp.MustCompile(`\/users\/[0-9]+\z`)
    doc.Find("a").Each(func(_ int, sel *goquery.Selection) {
        // ユーザリンク探す
        url, _ := sel.Attr("href")
        if reg.MatchString(url) {
            userLinks = append(userLinks, url)
        }

        // 次ページリンク探す
        rel, _ := sel.Attr("rel")
        if rel == "next" {
            nextUrl = url
        }
    })

    // 再帰的に探す
    if nextUrl != "" {
        nextUserLinks := getUserInfo(baseUrl, nextUrl)
        userLinks = append(userLinks, nextUserLinks...)
    }
    
    return userLinks
}

// ユーザのオファー詳細へのリンク取得
func getUserOfferLinks(userUrl string, fesNum string) ([]string) {
    time.Sleep(1 * time.Second)
    offerLinks := []string{}

    doc, err := goquery.NewDocument(userUrl)
    if err != nil {
        panic(err)
    }

    // 各回オファー取得
    doc.Find("div.ibox").Each(func(_ int, sel *goquery.Selection) {
        text := sel.Text()
        // 指定回のオファーの中でオファーのリンク取得
        if (strings.Contains(text, "第" + fesNum + "回 指名")) {
            sel.Find("a").Each(func(_ int, sel2 *goquery.Selection) {
                url, _ := sel2.Attr("href")
                if strings.Contains(url, "biddings") {
                    offerLinks = append(offerLinks, url)
                }
            })
        }
    })

    return offerLinks
}

// オファーの時間を取得
func getOfferTime(offerUrl string) (string){
    time.Sleep(1 * time.Second)

    offerTime := ""
    doc, err := goquery.NewDocument(offerUrl)
    if err != nil {
        panic(err)
    }

    doc.Find("p > span.u-m-r-25").Each(func(_ int, sel *goquery.Selection) {
        offerTime = sel.Text();
    })

    return offerTime
}

データをパース

goが最後に配列をどかんと吐いてくれるのでそれをテキストに写して
行単位のデータにする

offertimes.txt
2018.01.31 15:34
2018.01.20 13:35
2018.01.29 17:59
2018.01.31 18:54
2018.01.31 11:50
2018.01.20 15:01
2018.01.31 09:22
2018.01.31 11:56
2018.01.23 12:20
2018.01.31 14:54
2018.01.31 12:07
2018.01.31 22:57
2018.01.30 17:53
2018.01.29 15:17
2018.01.31 17:33
2018.01.31 22:53
2018.01.31 21:35
2018.01.31 11:32
.
.
.

あとは使い慣れてるphpで処理

offerTimeParse.php
<?php
    $parseData = array();

    $file = file_get_contents("./offertimes.txt");
    $offerTimeList = explode(PHP_EOL, $file);

    foreach ($offerTimeList as $key => $offerTime) {
     // 年月日がドット区切りなのでスラッシュへ変換
        $offerTime = str_replace(".", "/", $offerTime);

        $timekey = date("Y-m-d H:00", strtotime($offerTime));
        $parseData[$timekey] = isset($parseData[$timekey]) ? $parseData[$timekey] + 1 : 1;
    }

    ksort($parseData);
    foreach ($parseData as $key => $value) {
        echo $key . "," . $value . "\n";
    }
php offerTimeParse.php > time.csv

集計したデータ

ってことでデータを見ると

日時 オファー数
2018-01-17 15:00 4
2018-01-17 16:00 4
2018-01-17 17:00 6
2018-01-18 09:00 3
2018-01-18 18:00 1
2018-01-18 21:00 1
2018-01-19 06:00 1
2018-01-19 13:00 2
2018-01-19 14:00 2
2018-01-19 15:00 1
2018-01-19 16:00 1
2018-01-19 17:00 3
2018-01-19 18:00 1
2018-01-20 13:00 2
2018-01-20 14:00 1
2018-01-20 15:00 1
2018-01-21 10:00 2
2018-01-22 15:00 1
2018-01-22 16:00 3
2018-01-22 17:00 1
2018-01-23 10:00 2
2018-01-23 11:00 5
2018-01-23 12:00 5
2018-01-23 13:00 2
2018-01-23 15:00 4
2018-01-23 16:00 4
2018-01-23 17:00 2
2018-01-23 18:00 3
2018-01-23 19:00 1
2018-01-23 20:00 1
2018-01-24 15:00 1
2018-01-24 17:00 2
2018-01-24 18:00 2
2018-01-25 11:00 2
2018-01-25 12:00 3
2018-01-25 13:00 1
2018-01-25 16:00 2
2018-01-25 17:00 1
2018-01-25 18:00 7
2018-01-26 11:00 1
2018-01-26 12:00 1
2018-01-26 14:00 2
2018-01-26 15:00 5
2018-01-26 17:00 20
2018-01-26 18:00 7
2018-01-26 20:00 7
2018-01-27 03:00 1
2018-01-27 19:00 1
2018-01-28 03:00 1
2018-01-28 04:00 1
2018-01-28 11:00 1
2018-01-28 12:00 1
2018-01-29 02:00 1
2018-01-29 04:00 1
2018-01-29 11:00 2
2018-01-29 12:00 7
2018-01-29 13:00 10
2018-01-29 14:00 17
2018-01-29 15:00 13
2018-01-29 16:00 10
2018-01-29 17:00 8
2018-01-29 18:00 8
2018-01-29 19:00 10
2018-01-29 20:00 4
2018-01-29 21:00 13
2018-01-29 22:00 3
2018-01-30 00:00 1
2018-01-30 01:00 3
2018-01-30 02:00 7
2018-01-30 03:00 7
2018-01-30 04:00 4
2018-01-30 10:00 9
2018-01-30 11:00 4
2018-01-30 12:00 10
2018-01-30 13:00 16
2018-01-30 14:00 10
2018-01-30 15:00 13
2018-01-30 16:00 31
2018-01-30 17:00 20
2018-01-30 18:00 38
2018-01-30 19:00 20
2018-01-30 20:00 18
2018-01-30 21:00 19
2018-01-30 22:00 5
2018-01-30 23:00 4
2018-01-31 00:00 4
2018-01-31 01:00 4
2018-01-31 09:00 25
2018-01-31 10:00 44
2018-01-31 11:00 44
2018-01-31 12:00 20
2018-01-31 13:00 27
2018-01-31 14:00 15
2018-01-31 15:00 33
2018-01-31 16:00 29
2018-01-31 17:00 23
2018-01-31 18:00 36
2018-01-31 19:00 44
2018-01-31 20:00 50
2018-01-31 21:00 39
2018-01-31 22:00 19

結論

最終日の夜 > 最終日の朝 > 最終日の前日
といった感じですかね

開催期間が終わるまで諦めず待ちましょう

注意

スクレイピングのお作法には要注意です
ガンガンアクセスしないように

8
2
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
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?