はじめに
スクレイピングしてみたいなぁと思いつつクローラー/Webスクレイピング Advent Calendar 2017に参加しました。ただ仕事や忘年会の準備をしていると気づけば当日のお昼...
ということでスクレイピングをやったことないですが、3時間でなんとかしていきたいと思います...笑
まず最初にやること
何はともあれやってみるしかないんでスクレイピング 初心者
でググってみるところからスタート
とにかくコードが載っているページのやつを試しまくります
使用した環境が以下の環境(docker)です
FROM python:3
WORKDIR /usr/src/app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
CMD [ "python", "./train2.py" ]
# docker build -t tmp-train .
# docker run -v `pwd`:/usr/src/app tmp-train
Lv1
ひとまず以下のページからコピペしまくる
【初心者のためのPython入門】Webスクレイピング〜Webページから任意のデータを抽出する〜
# python3
# bs-select.py
from bs4 import BeautifulSoup
import urllib.request as req
url = "http://su-gi-rx.com/2017/07/16/python_4/"
# urlopen()でデータを取得
res = req.urlopen(url)
# BeautifulSoup()で解析
soup = BeautifulSoup(res, 'html.parser')
# 任意のデータを抽出
title1 = soup.find("h1").string
print("title = ", title1)
p_list = soup.find_all("p")
print("text = ", p_list)
なんかHTML取れた!
意外と早くできそう(15分経過)!
Lv2
ページは取れたので次にDBへ入れてみたいと思います。DBの準備も簡単に終わらせるためにsqliteで行います。
(ここでDBの情報を残すためにdockerをマウントさせました。docker run -v `pwd`:/usr/src/app tmp-train
)
というわけでsqlite python
でググると以下のページを発見
ひとまずコピペで動いたのでそれをさっきのコードに連結
dockerfileの変更と型変換関連で時間をとってしまい30分近く使ってしまった...
# -*- coding: utf-8 -*-
from bs4 import BeautifulSoup
import urllib.request as req
import sqlite3
url = "http://su-gi-rx.com/2017/07/16/python_4/"
dbname = 'database.db'
conn = sqlite3.connect(dbname)
c = conn.cursor()
table_name = 'test'
# train1の部分
def get_html():
# urlopen()でデータを取得
res = req.urlopen(url)
# BeautifulSoup()で解析
soup = BeautifulSoup(res, 'html.parser')
# 任意のデータを抽出
title1 = soup.find("h1").string
p_list = soup.find_all("p")
return [(str(title1), str(p_list))]
# train2の部分
def create_table(tname):
# executeメソッドでSQL文を実行する
create_table = '''create table if NOT EXISTS {} (title varchar(64), p_list varchar(32))'''.format(tname)
c.execute(create_table)
def insert_data(tname, data):
# 一度に複数のSQL文を実行したいときは,タプルのリストを作成した上で
# executemanyメソッドを実行する
insert_sql = 'insert into {0} (title, p_list) values (?,?)'.format(tname)
c.executemany(insert_sql, data)
conn.commit()
if __name__ == '__main__':
create_table(table_name)
test = get_html()
insert_data(table_name, test)
select_sql = 'select * from {0}'.format(table_name)
for row in c.execute(select_sql):
print(row)
conn.close()
Lv3
データが保存できるようになったので次は実際のページからデータをとってくるところを行いたいと思います。。みるページは以下のサイトにします
警視庁管内不審者情報
正規表現などで手こずってしまって結構時間がかかりました...(1時間くらい)
# -*- coding: utf-8 -*-
from bs4 import BeautifulSoup
import urllib.request as req
import sqlite3
import re
url = "http://www.keishicho.metro.tokyo.jp/kurashi/higai/kodomo/fushin/20_fushin.html"
dbname = 'database.db'
conn = sqlite3.connect(dbname)
c = conn.cursor()
table_name = 'test'
# train1の部分
def get_html():
# urlopen()でデータを取得
res = req.urlopen(url)
# BeautifulSoup()で解析
soup = BeautifulSoup(res, 'html.parser')
# 任意のデータを抽出
main = soup.find("div", id="main").find("div", class_="main_inner").find("div", class_="t-box2")
news_list = main.find("table").find_all("tr")
res = []
for news in news_list:
res += get_res(news)
return res
# 記事から情報を抜き出すところ
def get_res(news):
place = news.find('th').string
main = news.find('td').find('p').text
main_list = re.match('(.*警察署)([0-9]{1,2})月([0-9]{1,2})日((.)曜)、(.*)ころ、(.*)不審者の特徴:(.*)', main).groups()
police = main_list[0]
date = main_list[1] + main_list[2] + main_list[3]
time = main_list[4]
body = main_list[5]
feature = main_list[6]
return [(place, police, date, time, body, feature)]
def drop_table(tname):
# executeメソッドでSQL文を実行する
create_table = '''drop table if EXISTS {} '''.format(tname)
c.execute(create_table)
# train2の部分
def create_table(tname):
# executeメソッドでSQL文を実行する(型はひとまずcharで入れてます...)
create_table = '''
create table if NOT EXISTS {} (
place varchar(64),
police varchar(32),
date varchar(64),
time varchar(64),
body varchar(64),
feature varchar(64))'''.format(tname)
c.execute(create_table)
def insert_data(tname, data):
# 一度に複数のSQL文を実行したいときは,タプルのリストを作成した上で
# executemanyメソッドを実行する
insert_sql = 'insert into {0} (place, police, date, time, body, feature) values (?,?,?,?,?,?)'.format(tname)
c.executemany(insert_sql, data)
conn.commit()
def select_all(tname):
select_sql = 'select * from {0}'.format(table_name)
res = ""
for row in c.execute(select_sql):
res += str(row)
return res
if __name__ == '__main__':
drop_table(table_name)
create_table(table_name)
test = get_html()
insert_data(table_name, test)
print(select_all(table_name))
conn.close()
Lv4
12月文の情報が取れたということで月をどんどん変えてとっていきたいと思います。具体的には以下のURLの**
を変えて情報をとってきます
http://www.keishicho.metro.tokyo.jp/kurashi/higai/kodomo/fushin/**_fushin.html
数ページみるといろいろ問題が発生
- 小文字と大文字の違い(
不審者の特徴
の後の:) - bodyの中でhtmlを使っている場合があった...(例外処理で回避、htmlが含まれる場合データを取ることを断念)
かなりコードが汚くなってきたのでこのコードは読み飛ばしちゃっていいです
# -*- coding: utf-8 -*-
from bs4 import BeautifulSoup
import urllib.request as req
import sqlite3
import re
url = "http://www.keishicho.metro.tokyo.jp/kurashi/higai/kodomo/fushin/"
dbname = 'database.db'
conn = sqlite3.connect(dbname)
c = conn.cursor()
table_name = 'test'
# train1の部分
def get_html(url):
try:
# urlopen()でデータを取得
res = req.urlopen(url)
except:
# ページがない時など
return False
# BeautifulSoup()で解析
soup = BeautifulSoup(res, 'html.parser')
# 任意のデータを抽出
main = soup.find("div", id="main")
news_list = main.find("div", class_="main_inner").find("div", class_="t-box2").find("table").find_all("tr")
res = []
for news in news_list:
tmp = get_res(news)
if not tmp:
continue
res += tmp
return res
# 記事から情報を抜き出すところ
def get_res(news):
place = news.find('th').string
main = news.find('td').find('p').text
try:
main_list = re.match('(.*警察署)([0-9]{1,2})月([0-9]{1,2})日((.)曜)、(.*)ころ、(.*)不審者の特徴[:|:](.*)', main).groups()
except:
# 上記の表現で書かれていない場合
return False
police = main_list[0]
date = main_list[1] + main_list[2] + main_list[3]
time = main_list[4]
body = main_list[5]
feature = main_list[6]
return [(place, police, date, time, body, feature)]
def drop_table(tname):
# executeメソッドでSQL文を実行する
create_table = '''drop table if EXISTS {} '''.format(tname)
c.execute(create_table)
# train2の部分
def create_table(tname):
# executeメソッドでSQL文を実行する(型はひとまずcharで入れてます...)
create_table = '''
create table if NOT EXISTS {} (
place varchar(64),
police varchar(32),
date varchar(64),
time varchar(64),
body varchar(64),
feature varchar(64))'''.format(tname)
c.execute(create_table)
def insert_data(tname, data):
# 一度に複数のSQL文を実行したいときは,タプルのリストを作成した上で
# executemanyメソッドを実行する
insert_sql = 'insert into {0} (place, police, date, time, body, feature) values (?,?,?,?,?,?)'.format(tname)
c.executemany(insert_sql, data)
conn.commit()
def select_all(tname):
select_sql = 'select * from {0}'.format(table_name)
res = ""
for row in c.execute(select_sql):
res += str(row)
return res
if __name__ == '__main__':
drop_table(table_name)
create_table(table_name)
for i in range(1,30):
_url = url + str(i) + '_fushin.html'
test = get_html(_url)
if not test:
continue
insert_data(table_name, test)
print(select_all(table_name))
conn.close()
終わりに
途中から測るのがめんどくさくなったので3時間以内でどこまで行ったのか微妙です...笑。途中で感じたのはスクレイピング自体は割とすぐできるけどそれ使って何やるのかを考えるのが難しい気がしました。(欲しい情報にはだいたいAPIが実装されていたりしますし)時間があれば本文、特徴を形態素分析したりしてみたかったですね。最後に得られた不審者情報(数ヶ月分)の簡単なサマリーを載せておきます。
平日の方が不審者が多く報告されているみたいですね
属性 | 値 |
---|---|
レコード数 | 10/23~12/19 51(データ少ない...) |
場所別順位 | 足立区11件 > 葛飾区6件 > 大田区・豊島区5件 |
曜日別順位 | 月14 > 火11 > 木8 > 金6 > 水5 > 土4 > 日3 |