-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsga_crawler.rb
153 lines (144 loc) · 4.51 KB
/
sga_crawler.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
require 'json'
require 'rubygems'
require 'mechanize'
load 'state_machine.rb'
load 'subjects.rb'
# Recibe una lista de materias y las serializa en forma de JSON.
def create_subjects_json(subject_list)
subjects_json = []
subject_list.each do |subj|
subjects_json << subj.as_json
end
{ 'subjects' => subjects_json }.to_json
end
# Recive una lista de materias y las persiste en un archivo en forma de texto.
def save_data(subject_list)
File.open('data', 'w') do |file|
subject_list.each do |subj|
file.write("#{subj.code}|#{subj.name}\n")
subj.plans.each do |plan|
file.write("#{plan.name}\n")
plan.classes.each do |cla|
file.write("#{cla.day}\n#{cla.start}\n#{cla.finish}\n")
end
end
end
end
File.open('data.json', 'w') do |file|
file.write(create_subjects_json(subject_list))
end
end
# Busca un archivo en donde materias fueron persistidas previamente y las recupera.
def recover_data
subjects = []
dt = nil
begin
File.open('data', 'r') do |file|
file.each_line do |line|
next if line.nil?
if /^[0-9]+\.[0-9]+/.match(line)
code, name = line.strip.split('|')
subj = Subject.new(code, name)
subjects << subj
dt = DataExtractor.new(subj)
elsif /^[A-Z]+$/.match(line)
dt.handle_event(:comision, line.strip)
elsif /(Lunes|Martes|Miércoles|Jueves|Viernes|Sábado|Domingo)/.match(line)
dt.handle_event(:dia, line.strip)
elsif /^[0-9]+:[0-9]+$/.match(line)
dt.handle_event(:hora, line.strip)
end
end
end
File.open('data.json', 'w') do |file|
file.write(create_subjects_json(subjects))
end
rescue StandardError
puts 'Archivo corrupto.'
end
subjects
end
# Recive la url de una materia, analiza su html y extrae el contenido necesario para
# crear un objeto Subject con titulo, codigo, comisiones y horario de cada una.
def extract_data(link)
page = link.click.link_with(text: 'Comisiones').click
subj_title = page.css('div.tab-panel')[0].css('span')
print "#{subj_title[0].text} - #{subj_title[1].text}\n"
dt = DataExtractor.new(Subject.new(subj_title[0].text, subj_title[1].text))
begin
page.css('div.tab-panel')[0].xpath('//text()').each do |tag|
txt = tag.text.strip
dt.handle_event(:comision, txt) if /^[A-Z]+$/.match(txt)
if /(Lunes|Martes|Miércoles|Jueves|Viernes|Sábado|Domingo)/.match(txt)
dt.handle_event(:dia, txt)
end
dt.handle_event(:hora, txt) if /^[0-9]+:[0-9]+$/.match(txt)
end
return dt.subject
rescue StandardError
return Subject.new(subj_title[0].text, subj_title[1].text)
end
end
# Recive la url de una pagina del listado de materias y por cada materia en la
# lista pasa su url a extract_data(). Luego pasa recursivamente a otras paginas
# del listado.
def visit_list(link, visited_list_pages, subjects)
threads = []
page = link.click
page.links_with(text: 'Detalles').each do |link|
threads << Thread.new do
begin
subjects << extract_data(link)
rescue StandardError
puts 'Una materia fallo'
end
end
end
page.links.find_all { |l| /^[0-9]+$/.match(l.text) }.each do |link|
unless visited_list_pages.include? link.text.to_i
visited_list_pages << link.text.to_i
visit_list(link, visited_list_pages, subjects)
end
end
threads.each(&:join)
end
# Completa el formulario para loggearse en SGA usando un usuario y contrasena
def login(user_data)
agent = Mechanize.new
page = agent.get('http://sga.itba.edu.ar/')
login_form = page.form
login_form.user = user_data[0]
login_form.password = user_data[1]
login_form.js = 1
login_form.submit
end
# Extrae la informacion necesaria de SGA para crear una lista de materias con sus horarios.
def scrap_sga(user_data)
subjects = []
visited_list_pages = []
begin
page = login(user_data)
first_list_link = page.links.find { |l| l.text == 'Cursos' }
visited_list_pages << 1
visit_list(first_list_link, visited_list_pages, subjects)
save_data(subjects)
rescue StandardError
puts 'No se pudo acceder a SGA, verifique la validez de sus credenciales o su conexion a internet.'
end
subjects
end
subjects = []
if ARGV.size < 2
puts 'Usuario y contrasena de SGA no ingresados'
exit
end
case ARGV[2]
when 'scrap'
subjects = scrap_sga(ARGV)
when 'recover'
subjects = recover_data
else
puts "Ingrese 'scrap' o 'recover' para buscar informacion en sga o en la base de datos respectivamente"
exit
end
puts subjects.size