Skip to content

Instantly share code, notes, and snippets.

@korun
Last active August 29, 2015 14:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save korun/76ab466fc618f920fb19 to your computer and use it in GitHub Desktop.
Save korun/76ab466fc618f920fb19 to your computer and use it in GitHub Desktop.
Выгрузка комментариев ВКонтакте
# See http://help.github.com/ignore-files/ for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile '~/.gitignore_global'
# Ignore popular IDE local-settings.
.idea/*
.settings/*
# Ignore generated comment files.
comments*
Gemfile.lock

Выгрузка комментариев Вконтакте в файл для бэкапа

Что это?

Данный код представляет собой примитивное веб-приложение, отрисовывающее единственную страницу содержащую javascript и кнопку для его активации.

Скрипт перебирает все страницы вашего приложения ВКонтакте, и выгружает все комментарии.

На выходе получается файл comments.txt, который можно использовать в бэкапе.

Как установить?

Лучше разворачивать всё на linux, если решите ставить на windows - вы сами выбрали путь боли.

Установите ruby и bundler.

Скачайте данный проект, и разархивируйте в папку с удобным для вас именем.

В файле config.yml укажите ваш vk_api_id.

В этом же файле (в пункте last_index) укажите ID вашего самого последнего комментария из бэкапа.

После этого в файл hosts добавьте:

127.0.0.1 your_site.com

Известный баг: после восстановления из бэкапа все комментарии становятся первоуровневыми (по вложенности), т.е. теряется "древовидность". (На данный момент вроде бы пофикшен, но нет возможности проверить.)

Как запустить?

Запустить сервер: bundle install && bundle exec ruby app.rb

Перед запуском скрипта обязательно разлогиниться в ВКонтакте, и не входить туда во время работы скрипта!

Открыть в браузере страницу (http://your_site.com:4567/) и пока НЕ нажимать кнопку Сделать всё!

После того, как страница загрузится, скопировать ваш файл comments.txt из бэкапа в папку с проектом.

После этого в браузере нажать кнопку Сделать всё!, дождаться окончания работы скрипта.

Комментарии ВКонтакте будут добавлены к уже существующим в файл comments.txt.

$ ->
API_ID = <%= @config['vk_api_id'] %>
ANONYMOUS_NAME = '<%= @config['anonymous_name'] %>'
offset = 0
limit = 200 # Maximum
timeout_limit = <%= @config['vk_timeout'] %>
avatar_field = 'photo_<%= @config['vk_avatar_size'] %>'
page_index = 0
rows_count = 0
max_rows_count = 50
last_index = <%= @config['last_index'] %>
timer_id = null
module_ids = {
blog: 1
news: 2
publ: 3
photo: 4
load: 5
dir: 6
board: 7
}
parse_url = (url) ->
a = url.split '/'
{
module_id: module_ids[a[3]]
material_id: a[a.length - 1].split('-').pop()
}
pages = []
$target = $('#target')
$progressbar = $('#progressbar')
comment_template = (comment, options = {}) ->
if comment.user
username = "#{comment.user.first_name} #{comment.user.last_name}".replace(/\|/g, "&#124;")
else
username = ANONYMOUS_NAME
comment.user = {}
comment_text = comment.text.replace(/\|/g, "&#124;")
vk_avatar_url = comment.user[avatar_field] || '\\N'
vk_profile_url = comment.user.uid && "http://vk.com/id#{comment.user.uid}" || ''
[
comment.id # id
options.module_id # module_id
options.material_id # material_id
0 # premoderation_flag (0 - active, 1 - disabled)
comment.date # created_at timestamp
'' # nickname
username # username
'' # email
comment.user.uid || '' # site_url(?) || vk_uid
'0.0.0.0' # ip
comment_text # text
'' # answer text
0 # user_id
options.parent_id # parent_id
0 # rate
'' # rate_user_ids
0 # unknown_id
101 # unknown_constant (101 (?) - vk, 0 - for ucoz, N - for old)
comment.user.uid || '\\N' # vk_uid
vk_profile_url # vk_profile_url
vk_avatar_url # vk_avatar_url
0 # anonimous? (0 - no, 1 - yes)
0 # unknown_field
].join('|') + "|\n"
VK.init(
apiId: API_ID
onlyWidgets: true
)
get_pages = () ->
console.log "get_pages #{limit}, #{offset}"
VK.Api.call('widgets.getPages',
widget_api_id: API_ID
period: 'alltime'
count: limit
offset: offset
, (r) ->
console.log 'get_pages response'
unless r.response
console.log "ERROR: call 'widgets.getPages'"
console.log r
return
for page in r.response.pages
if page.comments.count
pages.push page
if r.response.pages.length < limit || (offset + limit) >= parseInt(r.response.count)
console.log 'Clear interval...'
clearInterval timer_id
start_comment_cycle()
return
offset += limit
console.log "Limit: #{limit}, offset: #{offset}, count: #{r.response.count}"
return
)
return
get_pages() # VK login
$('#get_pages').click ->
$(this).prop('disabled', true)
console.log 'click!'
old_offset = offset
get_pages()
timer_id = setInterval(() ->
console.log "cycle... #{old_offset} #{offset}"
if old_offset != offset
old_offset = offset
get_pages()
return
, timeout_limit)
return
start_comment_cycle = () ->
console.log 'start_comment_cycle!'
old_index = page_index = 0
get_comments_for(pages[page_index])
timer_id = setInterval(() ->
console.log "get_comments cycle... i#{old_index} i#{page_index} #{pages[page_index].url} (#{pages[page_index].page_id})"
if old_index != page_index
old_index = page_index
get_comments_for(pages[page_index])
return
, timeout_limit)
return
get_comments_for = (page) ->
old_offset = offset = 0
local_timer_id = null
result = ''
vk_get_comments = () ->
options =
widget_api_id: API_ID
url: page.url
count: limit
offset: offset
fields: ['from_id', 'date', 'text', 'replies', avatar_field]
options['page_id'] = page.page_id if page.page_id
VK.Api.call("widgets.getComments", options, (r) ->
unless r.response
console.log "ERROR: call 'widgets.getComments'"
console.log r
return
add_to_result = (com, parent_id = null) ->
options = parse_url page.url
options.parent_id = com.parent_id || parent_id || 0
result += comment_template(com, options)
rows_count += 1
return
result_arr = []
user_ids = []
for comment in r.response.posts
last_index += 1
comment.id = last_index
result_arr.push comment
user_ids.push(comment.from_id) unless comment.user
# Если на этот комментарий отвечали...
if comment.comments.replies.length
# Добавляем каждый ответ к себе
for reply in comment.comments.replies
last_index += 1
reply.id = last_index
# Для ответов иногда не подгружается информация о пользователе.
# Для комментариев первого уровня такого поведения не замечено
user_ids.push(reply.uid) unless reply.user
reply.parent_id = comment.id
result_arr.push reply
final_callback = ->
if r.response.count < limit || (offset + limit) >= parseInt(r.response.count)
$target.append result
add_to_file = (need_clear = true) ->
$.post('/add_to_file',
comments: $target.html(),
(data) ->
$progressbar.hide()
$target.html('')
rows_count = 0
if need_clear
clear_interval()
else # all is done, refresh page
window.location.reload()
return
)
return
clear_interval = () ->
console.log "Clear getComments #{page_index} interval..."
clearInterval(local_timer_id)
if (page_index + 1) >= pages.length
console.log "Clear main pages interval..."
clearInterval timer_id
add_to_file(false)
return
page_index += 1
return
if rows_count >= max_rows_count
$progressbar.show()
add_to_file()
else
clear_interval()
return
offset += limit
console.log "Limit: #{limit}, offset: #{offset}, count: #{r.response.count}"
return
if user_ids.length
VK.Api.call('users.get',
user_ids: user_ids
fields: [avatar_field]
, (ur)->
console.log "users.get"
console.log user_ids
console.log ur
users = {}
for user in ur.response
users[user.uid] = user
console.log users
for comment in result_arr
comment.user ||= users[comment.from_id || comment.uid]
add_to_result comment
final_callback()
return
)
else
for r_comment in result_arr
add_to_result r_comment
final_callback()
return
)
return
vk_get_comments()
local_timer_id = setInterval(() ->
if old_offset != offset
old_offset = offset
vk_get_comments()
return
, timeout_limit)
return
return
# -*- encoding : utf-8 -*-
require 'yaml'
require 'bundler'
Bundler.require
set :views, File.dirname(__FILE__)
COM_FILENAME = 'comments.txt'.freeze
ROOT_DIR = File.expand_path(File.dirname(__FILE__)).freeze
FULL_FILENAME = File.join(ROOT_DIR, COM_FILENAME).freeze
get '/' do
@file_url = File.exist?(FULL_FILENAME) ? "/#{COM_FILENAME}" : nil
haml :template
end
post '/add_to_file' do
File.open(FULL_FILENAME, 'a') do |f|
f << params['comments']
end
status 204
''
end
get '/app.js' do
@config = YAML.load_file(File.join(ROOT_DIR, 'config.yml'))['settings']
coffee erb(:'app.js.coffee')
end
get "/#{COM_FILENAME}" do
send_file COM_FILENAME
end
---
settings:
vk_api_id: 2014686
vk_avatar_size: 50 # 50, 100, 200, 'max'
vk_timeout: 300 # ms
last_index: 0
anonymous_name: 'Anonymous'
# -*- encoding : utf-8 -*-
source 'https://rubygems.org'
gem 'sinatra', '>= 1.4.5'
gem 'haml', '>= 4.0.0'
gem 'coffee-script', '>= 2.2.0'
gem 'therubyracer', '>= 0.12.1'
!!!
%html{:lang => 'ru'}
%head
%meta{:charset => 'utf-8'}
%title VK comments util
%script{:src => 'http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js'}
%script{:src => 'http://vk.com/js/api/openapi.js?113'}
%script{:src => 'app.js'}
:css
textarea {
width: 99%;
resize: none;
white-space: nowrap;
overflow-x: scroll;
}
img {
border: 0;
}
#progressbar {
display: none;
}
%body
- if @file_url
%a{:href => @file_url} Скачать файл
- else
%button#get_pages{:type => 'button'}
Сделать всё!
%span#progressbar
Отправляем данные...
%img{:src => 'http://www.ucoz.ru/.s/img/wd/6/ajaxs.gif'}
%textarea#target{:readonly => true, :rows => 50, :wrap => 'off'}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment