Skip to content

Instantly share code, notes, and snippets.

@madogiwa0124
Last active December 10, 2017 23:55
Show Gist options
  • Save madogiwa0124/45874f00149015292d0174b19565d7ea to your computer and use it in GitHub Desktop.
Save madogiwa0124/45874f00149015292d0174b19565d7ea to your computer and use it in GitHub Desktop.
はてなブログAPIを使って記事の一覧をJSONで取得する
HATENA_BLOG_URL=""
HATENA_API_KEY=""
HATENA_USER_ID=""
require 'uri'
require 'dotenv'
require 'net/http'
require 'nokogiri'
require 'json'
class Hatena
def initialize
# 環境変数のロード
Dotenv.load
@user_id = ENV['HATENA_USER_ID']
@password = ENV['HATENA_API_KEY']
@page_url = ENV['HATENA_BLOG_URL']
@entry_list = []
end
# はてなブログAPIにアクセスし記事の一覧を取得するメインメソッド
def get_entry_list()
loop do
# 遷移先URLが取得出来る間、処理を実行
if @page_url
# はてなブログAPIを呼び出し結果を取得しパース
entry = get_entry()
entry = Nokogiri::XML.parse(entry, nil, 'utf-8')
# ブログの記事部分を取得
set_entry_list(entry)
# 次回アクセス先をURL部分を取得
set_next_page(entry)
else
return @entry_list.flatten.to_json
end
end
end
private
# title,content,categoryのリストをhashに整形して返却するメソッド
def entry_list_to_hash(entry)
entry_hash_list = []
titles = entry.css('entry title').map(&:text)
contents = entry.css('entry content').map(&:text)
# 記事とcategoryは、1:nの関係のため、記事毎に配列で取得
category = []
entry.css('entry').each do |e|
category << e.css('category').map{ |c| c.attribute('term').value }
end
titles.length.times do |i|
entry_hash_list << { title: titles[i],
content: contents[i],
category: category[i] }
end
entry_hash_list
end
# 整形された記事一覧を格納するメソッド
def set_entry_list(entry)
@entry_list << entry_list_to_hash(entry)
end
# 次アクセスするURLを取得するメソッド
def set_next_page(entry)
link_urls = entry.css("link[rel^='next']")
@page_url = link_urls.empty? ? nil : link_urls.attribute('href').value
end
# はてなブログAPIを呼び出し結果取得するメソッド
def get_entry()
uri = URI(@page_url)
req = Net::HTTP::Get.new(uri)
req.basic_auth(@user_id, @password)
res = Net::HTTP.start(uri.host,
uri.port,
use_ssl: true) { |http| http.request(req) }
res.body
end
end
# はてなブログのインスタンスを生成し、取得した記事一覧をファイル出力
File.open('hatena_entry_list.json', 'w') do |f|
f.puts Hatena.new.get_entry_list
end
[
{
"title": "Ruby:seleniumを使って動的ページをスクレイピングしてみる",
"content": "以前、Nokogiriを使ったスクレイピングについてブログを書きましたが、JavaScriptで初期表示時にページを動的に変更しているページ等上手く値を取得するこが出来ません。。。 \n※httpレスポンスに設定されたHTMLを取得しているため\n\n[http://madogiwa0124.hatenablog.com/entry/2017/09/01/000227:embed:cite]\n\n動的ページのスクレイピングには、Webブラウザでの操作を自動化できる`selenium`が便利です。 \n\n[https://github.com/SeleniumHQ/selenium:embed:cite]\n\n[:contents]\n\n# はじめに\n今回は、Google翻訳の結果をスクレイピングするツールを作ったので、それをもとに`selenium`の使い方を書いていきます。\nちなみにソースコードは`Ruby`です。\n\n# 使い方\n## 環境構築\n`selenium`を使うのに必要なのは、selenium用のgemとWebDriverです。今回はGoogleChromeのドライバをインストールしてます。\n\n```\n# selenium用のgemをインストール\n$ gem install selenium-webdriver\n# Chrome用のドライバをインストール\n$ brew install chromedriver\n```\n\nこれで準備完了です!\n\n## 実際にスクレイピングしてみる\nスクレイピングの基本的な手順は、下記の通りです。\n\n1. ドライバーの起動\n1. URLへアクセス\n1. 要素を取得\n\n実際にGoogle翻訳の結果を取得するソースコードが以下のものです。\n``` ruby\n# Google翻訳にアクセスして、翻訳結果を取得\n# target_lang:翻訳前言語、result_lang:翻訳後言語、word:翻訳対象文字列\ndef get_translate_result(target_lang, result_lang, word)\n # URL生成\n url = \"#{GOOGLE_TRANSLATE_URL}##{target_lang}/#{result_lang}/#{word}\"\n # ドライバーの起動\n driver = Selenium::WebDriver.for :chrome\n # URLへアクセス\n driver.navigate.to(url)\n # タイムアウト値の設定\n wait = Selenium::WebDriver::Wait.new(timeout: 10)\n begin\n # タイムアウトになるまで、翻訳結果部からテキスト情報を取得\n wait.until{ html = driver.find_element(id: 'result_box'); html.text }\n rescue RuntimeError => e\n # ERROR処理、メッセージを出力してドライバーを終了\n puts e.message\n driver.quit\n end\nend\n```\n\n## ヘッドレスブラウザを使ってみる\nヘッドレスブラウザを使うと、ブラウザを起動せずにスクレイピングを行うことができます。 \n先程のソースコードをヘッドレスブラウザを使用するように書き替えたものが以下です。\n\n``` ruby\n# Google翻訳にアクセスして、翻訳結果を取得\ndef get_translate_result(target_lang, result_lang, word)\n # URL生成\n url = \"#{GOOGLE_TRANSLATE_URL}##{target_lang}/#{result_lang}/#{word}\"\n # オプションの生成(ヘッドレスブラウザで動作するように)\n options = Selenium::WebDriver::Remote::Capabilities\n options = options.chrome('chromeOptions' => { args: ['--headless'] })\n # ドライバーの起動\n driver = Selenium::WebDriver.for :chrome, desired_capabilities: options\n # URLへアクセス\n driver.navigate.to(url)\n # タイムアウト値の設定\n wait = Selenium::WebDriver::Wait.new(timeout: 10)\n begin\n # タイムアウトになるまで、翻訳結果部からテキスト情報を取得\n wait.until{ html = driver.find_element(id: 'result_box'); html.text }\n rescue RuntimeError => e\n # ERROR処理、メッセージを出力してドライバーを終了\n puts e.message\n driver.quit\n end\nend\n```\n\nソースコードの完全版は下記です。\n\n[https://github.com/Madogiwa0124/GoogleTranslateCmd:embed:cite]\n\n# 参考\n[https://qiita.com/edo_m18/items/ba7d8a95818e9c0552d9:embed:cite]\n\n[https://qiita.com/meguroman/items/41ca17e7dc66d6c88c07:embed:cite]\n\n",
"category": [
"Ruby"
]
},
{
"title": "Ionic:スワイプでページ更新する方法のメモ",
"content": "Ionicで下スワイプでページ更新を行う方法をメモφ(..)\n\n↓実装イメージはこんな感じです。\n\n[f:id:madogiwa0124:20171112232529g:plain]\n\n# 手順\n## 概要\nIonicでスワイプでページ更新を実装するには、`ion-refresher`を使用します。 \nViewの`ion-content`内に`ion-refresher`を配置し、`(ionRefresh)=\"doRefresh($event)\"`のように実行メソッドを定義します。\n\n詳細は下記を参照頂ければ!※Ionicの公式ドキュメントです\n\n[https://ionicframework.com/docs/api/components/refresher/Refresher/:title]\n\n## Viewへの配置\n下記のようにView内の`ion-content`配下に`ion-refresher`を配置します。\n``` html\n<ion-content no-padding>\n <ion-refresher (ionRefresh)=\"doRefresh($event)\">\n <ion-refresher-content></ion-refresher-content>\n </ion-refresher>\n</ion-content>\n```\n\n## 更新処理の実行及び完了の実装\nViewへの配置が終わったら`.ts`側にページ更新及び完了の処理を実装していきます。 \n下記の実装では、API呼び出しによるデータ取得の完了または、3秒経過したら完了とし、ローディングを終了させています。\n\n```\n@IonicPage()\n \n@Component({\n selector: 'page-page-index',\n templateUrl: 'page-index.html',\n})\nexport class PageIndexPage {\n pages = [];\n result = '';\n api_url = 'https://moook.herokuapp.com/pages.json';\n refresher = null;\n\n constructor(public navCtrl: NavController, public navParams: NavParams, private http: Http, public loadingCtrl: LoadingController) {\n // ロード画面を表示\n this.presentLoading();\n // APIからデータを取得\n this.get_pages_data();\n }\n\n doRefresh(refresher) {\n this.refresher = refresher;\n // APIからデータを取得\n this.get_pages_data();\n // タイムアウトの設定\n setTimeout(() => {\n // ページ更新を完了とし、処理を中断\n refresher.complete();\n }, 3000);\n }\n\n get_pages_data(){\n this.http.get(this.api_url).subscribe(\n respons => {\n let result = respons.text();\n this.pages = [];\n JSON.parse(result || null).forEach(data => {\n this.pages.push(new Page(data)); \n });\n if (this.refresher != null) {\n // データ取得が終わったら完了\n this.refresher.complete();\n }\n },\n error => {\n let result = 'faild : '+ error.statusText;\n console.log(result);\n }\n );\n }\n\n}\n\n```\n\n完全なソースコードは下記に配置してありますφ(..)\n\n[https://github.com/Madogiwa0124/Moook.FrontEnd/tree/master/src/pages/page-index:embed:cite]\n\n以上です。\n",
"category": [
"ionic",
"Anglar"
]
},
{
"title": "Ionic:API呼び出し処理の実装方法メモ",
"content": "Moookのモバイル対応でAPIからデータを取得する対応をしたので、その方法を備忘目的でメモφ(..)\n\n↓こんな感じでMoook(https://moook.herokuapp.com/pages)から取得したデータをIonic側で取得し、表示しています。 \n \n[f:id:madogiwa0124:20171105115923g:plain]\n\n# やりかた\n## モジュールのインポート\nまずは、`app.module.ts`に`http`モジュールを追記し、API呼び出しに使用するモジュールをインポートします。\n``` javascript\n/* 省略 */\nimport { HttpModule } from '@angular/http'; // 追記\n\n@NgModule({\n declarations: [\n MyApp,\n HelloIonicPage,\n ItemDetailsPage,\n ListPage,\n PageIndexPage,\n PageShowPage\n ],\n imports: [\n BrowserModule,\n IonicModule.forRoot(MyApp),\n HttpModule // 追記\n ],\n/* 省略 */\n})\nexport class AppModule {}\n```\n## API呼び出し処理の実装\nモジュールをインポートしたらコンポーネント側でAPI呼び出し処理を実装します。\n\n``` javascript\nimport { Component } from '@angular/core';\nimport { IonicPage, NavController, NavParams } from 'ionic-angular';\nimport { PageShowPage } from '../page-show/page-show';\nimport { Http } from '@angular/http'; // 追記\n\n/**\n * Generated class for the PageIndexPage page.\n *\n * See https://ionicframework.com/docs/components/#navigation for more info on\n * Ionic pages and navigation.\n */\n\n@IonicPage()\n \n@Component({\n selector: 'page-page-index',\n templateUrl: 'page-index.html',\n})\nexport class PageIndexPage {\n pages = [];\n result = '';\n api_url = 'https://moook.herokuapp.com/pages.json';\n\n constructor(public navCtrl: NavController, public navParams: NavParams, private http: Http) {\n this.get_pages_data();\n }\n\n get_pages_data(){\n   // API呼び出し処理\n this.http.get(this.api_url).subscribe(\n  // 成功時\n respons => {\n let result = respons.text();\n this.pages = JSON.parse(result || null);\n },\n // 失敗時\n error => {\n let result = 'faild : '+ error.statusText;\n console.log(result);\n }\n );\n }\n}\n\n```\n## まとめ\nとりあえずIonicでのAPI呼び出し処理の実装をまとめてみましたφ(..) \nまだまだ、APIのURLを共通化したりとか、HTTP呼び出し処理の共通化とか、まだまだ考慮しないといけない点はあると思いますが、これから色々勉強していこうと思います・・・! \n(誰か教えてください。。。)",
"category": []
},
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment