たれみみマンデー

Ruby(Ruby on Rails)でTwitterフォロー一覧のスクレイピング

Twitter連携アプリ(API)を作っていると、認証のためにパスワードまで要求してきます。

そのためユーザービリティが下がるので自分用にユーザーIDだけでTwitterの情報(不フォローしてる人)を取ってくるプログラムを書きました。

今回はCapybaraとPoltergeistを使っています。

尚、サーバー負荷、著作権に関することなど今一度Twitter利用規約をご覧の上各自の自己責任でお願いします。以下コントローラーです。

indexアクションに飛ぶと動き出します。

class SessionsController < ApplicationController

require 'pp'
require 'capybara/dsl'
require 'capybara/poltergeist'

  def index
    redirect
  end

  def redirect
    scrape = Scrape.new
    scrape.visit_site
    twitter_json
    redirect_to root_path
  end

 class Scrape
    #DSLのスコープを別けないと警告がでます
    include Capybara::DSL

  def initialize()
    Capybara.register_driver :poltergeist do |app|
      Capybara::Poltergeist::Driver.new(app, :inspector => true,js_errors: false, :timeout => 60)
    end
    Capybara.default_driver = :poltergeist
    Capybara.javascript_driver = :poltergeist
    Capybara.run_server = false
  end

  def visit_site
    page.driver.headers = { "User-Agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2564.97 Safari/537.36" }

    page.visit('https://twitter.com/******/following')
#スクリーンショットで保存
#    page.save_screenshot('screenshot.png', :full => true)
    begin
      fill_in "session[username_or_email]", with: "********"
      fill_in "session[password]", with: "*******"
    
      click_button "ログイン"
    rescue
    end

#     current_count = page.all('.ProfileCard-avatarLink').length
#     until current_count < page.all('.ProfileCard-avatarLink').length      
#      page.execute_script "window.scrollTo(0, document.body.scrollHeight);"
#     end
#      sleep(5)
#   wait_until { page.

    $friends = []
    doc = Nokogiri::HTML.parse(page.html)
    doc.css('.ProfileCard-avatarLink').each do |element|
      name = element.attribute("title").value
      id = element.attribute("href").value.gsub!("/","@")
        element.css('img').each do |image|
          image_url = image.attribute("src").value
          friends = {}
          friends = {name: name, id: id, image_url: image_url}
    $friends << friends
        end
    end
    pp $friends 
   end
  end
 
 
  def twitter_json
#  user = User.find_by(uid: @@user_id )
#   twitter_client(user) 
#    friends = @client.friends
   friends_json = []
   links_json = []
    
    $friends.each do |friend|
      friend_json = {
         "id" => friend[:id],
         "image_url" => friend[:image_url]
      } 
  friends_json << friend_json

      link_json = {
        "source" => friend[:id],
        "target" => friend[:id] 
      }

      links_json << link_json
    end

    complete_json = {
          "node" => friends_json ,
          "links" => links_json
    }
    @@u = "3"
    File.open("public/#{@@u}.json","w") do |f|
      f.write(complete_json.to_json)
    end 
  end

 

以上。

何かあれば気軽に連絡ください。

 

たれみみ

@taremimimi_7

Rubyで画像を無限に収集する

 

機械学習をするに大量の画像が必要だったのでプログラムをかきました。

個人的にGoogle画像検索よりBingの方が精度のいい画像がでてくると今回初めて気づいたのでBing画像検索でレッツゴーです。最新版のBing検索でRubyスクリプトはうちだけ!(なはず笑 探してもなかったからブログ書いてます)

(本来ならAPIを使うべきですが、Googleは制限が多い、BingはUIが変わってAPIが使えなかった、のでこのような対応をとりました。サーバー負荷を念頭に必ずおとなしくやりましょう。自己責任でお願いします。)

 

require 'selenium-webdriver'
require 'nokogiri'
require 'open-uri'
require 'date'
require 'addressable/uri'
require 'kconv'
require "uri"
require 'open_uri_redirections'

#文字対応増やすためStringクラスを拡張
class String  
  def sjisable  
   str = self  
   str = str.exchange("U+301C", "U+FF5E") # wave-dash  
   str = str.exchange("U+2212", "U+FF0D") # full-width minus  
   str = str.exchange("U+2013", "U+FF0D") # full-width minus  
   str = str.exchange("U+00A2", "U+FFE0") # cent as currency  
   str = str.exchange("U+00A3", "U+FFE1") # lb(pound) as currency  
   str = str.exchange("U+00AC", "U+FFE2") # not in boolean algebra  
   str = str.exchange("U+2014", "U+2015") # hyphen  
   str = str.exchange("U+2016", "U+2225") # double vertical lines  
 end  

 def exchange(before_str,after_str)  
  self.gsub( before_str.to_code.chr('UTF-8'),  
      after_str.to_code.chr('UTF-8') )  
 end  

 def to_code  
  return $1.to_i(16) if self =~ /U\+(\w+)/  
  raise ArgumentError, "Invalid argument: #{self}"  
 end  
end  



def get_url

driver = Selenium::WebDriver.for :chrome

#q=の後に日本語でキーワード入れて下さい(今は'安倍晋三'がエンコードされてます)
  driver.navigate.to 'https://www.bing.com/images/search?q=E6%A4%8E%E5%90%8D%E6%9E%97%E6%AA%8E'

   10.times do 
    driver.find_elements(:class, 'iusc').last.location_once_scrolled_into_view
      # Wait for the additional images to load
     current_count = driver.find_elements(:class, 'iusc').length
     until current_count < driver.find_elements(:class, 'iusc').length
      sleep(3)
     end
     sleep(5)
   end

  elements = driver.find_elements(:class, 'iusc')
# Check how many elements are there now
  ary = []
  elements.each do |element| 
       ary << element.attribute('m').scan(/","murl\":"(.+)","turl":/)
  end

  @ary = []
  ary.flatten!
  ary.each do |img|
    if /\.(jpg|png)$/ =~ img.to_s 
      @ary << URI.escape(img.to_s.force_encoding('utf-8').sjisable.encode('UTF-8'))
    end
  end
 @ary.flatten!
# ファイルの書き出しを行う
  @ary.each_with_index do |url,i|
# ファイル名を決定
    begin
# filename = File.basename(url)
     if /\.(jpg)$/ =~ url 
      filename = "abe#{i}.jpg"
     else 
      filename = "abe#{i}.png"
     end
# ファイルを保存するディレクトリ名を指定
   p filename
    dirname = "./abe_img/*" + filename
      open(dirname, 'wb') do |file|
        open(url.encode("utf-8", invalid: :replace, undef: :replace), :allow_redirections => :safe) do |data|
          sleep(1)
          file.write(data.read)
        end
      end
    rescue
      puts "とばしました"
    end
  end
end
get_url

エラーでたらそれはきっとsleniumドライバーのあれかBingの仕様変更です。

前者であればたくさんでてくるのでググってみて下さい笑

 

10.times do の数字をいじれば30件毎に画像の取得件数が増減すると思います(アレ、色がなんかおかしいぞまあいいや)

 

なにか質問等あれば気軽に連絡ください。

だれかのためになれば幸いです。

それでは〜!

 

たれみみ

@taremimi_7

 

RubyでSEIYUの商品情報をほぼ全取得、CSVに出力。

RubyでSEIYUの商品情報をほぼ全件取得、CSVに出力するプログラムを書きました。

商品件数は1万4000件ほどあります。

カラムは商品名、値段(¥)、商品画像URL、カテゴリ3つです。

主に使ったGemはAnemoneとNokogiriです。

自分用ビボウロクなのでインデントズレズレ、無駄なコメントアウト多くてすいません。無視して下さい、、、、、

 

require 'rubygems'
#require 'string-scrub'
require 'nokogiri'
require 'kconv'
require "open-uri"
require "anemone"
require 'csv'


def category_url_get
 $category = []
 $category_urls = []
 base = "https://www.the-seiyu.com/front/contents/top/ns/"
  Anemone.crawl(base, :depth_limit => 0 , :delay => 3) do |anemone|
   anemone.on_every_page do |page|
    doc = Nokogiri::HTML.parse(page.body.force_encoding("UTF-8")) 
  
      category_array = []
      doc.xpath("//*[@id='categoryListWrapper_0002']/div[2]").each do |node|
 #p         category_name_level_2 = node_2.inner_text.gsub!(/(\s)/,"")
   node.css("a.level_3_li_Inner").each do |node_3|
 #puts  "---------------------------------------------------"
 #category3
   category_3 = node_3.inner_text.gsub!(/(\s)/,"")
   category_url_3 = "https://www.the-seiyu.com" + node_3 ["href"]
 #category2
  category_2 = node_3.parent.parent.parent.css("a.level_2_li_Inner").inner_text.gsub!(/(\s)/,"")
 #category1
  category_1 = node_3.parent.parent.parent.parent.parent.css("div.level_1_li_Inner").xpath("./a/span").inner_text
 puts " ----------------------------------------------"                                                              
                  category_array << category_1
                  category_array << category_2
                  category_array << category_3
#                category_array << category_url_3
                 $category_urls << category_url_3
                 $category << category_array
                 category_array = []
            end
         end
      end
     end
#    puts "-------------------"
#p  $category_urls 
#p  $category
# puts "------------------------"
  end  

#  $category_urls = $category_urls.reject!{|elem| elem =~ /front\/app\/catalog\/select_commodity_list\/\?searchSelectParentCode=89/}
#category_url_get

#$other_urls = []
#def other_category_url
#  $other_urls = $category_urls.map {|url|  url + "&mode=image&pageSize=49&currentPage=#{p+1}&alignmentSequence=1&resultMessage="}
#end

def scrape
   #  $urls << "https://www.the-seiyu.com/front/app/catalog/list/init?searchCategoryCode=200013&nsFlg=true&wrt=y&parent1Code=200001&parent2Code=200013"
   # $urls << "https://www.the-seiyu.com/front/app/catalog/list/init?searchCategoryCode=200065&nsFlg=true&wrt=y&parent1Code=200007&parent2Code=200065"
   # $category_urls  = []
   #  $category = []
   # $category_urls << ["https://www.the-seiyu.com/front/app/catalog/list/init?searchCategoryCode=100870&nsFlg=true&wrt=y&parent1Code=200008&parent2Code=200071","https://www.the-seiyu.com/front/app/catalog/list/init?searchCategoryCode=200126&nsFlg=true&wrt=y&parent1Code=200002&parent2Code=200022"] 
   # $category <<  [["ペット・園芸用品", "園芸用品", "鉢・プランター"], ["ペット・園芸用品", "園芸用品", "園芸小物ほか"]]
 
    $urls = []
    $stuff=[]
    $result = []

    $category_urls.flatten! 
    $category.flatten!(1)

    n = 0
    $category_urls.zip($category).each do |category_url,category|
    p category_url
    p category
    puts "-----------------------"
    $urls = []
    $urls = Marshal.load(Marshal.dump(category_url))
#  $result = category.dup

# $urls.each do |url|
#      $url = []
#      $url << url
       Anemone.crawl($urls, :depth_limit => 0 , :delay => 3) do |anemone|
        flug = 0
        anemone.on_every_page do |page|
          doc = Nokogiri::HTML.parse(page.body.force_encoding("UTF-8")) 
          doc.xpath("//li[@class='jsFlatHeight_list' and 'resized']").each do |node|
          p node
          #商品名
            stuff=[]
            result = []
            stuff << node.xpath(".//img").attribute("title").value
          #値段
            stuff << node.xpath(".//div/div/span/strong").inner_text
          #画像URL
            stuff << node.xpath(".//img").attribute("data-original").value
            $stuff << stuff
            result = Marshal.load(Marshal.dump(category))
            result << $stuff
            $stuff = []
            result.flatten!
            $result << result
          end
puts "-------------------------"
          p $result
#最後の数字(1ページの為の例外処理)
          begin
            n = doc.xpath("//*[@id='list']/div/ul[2]/li[last()]/a").attribute("href").value.scan(/javascript:move\('(\d+)',/).flatten[0]
            n =- 1
           if flug == 0
              (1..n).each do |p|
                $urls << category_url + "&mode=image&pageSize=49&currentPage=#{p+1}&alignmentSequence=1&resultMessage="  
              end
            end
            flug =+ 1
          rescue 
           next
         end
        end
       end
#p   $stuff
#     end
      n =+1
    end
#p    $result
  end
scrape

def to_csv
  header = ["カテゴリ1","カテゴリ2","カテゴリ3","商品名","値段(¥)","画像URL"]  
  CSV.open('seiyu.csv','w',:encoding => "Windows-31J",:headers => true) do |file|  
    file << header
    $result.each do |line|
      file << line
    end
  end
 puts "--------------------------------------------------------------------------"
end
to_csv

 

サーバー負荷を考えて必ずdelayを長くとって下さい。

自己責任でお願いします。なにか不都合があれば即削除します。

 

質問も受け付けていますので気軽にメンションとばして下さい。

ではでは〜。

 

たれみみ

@taremimi_7

 

 

 

Ruby(Rails)でTwitterOauth認証の許可をすっ飛ばす。

タイトルにあります通り、Ruby(Rails)でTwitterOauth認証の許可をすっ飛ばすプログラムかきました。Oautu認証は認証画面でいったん外部にとばされるため、サイトコンテンツの世界観が崩れることを阻止する目的でかきました。だれかのためになれば幸いですが、よろしくなかったら即削除します。連絡下さい。

 

尚便宜上コンシューマーキーなどを出してますが、環境変数等に隠して下さい。

自動化するためGemにMechanizeをつかってます。Gemfileに追加して下さい。。

gem 'twitter'
gem 'mechanize'
gem 'oauth'

 

以下ContorollerとModelです。

indexアクションにとばしたら全てが動きます。


class
SessionsController < ApplicationController require 'pp' CONSUMER_KEY = "*******" CONSUMER_SECRET = "********" CALLBACK_URL="http://127.0.0.1:3000/auth/twitter/callback" def index redirect end def redirect consumer @@oauth_token = @@request_token.token mechanize end def mechanize agent = Mechanize.new agent.user_agent_alias = 'Mac Safari 4' page = agent.get("https://api.twitter.com/oauth/authorize?oauth_token=#{@@oauth_token}") form = page.forms[0] form["session[username_or_email]"] = "*****" #POSTでEmailかIDを受け取って入れて下さい form["session[password]"] = "******" #POSTでpasswordを受け取って入れて下さい next_page = form.submit elements = next_page.search("a") e = elements[6][:href] if e.match("oauth_verifier=") reg = e.match("oauth_verifier=") oauth_verifier = reg.post_match end @access_token = @@request_token.get_access_token(:oauth_verifier => oauth_verifier) @@token = @access_token.token @@token_s = @access_token.secret @@user_id = @access_token.params[:user_id] @@screen_name = @access_token.params[:screen_name] create end def create # user = User.from_omniauth(env["omniauth.auth"]) auth = { :token => @@token, :token_s => @@token_s, :user_id => @@user_id, :screen_name => @@screen_name } user = User.find_by_uid(@@user_id) || User.create_with_omniauth(auth) session[:user_id] = user.id twitter_json redirect_to root_path, :notice => 'ログインしました' end
#twitter_jsonはd3.js用の読み込みファイルなので無視してもらってかまいません
def twitter_json user = User.find_by(uid: @@user_id ) twitter_client(user) friends = @client.friends friends_json = [] links_json = [] friends.each do |friend| friend_json = { "id" => friend.screen_name, "image_url" => "#{friend.profile_image_url}" } friends_json << friend_json link_json = { "source" => user.screen_name, "target" => friend.screen_name } links_json << link_json end complete_json = { "node" => friends_json , "links" => links_json } File.open("public/#{@@user_id}.json","w") do |f| f.write(complete_json.to_json) end end def destroy session[:user_id] = nil redirect_to root_path, :notice => 'ログアウトしました' end def oauth_failure redirect_to root_path, :notice => 'ログイン処理が中断されました' end private def consumer consumer = OAuth::Consumer.new(CONSUMER_KEY, CONSUMER_SECRET, { :site => "https://api.twitter.com", :scheme => :header }) @@request_token = consumer.get_request_token(:oauth_callback => CALLBACK_URL) end def twitter_client(user) @client = Twitter::REST::Client.new do |config| config.consumer_key = CONSUMER_KEY config.consumer_secret = CONSUMER_SECRET config.access_token = user.token config.access_token_secret = user.token_s end end end
class User < ActiveRecord::Base
def self.create_with_omniauth(auth)
     create! do |user|
       user.uid = auth[:user_id]
       unless auth.blank?
         user.screen_name = auth[:screen_name] 
       end  
      user.token = auth[:token]
      user.token_s = auth[:token_s]
      user.save!
     end
   end
end

 カラムはこんな感じ。以下migrateファイル

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :uid
      t.string :token
      t.string :token_s
      t.string :name
      t.string :screen_name
      t.string :image
      t.string :friends

      t.timestamps null: false
    end
  end
end

自己責任でお願いします。

質問も受けてます。気軽にメンションください。

 

たれみみ

@taremimi_7

Rubyのエラー備忘録

Ruby

.rbenv/versions/2.2.1/lib/ruby/2.2.0/open-uri.rb:224:in `open_loop': redirection forbidden: http://files.qwe.jp/wvga/%82%D9%82%F1%82%CC%82%A42.jpg -> https://www.qwe.jp/wvga/%82%d9%82%f1%82%cc%82%a42.jpg (RuntimeError)

 

解決策:

1.gemを入れる

require 'open_uri_redirections'

2.ファイルオープンするときにオプションつける。

open(url, :allow_redirections => :safe)

Pythonで文字列を逆に。

言語処理100本ノック1つ目。

Rubyではreverseで文字列を逆にできますが

Pythonでスマートなやり方知らなかったのでビボウロク。

 

s = "abcdefg"

print(s[::-1])

 

だそうです。