pyppeteerでスクレイピング

Pyppeteer’s documentation — Pyppeteer 0.0.24 documentation

import asyncio
from pyppeteer import launch

async def main():
    browser = await launch()
    page = await browser.newPage()
    await page.goto('http://www.nikkei.com/markets/kabu/')

    element = await page.querySelector('span.mkc-stock_prices')
    title = await page.evaluate('(element) => element.textContent', element)

    print(title)

    await browser.close()

asyncio.get_event_loop().run_until_complete(main())

FC今治のゴール集計・先取点

!pip install lxml
!apt install fonts-ipafont-gothic

!rm /content/.cache/matplotlib/fontList.json

"""再起動"""

import time
import csv
import requests
from bs4 import BeautifulSoup

# 試合数
n = 10 + 1

with open('fcimabari_goal.tsv', 'w') as fw:

    writer = csv.writer(fw, dialect='excel-tab', lineterminator='\n')
    
    # ヘッダー
    writer.writerow(['節', 'ホーム', 'アウェイ', '時間', 'チーム名', 'スコア', '背番号', '名前'])
    
    for i in range(1, n):

        url = 'http://www.fcimabari.com/team/game/result/JFL180{0:02d}.html'.format(i)
        
        headers = {
            'User-Agent':
            'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko'
        }

        r = requests.get(url, headers=headers)

        if r.status_code == requests.codes.ok:

            soup = BeautifulSoup(r.content, 'lxml')

            home = soup.select_one('table.tableTypeResult > tr:nth-of-type(2) > td:nth-of-type(1)').get_text(strip=True)
            away = soup.select_one('table.tableTypeResult > tr:nth-of-type(2) > td:nth-of-type(3)').get_text(strip=True)

            tables = soup.select_one('table.tableType03')

            for trs in tables.select('tr'):

                temp = []

                for tds in trs.select('td'):

                    temp.append(tds.get_text(strip=True))

                if len(temp) > 0:
                    # 時間の分を削除
                    temp[0] = temp[0].strip('分')
                    

                    writer.writerow([i, home, away] + temp)
                    # print(temp)

        time.sleep(3)

import pandas as pd

df = pd.read_table('fcimabari_goal.tsv', dtype = {'節' : 'int', 'ホーム' : 'object', 'アウェイ' : 'object', '時間' : 'int', 'チーム名' : 'object', 'スコア' : 'object', '背番号' : 'object', '名前' : 'object'})

df

from google.colab import files

df.to_csv('goal_list.csv')
files.download('goal_list.csv')

# 時間・チーム名を抽出
df1 = df.iloc[:, 3:5]

# カウント用にカウントを追加しデーターを1
df1['カウント'] = 1

'''
# FC今治以外を欠測値に書き換え
df1['チーム名'] = df1['チーム名'].where(df1['チーム名'] == 'FC今治')

# 欠測値を敵チームに書き換え
df1.fillna('敵チーム', inplace=True)
df1
'''
# FC今治以外を敵チームに書き換え
df1.loc[df1['チーム名'] != 'FC今治', 'チーム名'] = '敵チーム'

df1.head()

# 時間別ゴール集計
pv = df1.pivot_table(values = 'カウント', index = '時間', columns='チーム名', aggfunc = sum, fill_value = 0)
pv.head()

# 時間1~99の0のデーターフレームを作成
pv2 = pd.DataFrame(data=[[i, 0, 0] for i in range(1,100)], columns=['時間', '敵チーム', 'FC今治'])
pv2.set_index('時間', inplace=True)
pv2

# 0とゴール集計を結合
pv3 = pv2.append(pv)

# 時間後にまとめる
grouped = pv3.groupby(level=0) 

# 最後のデータのみ(ゴール集計)
pv_goal = grouped.last() 
pv_goal

#時間別ゴールグラフ
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'IPAPGothic'

pv_goal[::-1].plot.barh(figsize=(5, 20), xticks=[0, 1, 2, 3, 4])

# ゴールタイムランキング
df.sort_values(by=['時間'], ascending=True).reset_index(drop=True).head(10)

from google.colab import files

pv_goal.to_csv('goal.csv')
files.download('goal.csv')

# 先取点
goal_time = df.loc[:, ['節','時間','チーム名']]
goal_time

# 先取点一覧
# goal_time.groupby('節')['時間'].min()

first_goal = goal_time.groupby('節').apply(lambda x: x[x['時間'] == x['時間'].min()])
first_goal

# FC今治の先取点の回数と試合数
print(first_goal['チーム名'].value_counts()['FC今治'], '/', df['節'].max())

# FC今治
df_time = df[df['チーム名']=='FC今治']['時間']

# 相手チーム
# df_time = df[df['チーム名']!='FC今治']['時間']

df_time

# 前半
df_time[df_time <= 48].count()

# 後半
df_time[df_time > 48].count()

# 全体
df_time.count()

df_time.describe()

df_count = df_time.value_counts().sort_index()
df_count

PythonでスクレイピングしてテーブルをCSVに保存

Beautifulsoupの場合

import csv

from bs4 import BeautifulSoup
import requests

url = 'http://www.example.com/'

r = requests.get(url)

if r.status_code == requests.codes.ok:

    soup = BeautifulSoup(r.content, 'html.parser')

    result = [[[td.get_text(strip=True) for td in trs.select('th, td')]
               for trs in table.select('tr')]
              for table in soup.select('table')]

    # 取得テーブル数確認
    print(len(result))

    # 取得テーブルデータ確認
    print(result)

    with open('result.csv', 'w') as fw:
        writer = csv.writer(fw, dialect='excel', lineterminator='\n')

        # 0番目のテーブルを保存(リストの番号を変更する)
        writer.writerows(result[0])

Pandasの場合

import pandas as pd

url = 'http://www.example.com/'
dfs = pd.read_html(url, header=0, index_col=0)

# 取得テーブル数確認
print(len(dfs))

# 取得テーブルデータ確認
print(dfs)

# 結合
# df = pd.concat(dfs)

# 0番目のテーブルを保存(リストの番号を変更する)
dfs[0].to_csv('result.csv')

Pandasでミニロトで遊ぶ

!pip install lxml

import pandas as pd
import io
import requests

# User-agentを設定しないとダウンロードできない
url = 'http://www.japannetbank.co.jp/lottery/co/minilotojnb.csv'

headers = {
    'User-Agent':
    'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko'
}

r = requests.get(url, headers=headers).content

df = pd.read_csv(io.StringIO(r.decode('cp932')), index_col=0)

# ソート
df.sort_index(inplace=True)

df.head()

df.dtypes

# 抽せん数字+ボーナス数字のみにする
dfl = df.iloc[:, 2:8]
dfl.head()

# DataframeからSeriesに変換
sel = dfl.melt()
sel.head()

# 度数分布
ptl = pd.pivot_table(sel, index='value', aggfunc='count')
ptl

ptl.columns = ['frequency']
ptl.sort_values('frequency', ascending=False).plot.bar()

# 抽せん数字の数字部分を削除
ser = sel.replace('抽せん数字\d$', '抽せん数字', regex=True)
ser

# 度数分布
ptr = ser.pivot_table(values='value', index='value', columns='variable', aggfunc=lambda x : len(x), fill_value = 0)
ptr['合計'] = ptr['抽せん数字'] + ptr['ボーナス数字']
ptr

ptr.sort_values('合計', ascending=False).plot.bar(y=['抽せん数字', 'ボーナス数字'], stacked=True)
  • 抽せん数字とボーナス数字の度数分布の積上棒グラフが作りたい f:id:imabari_ehime:20180424094701g:plain

f:id:imabari_ehime:20180505132938p:plain

石手川ダムのリアルタイムダム諸量一覧表でpandas練習

imabari.hateblo.jp

!pip install lxml

import pandas as pd
import requests
from urllib.parse import urljoin
from bs4 import BeautifulSoup

# 石手川ダムのリアルタイムダム諸量一覧表
url = 'http://www1.river.go.jp/cgi-bin/DspDamData.exe?ID=1368080150020&KIND=3&PAGE=0'

headers = {
    'User-Agent':
    'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko'
}

r = requests.get(url, headers=headers)

if r.status_code == requests.codes.ok:

    soup = BeautifulSoup(r.content, 'html.parser')

    # ページからデータのリンク先URLを取得
    link = urljoin(url, soup.select_one('body > center > p > a').get('href'))
    print(link)


    # データーフレームに読み込み
    df = pd.read_csv(
        link,
        skiprows=9,
        encoding='shift_jis',
        names=[
            '年月日', '時刻', '流域平均雨量', '流域平均雨量属性', '貯水量', '貯水量属性', '流入量', '流入量属性',
            '放流量', '放流量属性', '貯水率', '貯水率属性'],
        parse_dates={'日時':['年月日', '時刻']},
        index_col=['日時'],
        # 貯水率が0の時は欠損にする
        na_values=0
    )
    # df['日時']=pd.to_datetime(df['日時'], format='%Y/%m/%d %H:%M')
    # 日時と貯水率以外は削除
    df.drop(['流域平均雨量', '流域平均雨量属性', '貯水量', '貯水量属性', '流入量', '流入量属性', '放流量', '放流量属性', '貯水率属性'], axis=1, inplace=True)

# データ確認
df
df.plot()

# 欠損部チェック
df[df.isnull().any(axis=1)]

# 欠損部補完(前の値で置き換え)
dfh = df.fillna(method='ffill')
dfh.plot()

df.dtypes

type(df.index[0])

わからない

  1. 年月日と時刻を結合して日時を作成できたがdatetimeにならない
  2. 欠測、未受信の場合の欠損値の補完
    • 正常値の属性は半角スペース
    • 欠測は属性全部が$
    • 未受信の場合流域平均雨量属性は-でそれ以外の属性は#
    • 半角スペース以外の時は欠損値を上から補完
!pip install lxml

import pandas as pd
import datetime

import requests
from urllib.parse import urljoin
from bs4 import BeautifulSoup

# 早明浦ダムのリアルタイムダム諸量一覧表
url = 'http://www1.river.go.jp/cgi-bin/DspDamData.exe?ID=1368080700010&KIND=3&PAGE=0'

headers = {
    'User-Agent':
    'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko'
}

r = requests.get(url, headers=headers)

if r.status_code == requests.codes.ok:

    soup = BeautifulSoup(r.content, 'html.parser')

    # ページからデータのリンク先URLを取得
    link = urljoin(url, soup.select_one('body > center > p > a').get('href'))
    print(link)


    # データーフレームに読み込み
    df = pd.read_csv(
        link,
        skiprows=9,
        encoding='shift_jis',
        names=[
            '年月日', '時刻', '流域平均雨量', '流域平均雨量属性', '貯水量', '貯水量属性', '流入量', '流入量属性',
            '放流量', '放流量属性', '貯水率', '貯水率属性'],
        parse_dates=['年月日'],
        # na_values=0
    )


def conv(temp):
    H, M = map(int, temp.split(':'))
    return datetime.timedelta(hours=H, minutes=M)


df['日時'] = df['年月日'] + df['時刻'].apply(conv)
df.set_index('日時', inplace=True)

df.dtypes

df.head(10)

# 属性が欠測、未受信の場合は欠損値にする 
df['流域平均雨量'] = df['流域平均雨量'].where(df['流域平均雨量属性'] == ' ')
df['貯水量'] = df['貯水量'].where(df['貯水量属性'] == ' ')
df['流入量'] = df['流入量'].where(df['流入量属性'] == ' ')
df['放流量'] = df['放流量'].where(df['放流量属性'] == ' ')
df['貯水率'] = df['貯水率'].where(df['貯水率属性'] == ' ')

# 貯水率のみにする
rate_se = df.loc[:, '貯水率']
rate_se.head(10)

# 欠損値のカウント
rate_se.isnull().sum()

# 欠損部チェック
rate_se[rate_se.isnull()]

# グラフ表示
rate_se.plot()

# 欠損値補完(前の値で置き換え)
rate_correct = rate_se.fillna(method='ffill')

# 欠損値補完(後の値で置き換え)
rate_correct = rate_se.fillna(method='bfill')
rate_correct

# グラフ表示
rate_correct.plot()

# 1時間毎に変更
rato_hour = rate_correct.asfreq(freq='H')
rato_hour

# グラフ表示
rato_hour.plot()

# 欠損値を除去
rate_del = rate_se.dropna()
rate_del.plot()

Colaboratoryでスクレイピング

2021/04/18現在利用できません

news.mynavi.jp

https://colab.research.google.com/

f:id:imabari_ehime:20180416172850p:plain

import pandas as pd
import requests
from bs4 import BeautifulSoup


url = 'http://www.river.go.jp/kawabou/ipDamGaikyo.do?init=init&areaCd=88&prefCd=3801&townCd=&gamenId=01-0903&fldCtlParty=no'

headers = {
    'User-Agent':
    'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko'
}

r = requests.get(url, headers=headers)

if r.status_code == requests.codes.ok:

    soup = BeautifulSoup(r.content, 'html5lib')

    result = [[
        x.get_text(strip=True) for x in y.find_all(['th', 'td'])
    ] for y in soup.select('body > div.gaikyoCntt > table > tbody > tr ')]

    df = pd.DataFrame(data=result[1:], columns=result[0])
    df.set_index('ダム名', inplace=True)

df

Pandas

pip3じゃなくてpipだとできた

!pip install lxml
import pandas as pd

url = 'http://www.river.go.jp/kawabou/ipDamGaikyo.do?init=init&areaCd=88&prefCd=3801&townCd=&gamenId=01-0903&fldCtlParty=no'
dfs = pd.read_html(url, header=0, index_col=0)
dfs[7]

f:id:imabari_ehime:20180417085825g:plain

愛媛県動物愛護センターから迷い犬猫情報を取得しTwitterに投稿

import tempfile
from urllib.parse import urljoin

import requests
import twitter
from bs4 import BeautifulSoup

# 迷い犬猫情報にアクセスし、HTMLを取得する
url = 'http://www.pref.ehime.jp/h25123/4415/mayoi.html'
r = requests.get(url)

# エラーがないか確認する
if r.status_code == requests.codes.ok:

    # BeautifulSoupを使い迷い犬猫を取得する
    soup = BeautifulSoup(r.content, 'html.parser')

    for table in soup.select('#tmp_contents > table.datatable'):

        # タイトル取得
        caption = table.select_one('caption > h3').get_text(strip=True)

        if '犬' in caption:
            tribe = '犬'
        elif '猫' in caption:
            tribe = '猫'
        else:
            tribe = ''

        # 一行目ヘッダーのため二行目から取得する
        for row in table.select('tbody > tr')[1:]:

            # 迷い犬猫情報を取得する
            dogcat = [cell.get_text(strip=True) for cell in row.select('td')]

            # 写真のURLを取得する
            img_url = urljoin(url, row.select_one('img')['src'])

            # print(img_url)

            # 写真をダウンロードする
            res = requests.get(img_url)

            if res.status_code == requests.codes.ok:

                # 写真をファイルに一時保存する
                with tempfile.TemporaryFile() as fp:

                    fp.write(res.content)
                    fp.seek(0)
                    fp.read()

                    twit = '愛媛県動物愛護センター {0}\n\n拾得・捕獲場所:{1[0]}\n種類:{1[1]}\n毛色:{1[2]}\n性別:{1[3]}\n体格:{1[4]}\n備考:{1[5]}\n\n#愛媛県 #迷い{2}\n\n{3}'.format(
                        caption, dogcat, tribe, url)

                    # print(twit)

                    api = twitter.Api(
                        consumer_key='',
                        consumer_secret='',
                        access_token_key='',
                        access_token_secret='')

                    # Twitterに投稿する
                    status = api.PostUpdate(twit, media=fp)