スクレイピングの基本

4.取得した結果をcsvに保存する

前の記事のおさらいと本記事でやること

前の記事ではすべての検索結果のページの要素の取得方法を解説しました。

この記事では、取得した結果をCSVに保存し、また検索キーワードの入力をコンソール上で行う応用も解説します。

スクレイピングした結果をCSVに保存する

前の記事までで作ったmain()関数にCSVを保存するコードを追記しましょう。

def main():
    url = "https://townwork.net/joSrchRsltList/?fw=プログラミング"
    columns = ["rid", "company", "title", "salary", "access", "term", "timelimit"]
    df = pandas.DataFrame(columns=columns)
    while True:
        df, url = get_items(url, df, columns)
        time.sleep(5)
        if url is None:
            break
    df.to_csv('scraping.csv', index=False, encoding='utf_8_sig')

データフレームのCSVへの出力は53行目に追記したPandasのto_csv()メソッドのみで完了します。

第一引数の出力するファイル名(ファイルパス)以外の引数の指定は任意です。ただし、スクレイピングデータをCSVとして出力する場合は、第二引数に行名の非出力、第三引数に文字コードを指定すると良いでしょう。

index=Falseを指定しないと0,1,2...という行番号まで出力されてしまいます。CSVとしては出力しないのが通常です。

文字コードの指定をしないと、エクセルなどでCSVを開く際には日本語が文字化けしてしまいます。utf_8_sig(BOM有りUTF-8)と指定すると文字化けを防ぐことができます。

Pandasのデータフレーム構造のCSVへの書き込みは一行で済み非常に楽であるため、スクレイピング時には辞書やリストではなくデータフレームを使うのが便利です。

例えば辞書をCSVに書き込もうとすると

import csv

with open('names.csv', 'w', newline='') as csvfile:
    fieldnames = ['first_name', 'last_name']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames)

    writer.writeheader()
    #実践的には以下はfor文でループさせるのでwriter.writerowは1行で済む
    writer.writerow({'first_name': 'Baked', 'last_name': 'Beans'})
    writer.writerow({'first_name': 'Lovely', 'last_name': 'Spam'})
    writer.writerow({'first_name': 'Wonderful', 'last_name': 'Spam'})

のように、書き込みたいファイルをwith文で開いたうえで、一行一行をfor文などを利用して書き込む処理を書く必要があり、面倒になってしまいます。

検索キーワードをコマンドラインで指定する

これだけでもスクレイピングを行うプログラムとしては完成です。しかし、スクレイピングするURLは直接コードをいじって変更しなければならないため、やや面倒です。

ジャンルで絞り込みたい場合やキーワード指定せず全件検索したいようなときは、直接URLを変更するしかありませんが、検索キーワード指定をしたい場合は、コマンドライン上で指定させるコードを書くこともできます。

python scraping.py プログラミング

などとコマンドライン(コマンドプロンプト)上で入力したとき、実行ファイル名(上の例だとscraping.py)の後ろの引数(上の例だとプログラミング)が検索キーワードとして認識され、スクレイピングを行うコードを書いてみましょう。

import requests
from bs4 import BeautifulSoup
import pandas
import time
import sys

冒頭のimport文にシステム関連の標準モジュールであるsysを追記したのち、main()関数を変更します。

def main():
    args = sys.argv
    query = args[1]
    url = "https://townwork.net/joSrchRsltList/?fw={}".format(query)
    columns = ["rid", "company", "title", "salary", "access", "term", "timelimit"]
    df = pandas.DataFrame(columns=columns)

    while True:
        df, url = get_items(url, df, columns)
        time.sleep(5)
        if url is None:
            break

    df.to_csv('{}.csv'.format(query), index=False, encoding='utf_8_sig')
    print("CSVへの書き込みが完了しました")

46~47行目で入力された引数を取得しています。

48行目でURLの代入文を変更しています。ここでは、従来「プログラミング」という文字列を設定していたURLの検索キーワード部分を、コンソール上で入力された引数とし、format()により代入しています。この処理により、引数として指定したワードを検索キーワードにすることができます。

58行目では、ついでに出力するCSVのファイル名を{検索キーワード}.csvというコードに変えています。

sysモジュールで実際に使うことが多いのは、引数の情報を取得できるsys.argvとプログラムを終了させるsys.exit()の二つのみです。 

sys.argv

args = sys.argv

このとき、変数argsの中には

>>> print(args)
['C:/ファイルパス/scraping.py', 'プログラミング']

のように、実行ファイル名と引数がリストとして格納されています。

今回解説しているコードでは引数のみを取り出したいので

query = args[1]

と指定し、リストの二番目の要素=引数を変数queryに代入しています。

sys.exit()

import sys

if len(sys.argv) < 2:
    print('引数の指定がありません。プログラムを終了します。')
    sys.exit()

sys.exit()はプログラムを終了させるメソッドです。sys.argvと組み合わせて、引数の入力数に応じてプログラムを終了させるコードを書くこともできます。

コードのまとめ

import requests
from bs4 import BeautifulSoup
import pandas
import time
import sys

def get_items(url, df, columns):
    res = requests.get(url)
    res_text = res.text
    soup = BeautifulSoup(res_text, "html.parser")
    ret = soup.find_all("div", class_="job-lst-main-cassette-wrap")
    ret.pop(0) # 最初と最後の要素は広告なので削除
    ret.pop(-1)
    for item in ret:
        rid = item.find("a", class_="job-lst-main-box-inner").get("href")
        company = item.find("h3", class_="job-lst-main-ttl-txt").text.strip()
        title = item.find("p", class_="job-lst-main-txt-lnk").text.strip()
        trs = item.select("tr.job-main-tbl-inner > td > p")
        salary = trs[0].text
        access = trs[1].text
        term = trs[2].text
        try:
            timelimit = item.select_one("p.job-lst-main-period-limit > span").text
        except AttributeError:
            timelimit = "不明"

        print("{0}番目の情報({1}):{2}をDataFrameに追加します...".format(len(df)+1, rid, title))
        print("会社名:{0} タイトル:{1} 給与:{2} 交通:{3} 勤務時間:{4} 掲載終了日時:{5}" \
              .format(company, title, salary, access, term, timelimit))
        se = pandas.Series([rid, company, title, salary, access, term, timelimit], columns)
        df = df.append(se, ignore_index=True)
    url = get_nextpage(soup)
    return df, url

def get_nextpage(soup):
    try:
        url = soup.select_one("div.pager-next-btn > div.btn-wrap > a").get("href")
    except AttributeError:
        print("最後のページです")
        return
    url = "https://townwork.net" + url
    print(url)
    return url

def main():
    args = sys.argv
    query = args[1]
    print(args)
    url = "https://townwork.net/joSrchRsltList/?fw={}".format(query)
    columns = ["rid", "company", "title", "salary", "access", "term", "timelimit"]
    df = pandas.DataFrame(columns=columns)

    while True:
        df, url = get_items(url, df, columns)
        time.sleep(5)
        if url is None:
            break

    df.to_csv('{}.csv'.format(query), index=False, encoding='utf_8_sig')
    print("CSVへの書き込みが完了しました")

if __name__ == '__main__':
    main()

これで、検索キーワードを引数に指定して実行することで、タウンページの検索結果のすべてを取得しCSVに保存するプログラムが完成しました。

さらに実用的なプログラムへの応用

スクレイピングは、価格の分析などでも利用できますので、興味がある方は、ぜひnoteの応用記事をご覧ください。

Seleniumを利用したメルカリのスクレイピング

メルカリの検索結果をスクレイピングするプログラミング解説|みゃふのPythonプログラミング~応用編~|note
メルカリで何か探しているときに、目当ての出品物が見つかるまでひたすら検索結果を見るのは大変だなぁ…と思ったことはありませんか? カテゴリによっては結果一覧が何ページにも及ぶ場合があるので、お目当ての出品物を見つけるのはとても骨の折れる作業ですよね… でも、もしあなたがPythonの基本的な知識をご存じなら、簡単なプロ...

ヤフオク!をスクレイピングして、落札結果をBigQueryに保存

ヤフオク!の落札結果をスクレイピングして、BigQueryで分析するプログラミング解説|みゃふのPythonプログラミング~応用編~|note
Yahoo!オークション(ヤフオク!)で落札や出品する場合、最適な金額がいくらなのか気になりませんか? 今回は、Yahoo!オークション(ヤフオク!)の落札金額を取得して、最適な金額を検討するためのデータ取得方法からデータ分析の方法までをご紹介します。 スクレイピングに初めて挑戦する方はぜひ以下の記事をご覧くださ...
タイトルとURLをコピーしました