アンカータグに入ったJavaScriptスキームのサイトにScrapyで挑む
Scrapyしにくいサイト
ここです。
jinzai.hellowork.mhlw.go.jp
ちょっとアクセスすると判るのですが、href
にJavaScript:
に入れまくっている(JavaScriptスキーム)平成初期の臭いが漂うサイトですね。
今回は、Scrapyを使ってスクレイピングしてみます。
Scrapyの下準備
Scrapyの説明も含めて、こちらをごらんください。
qiita.com
非常に解りやすいので、オススメです。
デベロッパーツールはともだち
Scrapyは何でもいけますが、リクエスト内容がわからないと画面遷移ができないので調査します。
サイトにアクセスしてから、デベロッパーツールを開きます。
※POSTMANなどツールもありますが、今回は使わないでいきます。
ご存知だと思いますが [表示] - [開発 / 管理] - [デベロッパーツール]でデベロッパーツールで開きます。(ショートカットキーでもOK)
そしてNetworkに切り替えます。
start_urlsのURLを探索する
調査したいリンクを押下します。
そして、Networkで表示されたリソースの一番上を押下して情報を所得したら、一番下までスクロールします。
Form Dataが必要な情報になります。
Form Dataにトークンらしきものが無ければ、このForm Dataのデータを使ってGet Methodにしてアクセスしてみます。
Get Methodにできればコードが減るので、このURLをとりあえず覚えておきます。
※Post Methodのみだったら、ここからコードを書くことになります。
さらにもっとできないか探索します。
調査を続けます、次に都道府県の東京都をチェックしてから検索を押下します。
同じようにForm Dataを見て、トークンらしきものが無ければForm Dataのデータを使ってGet Methodにしてアクセスをします。
そうすると、アクセスできたので、このURLもとりあえず覚えておきます。
トークンがある
検索結果を全て取得したいので、次ページのページネーションを確認します。
画面の下の2
を押下します。
そうすると、ついにトークンらしきものが現れました。
ここからは、Post Methodの方が良さそうなので、さきほど覚えたURLをScrapyのstart_urls
としてコードを書きます。
コードを書く
会社名を取得するので、items.py はcompany_name
と書く。
# -*- coding: utf-8 -*- # Define here the models for your scraped items # # See documentation in: # https://docs.scrapy.org/en/latest/topics/items.html import scrapy class Post(scrapy.Item): company_name = scrapy.Field()
spiders/hellowork.py はこんな感じです。
# -*- coding: utf-8 -*- import re import scrapy from .. items import Post class HelloworkSpider(scrapy.Spider): name = 'Hellowork' allowed_domains = ['jinzai.hellowork.mhlw.go.jp'] start_urls = ['https://jinzai.hellowork.mhlw.go.jp/JinzaiWeb/GICB102010.do?action=search&screenId=GICB102010&cbTokyo=1'] def parse(self, response): for post in response.css('table#search tr'): name = post.css('span#ID_lbJigyoshoName::text').extract_first() if name is None: continue yield Post( company_name = name ) # 現在のページ数を取得する for current in response.css('table tr td[style="width: 60%;"]'): current_page = current.css('span#ID_lbSearchCurrentPage::text').extract_first() if current_page is None: return # 次のページがあるか確認する next_page = int(current_page) + 1 has_next_page = False for id_pager_tag in response.css('table tr td[style="padding:5px;"]'): id_pager_href = id_pager_tag.css('a::attr(href)').extract_first() if id_pager_href is not None: find_array = re.findall('[0-9]+', id_pager_href) if len(find_array) == 0: continue if int(find_array[0]) == next_page: has_next_page = True next_page = str(next_page) break # 次ページが無ければ終了する if has_next_page is False: return # 次のページをリクエストして、解析用parseに渡す yield scrapy.FormRequest.from_response( response, formdata = dict( screenId = 'GICB102010', action = 'page', cbTokyo = '1', curPage = current_page, params = next_page), callback = self.parse, dont_click = True ) return
説明
parseの
response.css('table#search tr'):
から会社名を取得します。
会社名はID名がsearchのtableタグのの中にあるのでresponse.css('table#search tr')
というので検索せます。
会社名自体はspanタグのID名がID_lbJigyoshoNameのテキスト項目なのでspan#ID_lbJigyoshoName::text
で引っ張り出します。
なお、tableタグの中にはtrには何も定義が無いので、余分なtrも検索してしまうので、spanタグがうまく取れなかったらスルーします。次ページ
みたいのがないので、現在ページを取得+1を計算してページネーションと比較する方法を取ります。
現在ページ + 1 in ページネーション数値
を次ページとしたいので、まずは現在ページを取得します。
会社一覧の上に小さく現在ページが表示されているので、そこのタグspan#ID_lbSearchCurrentPage::text
がページ数らしいですので、それを取得します。次はページネーションのところを探索します。
table tr td[style="padding:5px;"]
を配列で取得して、a::attr(href)
のJavaScriptスキームを取得して、そこから正規表現で数値だけを取り出します。
なんで、こんな面倒なことをしているのかというと、上記のページネーションは世間一般のページネーションとは異なっているからで、次ページ表記っぽい>>
が実は「10*次ページ数+1」という謎ルールで、それなら素直にアンカーダグのJavaScriptスキームを読んだほうが早いからです。現在ページ + 1 in ページネーション数値
と判定できれば次ページが存在する、無ければ存在しないのでプログラムは終了とする。次ページに遷移させるところは、トークンらしきものがあるので、ここはFormRequestを使います。
formdata
に色々とデータを押し込んで、callback
はparse
を再帰呼び出しで次ページが無くなるまで繰り返すようにして、dont_click
をTrueにしてJavaScriptスキームを発火させています。
動作結果
scrapy crawl Hellowork
で実行すると、以下のように会社名が取れていることが確認できます。
なお、CSVファイルに出力したいときはscrapy crawl Hellowork -o company_name.csv
とします。
今回、このサイトをスクレイピングして思ったのは、JavaScriptスキームも面倒でしたが、それ以上に面倒と思ったのは、タグにID名が振っていないと、スクレイピングしにくいわ!と心底思いました。
そのため、自分でサイトを作るときは、なるべくタグにはID名を振ろうと反省するのでありました。
#マイナンバー通知カード が廃止(配布の廃止)されます (発行済の通知カードは引続き使用可能)
マイナンバー通知カードは2020年5月末日で廃止(配布の廃止)されます。
発行済のマイナンバー通知カードは引続き使用可能です。
詳細は各自治体のホームページで確認してください。
注意!既に配布済みのマイナンバー通知カードは廃止や無効になりません、引続き使用可能です。
2020年6月以降、マイナンバー通知カードの配布が廃止になるだけです。
現在所有しているマイナンバー通知カードは、2020年6月以降も各種書類を記載するときに使用し、記載住所が現住所と一致していればマイナンバーを証明する書類として使用できます。(確定申告など)
2020年6月以降、新しくマイナンバーを知る方法(書類みたいな物)が各自治体から発表される予定です。
東京都世田谷区、東京都足立区、埼玉県志木市などは2020年5月25日と明記されています。
https://www.city.adachi.tokyo.jp/koseki/20200403tuutikadohaisi.htmlwww.city.adachi.tokyo.jp
神奈川県横浜市は2020年5月31日と明記されています。
www.city.yokohama.lg.jp
埼玉県川越市は2020年5月末日と明記されています。
www.city.kawagoe.saitama.jp
マイナンバー通知カード廃止(配布の廃止)で変わること
注意: 記載住所が現住所と一致していればマイナンバーを証明する書類として今後も使用できます。
- マイナンバー制度の廃止ではない。
- マイナンバー通知カード廃止(配布の廃止)は、自治体によって異なるが遅くとも2020年5月31日で廃止(配布の廃止)される。
- 2020年6月以降で変わることは以下のとおりです。
- 新生児には、マイナンバー通知カードは送付されません。(個人番号通知書らしく、今のところ未定)
- 各自治体がマイナンバー通知カードに変わる通知方法を行う。(個人番号通知書らしく、今のところ未定)
- 住民票をマイナンバー付きで出せるけど料金かかるんだよね・・・。
- マイナンバー通知カードを紛失しても、マイナンバー通知カードの再発行はできなくなります。
- 現在は再発行手数料500円です。
- 引越しをするときに、役場にマイナンバー通知カードに住所を記載してもらう必要がなくなります。
- 引越しをした後、マイナンバーカードを申請する申請書IDは無効になります。(2020年6月以降、申請書IDの新規発行については不明)
- マイナンバーカードの有効期限を超えたとき(電子証明書ではない)、マイナンバーカードを「新規作成」する方法が公式にアナウンスされているが、上記のとおり引っ越しをしていると申請する申請書IDは無効になります。
- マイナンバーを知る方法は、各自治体に一任されているので自治体に問い合わせてください。(個人番号通知書らしく、今のところ未定)
- 現在所有しているマイナンバー通知カードは、2020年6月以降も各種書類を記載するときに使用するので引き続き所有してください。
今のところ細かい情報について未定が多く、2020年6月以降に情報が出ると思います。
マイナンバーカードを申請する申請書IDが失効したときは この書類を使用するらしい。 https://www.kojinbango-card.go.jp/hpsv/wpmng/documents/tegaki-kofu-shinseisho.pdf
マイナンバー通知カード廃止(配布の廃止)の理由
マイナンバー通知カード廃止(配布の廃止)は、2020年5月31日施行されるデジタル手続法に対応した動きです。
www.kantei.go.jp
簡単に言うと デジタル手続法案についての7ページ目に書かれていますが「通知カード」と記載事項変更等の手続を廃止し、負担軽減とマイナンバーカード普及を実現」によることです。
他に総務省の資料は、あんまりないですが・・・デジタル・ガバメント及びマイナンバー制度について(12ページ)や石田総務大臣閣議後記者会見の概要 平成31年3月19日にマイナンバー通知カードの廃止の話が確認できます。
参考までにデジタル手続法が決定したときの答弁があります。
平成31年3月15日に平井内閣府特命担当大臣閣議後記者会見要旨
www.cao.go.jp
それらの動きに応じて各自治体に通達がきているようで、Googleで「マイナンバー通知カード 廃止」で検索すると、多くの自治体が廃止の話を掲載しています。
www.google.com
デジタル手続法について
デジタル手続法が施行される背景などがあり、各省庁の大臣もオンライ化への動きが活発になっています。
「コロナ発生届けが内科医の一言にてWEB対応できるようになる(結核や麻疹等の他の感染症も対応する方向)」は、行政やるじゃん!と思った方は多いと思います。
togetter.com
当然ながら各部署の努力もありますが、その動きを活性化させたのはデジタル手続法が近々施行されるなどの動きがあったのだと思います。
デジタル手続法があるから各省庁や大臣なども動けたんだろうと思います。
あと、IT担当大臣の竹本直一氏の「しょせんは民・民の話」と言ったのも、官は法整備してますの意味合いだったんだろうと、わたしは推測します。
しかし、言い方が悪かったのが、そのひと言で一気にオンライン化が進んだのは皮肉なものです。
www.itmedia.co.jp
今回のコロナ禍が行政を変えた側面もありますが、デジタル手続法が整備されていたということを多くの人は知っておいてほしいと思います。
#GWアドベントカレンダー 「1つサービスを作る」の7日目記事 Herokuへdeploy
以下の GWアドベントカレンダー 「1つサービスを作る」の7日目記事です。
とりあえず出来たものをHerokuにdeployしていきます。
ソースの変更
Herokuのアカウントは作ってある、そしてHerokuのCLIツールはインストール済みとします。
まずは定番のGemfileの編集です。
sqlite3
をdevelopment group
に移し、新しくproduction group
を作り、そこに移動します。
group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] # Use sqlite3 as the database for Active Record gem 'sqlite3', '~> 1.4' end group :production do gem 'pg' end
そしてインストールします。
bundle install
なお --without production
を付けてないのは--without
がDEPRECATED
だからです。
次にデータベースの設定を変更します。
config/database.ymlを変更します。
production: <<: *default database: db/production.sqlite3
とあるので、これを変更します。
production: <<: *default adapter: postgresql encoding: unicode pool: 5
最後に、config/environments/production.rbのconfig.assets.compile
をtrue
に変更します。
config.assets.compile = true
Gitの初期化
ローカル環境のGitを初期化しておきます。
git init git add -A git commit -m 'Initial commit'
Herokuへdeploy
Herokuにログインします。
heroku login
ブラウザが起動するのでログインします。
Herokuでアプリの作成をします。
これは1回だけ実行します。
heroku create heroku remote -v
Herokuのdeploy先が表示されればOKです。
deployする。
git push heroku master
多少のワーニングが出るが気にしない。
データベースをMigrationする。
heroku run rails db:migrate
Twitterの設定変更する
Callback URLをheroku create
したときに出た場所に変更する。
https://deployした場所.herokuapp.com/users/auth/twitter/callback
動作確認
https://deployした場所.herokuapp.com/users/sign_up/ にアクセスするとdeployができていることが確認できます。
Sign in with TwitterをクリックするとSign inが出来ました。
まとめ
railsでdeviceとomniauth-twitterとbootstap4を使ってHerokuへのdeployまでができました。
完成には、まだ遠いのですが、ベースの仕組みはできたので、なにかに使えればいいですね。
余計なサービスなども、そのうちやっていきますが、今回のGWアドベントカレンダーは、ここで終わりにします。
#GWアドベントカレンダー 「1つサービスを作る」の6日目記事 アクセスコントロール対応
以下の GWアドベントカレンダー 「1つサービスを作る」の6日目記事です。
昨日作った更新URLに、アクセスコントロールを追加します。
アクセスコントロール
app/controllers/users_controller.rb を編集し先頭に1行追加します。
before_action :authenticate_user!, only: [:edit, :update]
authenticate_user!
をbefore_action
に入れることで認証されたユーザーのみがアクセスできるようになります。
editとupdateが対象で、showは誰でもアクセスさせたいのでonly: [:edit, :update]
とします。
動作確認
http://localhost:3000/users/1/edit にアクセスしてDeveloper toolを起動しApplicationのCookiesから認証されたCookieを削除します。
画面をリロードすると、サーバーログではUnauthorized
となっていることが確認できます。
#GWアドベントカレンダー 「1つサービスを作る」の5日目記事 情報の追加2
以下の GWアドベントカレンダー 「1つサービスを作る」の5日目記事です。
情報の追加をしていきます。
具体的にはプロフィール画面に、ウェブサイト情報などを追加したのは読めたのですが、更新ができないので更新します。
更新
app/controllers/users_controller.rb を編集しupdateを追加します。
def update @user = User.find(params[:id]) if @user.update(websites_params) flash[:success] = "Websites updated" redirect_to @user else render 'edit' end end private def websites_params params.require(:user).permit(websites_attributes:[:title, :url, :id]) end
ここでは permitで更新したい関連テーブルをwebsites_attributes:
シンボルで指定します。
これでwebsitesテーブルだけを更新します。
app/views/users/show.html.erb も更新し<p><%= @user.twitter %></p>
の下に以下の変更点を追記します。
<table class="table table-responsive"> <% @user.websites.each do |w| %> <tr> <td> <%= w.title %> </td> <td> <%= w.url %> </td> </tr> <% end %> </table>