前の記事のおさらいと本記事でやること
前の記事ではすべての検索結果のページの要素の取得方法を解説しました。
この記事では、取得した結果を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を利用したメルカリのスクレイピング
ヤフオク!をスクレイピングして、落札結果をBigQueryに保存