Pythonスクレイピングの基本と実践 データサイエンティストのためのWebデータ収集術

スクレイピングの基本を勉強するにはいいかも

今まで見た本の中で説明が長いのと表になっていないので見づらい

book.impress.co.jp

Pythonスクレイピングの基本と実践 データサイエンティストのためのWebデータ収集術 (impress top gear)

Pythonスクレイピングの基本と実践 データサイエンティストのためのWebデータ収集術 (impress top gear)

github.com

camelotでPDFの表からEXCELにコマンド変換(CSV・XLSX)

Camelot: PDF Table Extraction for Humans — Camelot 0.8.2 documentation

インストール

Installation of dependencies — Camelot 0.8.2 documentation

apt install python3-tk ghostscript
pip install camelot-py[cv]
# PATH追加
export PATH=$PATH:/home/imabari/.local/bin

# 変換
camelot -p 2-end -o black.xlsx -f excel -split lattice 180928.pdf

# 表示
camelot -p 2 lattice -plot joint 180928.pdf

# 線が短い表の場合 -scale 40 を付ける
camelot -p all -o black.xlsx -f excel -split lattice -scale 40 180928.pdf

# テキストの改行スペース削除 -strip ' \n'
camelot -p all -o black.xlsx -f excel -split -strip ' \n' lattice 180928.pdf

# テキストコピー
camelot -p all -o data.csv -f csv -strip ' .\n' -split lattice -scale 40 -copy v data.pdf

コマンドライン

Command-Line Interface — Camelot 0.8.2 documentation

Usage: camelot [OPTIONS] COMMAND [ARGS]...

  Camelot: PDF Table Extraction for Humans

Options:
  --version                       Show the version and exit.
  -q, --quiet TEXT                Suppress logs and warnings.
  -p, --pages TEXT                Comma-separated page numbers. Example: 1,3,4
                                  or 1,4-end.
  -pw, --password TEXT            Password for decryption.
  -o, --output TEXT               Output file path.
  -f, --format [csv|json|excel|html]
                                  Output file format.
  -z, --zip                       Create ZIP archive.
  -split, --split_text            Split text that spans across multiple cells.
  -flag, --flag_size              Flag text based on font size. Useful to
                                  detect super/subscripts.
  -strip, --strip_text TEXT       Characters that should be stripped from a
                                  string before assigning it to a cell.
  -M, --margins <FLOAT FLOAT FLOAT>...
                                  PDFMiner char_margin, line_margin and
                                  word_margin.
  --help                          Show this message and exit.

Commands:
  lattice  Use lines between text to parse the table.
  stream   Use spaces between text to parse the table.

camelotでPDFの表からEXCELに変換(CSV・TSV・XLSX)

厚生労働省ブラック企業リストをTSV変換

imabari.hateblo.jp

前回tabulaのは失敗するのでcamelotで再挑戦

Camelot: PDF Table Extraction for Humans — Camelot 0.7.3 documentation

厚生労働省長時間労働削減に向けた取り組みから www.mhlw.go.jp

労働基準関係法令違反に係る公表事案をダウンロード https://www.mhlw.go.jp/kinkyu/dl/180928.pdf

表の部分しか取れないので労働局名と最新更新日がありません

apt install ghostscript
pip install camelot-py[cv]
import re
import pandas as pd
import matplotlib.pyplot as plt

import camelot

# 2ページ目から最終頁まで、セパレーター優先、改行除去
tables = camelot.read_pdf('180928.pdf', pages='2-end', split_text=True, strip_text='\n')

dfs = []

# dataframeに変換、ヘッダー部削除、ヘッダー追加
for table in tables:
    df = table.df
    df.drop(0, inplace=True)
    df.columns = ['企業・事業場名称', '所在地', '公表日', '違反法条', '事案概要', 'その他参考事項']
    dfs.append(df)

# ページ結合
df_black = pd.concat(dfs)

# カンマを追加
def ihan_conv(temp):
    result = re.sub('条(の\d{1,3})?', lambda m: m.group(0) + ', ', temp).rstrip(', ')
    return result

def sonota_conv(temp):
    result = re.sub('H\d{1,2}\.\d{1,2}\.\d{1,2}', lambda m: ', ' + m.group(0), temp).lstrip(', ')
    return result

df_black['違反法条'] = df_black['違反法条'].apply(ihan_conv)
df_black['その他参考事項'] = df_black['その他参考事項'].apply(sonota_conv)

# CSVファイルへ出力
df_black.to_csv('black.csv')

# TSVファイルへ出力
df_black.to_csv('black.tsv', sep='\t' )

# EXCELファイルへ出力
with pd.ExcelWriter('black.xlsx') as writer:
    df_black.to_excel(writer, sheet_name='sheet1')

camelotは線できちんと囲まれているものは大丈夫そう。 2行で1つのセルのようなexcelで1つは線ありもう一つは文字オーバーして線が消えていると隣や上にくっついてしまうので難しい。

点線

needtec.sakura.ne.jp

愛媛県のインフルエンザ患者報告数をスクレイピング

colspanがめんどくさいので直取り

import requests
from bs4 import BeautifulSoup

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


def get_table(url):

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

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

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

        tables = soup.find_all('table', class_='datatable')

        return tables


def scraping(tables, num, area):

    # 今週
    this_week = float(tables[4].select_one(
        'tr:nth-of-type(3) > td:nth-of-type({})'.format(num + 1)).get_text(
            strip=True))

    # 前週
    last_week = float(tables[4].select_one(
        'tr:nth-of-type(5) > td:nth-of-type({})'.format(num)).get_text(
            strip=True))

    # 警報・注意報
    alarm = tables[4].select_one(
        'tr:nth-of-type(4) > td:nth-of-type({})'.format(num - 1)).get_text(
            strip=True)

    # 前週差
    comp_week = this_week - last_week

    if comp_week > 0:

        sign = '↑'
    elif comp_week < 0:
        sign = '↓'
    else:
        sign = ''

    # インフルエンザ患者報告数

    this_count = tables[0].select_one(
        'tr:nth-of-type(2) > td:nth-of-type({})'.format(num)).get_text(
            strip=True)

    # 迅速検査結果

    temp = []

    inful_a = tables[0].select_one(
        'tr:nth-of-type(4) > td:nth-of-type({})'.format(num + 1)).get_text(
            strip=True)
    inful_b = tables[0].select_one(
        'tr:nth-of-type(5) > td:nth-of-type({})'.format(num)).get_text(
            strip=True)
    inful_n = tables[0].select_one(
        'tr:nth-of-type(6) > td:nth-of-type({})'.format(num)).get_text(
            strip=True)

    if inful_a:
        temp.append('A型:{}人'.format(inful_a))

    if inful_b:
        temp.append('B型:{}人'.format(inful_b))

    if inful_n:
        temp.append('不明:{}人'.format(inful_n))

    inful_kata = '、'.join(temp)

    result = '({0}){1}\n定点当たり:{2:.1f}人{3}(前週比:{4:+.1f}人)\n患者報告数:{5}人({6})'.format(
        area, alarm, this_week, sign, comp_week, this_count, inful_kata)

    return result


# 02:愛媛県
# 03:四国中央
# 04:西条
# 05:今治
# 06:松山市
# 07:中予
# 08:八幡浜
# 09:宇和島

if __name__ == '__main__':

    tables = get_table(
        'https://www.pref.ehime.jp/h25115/kanjyo/topics/influ1819/tb_flu1819.html'
    )

    tables.extend(
        get_table(
            'https://www.pref.ehime.jp/h25115/kanjyo/topics/influ1819/index1819.html'
        ))

    # print(len(tables))

    if len(tables) == 5:

        temp = []

        temp.append('インフルエンザ患者報告数')

        temp.append(scraping(tables, 2, '愛媛県'))

        temp.append(scraping(tables, 5, '今治'))

        temp.append('https://www.pref.ehime.jp/h25115/kanjyo/index.html')

        result = '\n\n'.join(temp)

        print(result)
続きを読む

今治市の歯科医院を探す

import csv
import time
from urllib.parse import parse_qs, urljoin, urlparse

import requests
from bs4 import BeautifulSoup

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


def scraping(url):

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

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

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

        result = [
            td.get_text(strip=True)
            for td in soup.select('table#hor-zebra > tbody > tr > td')
        ]

        # GooglemapからURL取得
        gmap = soup.select_one(
            'table#jyouhou-table > tbody > tr > td > div > iframe').get('src')

        # URLからquery取得
        temp = parse_qs(urlparse(gmap).query)

        # queryから緯度・経度取得
        lon, lat = temp['q'][0].replace('loc:', '').split(',')

        return ([lon, lat] + result)


if __name__ == '__main__':

    # 愛媛県歯科医師会より今治市の歯科医院を探す
    url = 'https://www.ehimeda.or.jp/doctor/sdental_u/resultlist.php?selADD1=02&SelCount=5'

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

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

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

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

            # ヘッダーをスキップするため1からスタート
            for trs in soup.select('table#list-table > tbody > tr')[1:]:

                result = [td.get_text(strip=True) for td in trs.select('td')]

                link = urljoin(url, trs.select_one('a').get('href'))

                temp = scraping(link)

                writer.writerow(result + temp)

                time.sleep(1)