スクリプトの目的・概要
タイトルの通り,
産経ニュース様から経済カテゴリのニュース情報をスクレイピングするスクリプトを書きました.
株価の時系列分析の補足情報として用いることを考えているため,
- 記事タイトル
- 記事内容
- 公開日時
- タイトルの先頭に存在する東証一部上場企業名
- タイトルの先頭に存在する東証一部上場企業の株式コード
以上5点を抽出するスクリプトを開発しました.
これらを列名として,行ごとに1つのニュース情報が格納されたpandasのdataframeを出力するスクリプトです.
スクリプトの流れ
東証一部上場企業のコード&名称リストを作成
市場名を指定することで,その市場に上場している企業のコード一覧を返す関数get_code()を作成しました.jsmパッケージを用いています.
コードは以下の通りです.
def get_code(shijo="東証1部"):
shijolist = []
sappend = shijolist.append
q = jsm.Quotes()
for fieldcode in data:
for BrandData in data[fieldcode]:
if(BrandData.market == shijo):
sappend(BrandData.ccode)
return shijolist
コードと企業名をセットにしたdataframeを作成するため,カブサポ様の上場企業一覧csvを利用しました.
csvはこちらからダウンロードができます.
記事タイトルに企業名が含まれているかを調べるため,各企業名から(株)を外して利用しました.以下のような内容が格納されています.
銘柄コード | 銘柄名 | 市場名 | 業種分類 | 単元株数 | 日経225採用銘柄 |
---|---|---|---|---|---|
1301 | 極洋 | 東証1部 | 水産・農林業 | 100 | NaN |
1332 | 日本水産 | 東証1部 | 水産・農林業 | 100 | 1 |
1333 | マルハニチロ | 東証1部 | 水産・農林業 | 100 | 1 |
1352 | ホウスイ | 東証1部 | 卸売業 | 100 | NaN |
1376 | カネコ種苗 | 東証1部 | 水産・農林業 | 100 | NaN |
一部上場企業のコードリストを引数に,csvから一部上場企業のデータだけを抽出する関数を書きました.コードは以下の通りです.
def complist(code):
pd.read_csv("stock_list.csv",header = 0,engine = "python")
compdata = compdata[compdata["銘柄コード"].isin(code)]
return compdata
ニュース記事のスクレイピング
スクレイピングの流れは以下の通りです.
- Beautifulsoupを用いて産経新聞の経済記事一覧のlxmlを取得する.
- 得られたlxmlの中から,記事のタイトル,公開日時を取得
- 各記事の見出しに含まれるリンクから記事の本文ページのlxmlを取得し,記事内容を取得する.
以下のgetscrape()関数によって,pandasの記事のタイトル,内容,公開日時が格納されたdataframeが作成されます.
引数のyearについてですが,記事の公開日時情報に年が含まれていないため用意しています.現在時刻などをとって年の情報を取得すればよいのですが…
def scrape(year="2018"):
dflist = []
pageSize = 1
for i in range(1,pageSize+1):
url = "https://www.sankei.com/economy/newslist/business-n" + str(i) + ".html"
bshtml = gethtml(url)
news = getnewses(bshtml,year)
dflist.append(news)
newsdata = dflist[0]
for i in range(pageSize):
newsdata = pd.concat([newsdata,dflist[i]])
return newsdata
getscrape()関数で参照しているgethtml(url),getnewses(bshtml,year)について説明します.
まず,gethtml(url)に関してですが,これは引数のurlをスクレイピングして,Beautifulsoupを用いてlxmlデータを取得する関数です.
コードは以下の通りです.念のため,sleep関数で短時間に多くのアクセスをしないようにしてあります.
def gethtml(url): #urlのhtml形式を取得 time.sleep(3) html = requests.get(url) bshtml = bs4.BeautifulSoup(html.text,"lxml") return bshtml
続いてgetnewses(bshtml,year)関数について説明します.この関数を実行することで,gethtml関数で取得したlxmlの中から各記事のタイトル,記事内容および公開日時を抽出しています.コードは以下の通りです.
def getnewses(bshtml,year):
newsdata = pd.DataFrame(index = [],columns = ["title","when","contents"])
section = bshtml.find_all("div",class_= "entry_content")
for elem1 in section:
news = []
a = elem1.select("a")
test = a[0].get("href")
contents_url = test
contents = gethtml(contents_url)
c_section = contents.find_all("div",class_="post_content")
arts = c_section[0].find_all("p")
tbody = ""
for art in arts:
if(art.string):
tbody += art.string
span = elem1.find("div",class_ ="entry_meta")
try:
when_raw = str(span.select("time")[0].string)
timelist = when_raw.split(" ")
month,day = timelist[0].split(".")
hour,minute = timelist[1].split(":")
when = pd.datetime(int(year),int(month),int(day),int(hour))
except:
print("no time data")
continue
news.append(a[0].string)
news.append(when)
news.append(tbody)
news = pd.Series(news,index = newsdata.columns)
newsdata = newsdata.append(news,ignore_index = True)
return newsdata
企業に関するニュースの抽出&企業の名称とコードの追加
企業名がタイトルに含まれるニュースデータを抽出して,その企業名と株式コードを新たな列項目として付加したdataframeを返す関数matchtitle(newsdata,compdata)を作成しました.
ここで,newsdata,compdataはそれぞれgetscrape()関数で抽出したニュースデータとget_code()関数で取得した企業データです.
def matchtitle(newsdata,compdata):
"""一部上場している企業一覧とタイトル一覧から,企業名が入っているタイトルを抽出"""
selectnews = pd.DataFrame(index = [],columns = ["title","when","contents","company","code"])
for compname in compdata["銘柄名"]:
for newsname in newsdata["title"]:
if(compname == newsname[:len(compname)]):
appenddata = newsdata[newsdata["title"] == newsname]
appenddata["company"] = compname
appenddata["code"] = int(compdata.loc[compdata.loc[:,"銘柄名"] == compname,"銘柄コード"])
selectnews = selectnews.append(appenddata,ignore_index = True)
selectnews[["code"]] = selectnews[["code"]].astype(int)
return selectnews
なぜかニュースが重複して抽出されてしまうため,重複しているニュースを削除する関数を作りました.
def delete_duplicatedNews(newsdata):
new_newsdata = pd.DataFrame(index = [],columns = newsdata.columns)
for no_old,row_old in newsdata.iterrows():
isAdd = True
for no_new ,row_new in new_newsdata.iterrows():
if(row_old["wakachi"] == row_new["wakachi"]):
isAdd = False
if(isAdd):
new_newsdata = new_newsdata.append(row_old,ignore_index = True)
return new_newsdata
抽出結果
以下のようなdataframeが抽出できます.
title | when | contents | company | code |
---|---|---|---|---|
カルビーが47都道府県の地元味ポテチ東京は「てんぷら味」 | 2018/10/25 16:00 | カルビーは25日、江戸前天ぷらの味わいを再現した「ポテトチップス\u3000てんぷら味」を29日に、… | カルビー | 2229 |
日本ハムが早期退職200人募集社員の1割超 | 2018/10/31 19:00 | 日本ハムが31日発表した平成30年9月中間連結決算は、売上高が前年同期比1・4%減の618… | 日本ハム | 2282 |
キッコーマンの茂木友三郎名誉会長が文化功労者に | 2018/10/26 16:00 | 平成30年の文化功労者に選ばれたキッコーマンの茂木友三郎名誉会長は26日、「日本の『食』に… | キッコーマン | 2801 |
キッコーマン、ライブ型レストランを東京・有楽町に新設 | 2018/10/25 13:00 | キッコーマンは25日、有名シェフによる料理の実演などが楽しめるライブ型の新スタイルレストラ… | キッコーマン | 2801 |
東レのキャンペーンガールにタレントの松田紗和さん | 2018/10/30 14:00 | 東レは30日、平成31年のキャンペーンガールに、タレントの松田紗和さん(20)を起用すると… | 東レ | 3402 |
今後の方針
文書データを時系列分析の補助情報として扱うためには,文書データを数値化する必要があります.文書を数値化する手法としては,細かい説明は割愛しますが,
- Latent Semantic Analysis(LSA)
- Latent Dirichlet Allocation(LDA)
- Doc2Vec
といった手法があります.
どの手法を用いるにしても,文書を単語あるいは品詞ごとに区切る必要があります.英語の場合は空白ですでに区切られているため,あとは活用形を原形に戻すかどうか(ステミングと言います)などを考えればよいのですが,日本語の場合は適切な区切り位置で文を区切る必要があります.これを自動的に行うための解析手法は形態素解析と呼ばれ,すでに様々な形態素解析エンジンが開発されています.
今後の方針として,
- 文書データを形態素解析エンジンを用いて分かち書きで表現
- 得られた分かち書きデータを,Doc2Vecを用いて数値化する
といった形でデータの加工をやっていきます