ruby + nokogiri实现将天涯易读全帖转换成txt文件的功能

YiduFreeTxt 0.1beta版发布

天涯易读网站原本是有提供下载全帖txt版本的功能的,但是该功能需要易读积分,这对于从来不登陆易读的笔者来说,无疑是一件不可能完成的任务。

于是随手写了个免费将易读全贴转换成txt文件的小工具,一来自娱自乐,二来献给老婆。因为老婆最近都在易读追帖,一天花掉30M的流量,让亲者痛,仇者快(好吧,我是亲者,移动是仇者)。

自从有了YiduFreeTxt,哪里要看点哪里,一键转成txt,老公再也不用担心我的流量了。

一些必要的说明

YiduFreeTxt使用ruby192开发,所以没有安装ruby的同学,或者ruby版本不符的同学可能没有办法进行试用。

YiduFreeTxt使用nokogiri库进行html的解析,请确保你的本地gem安装了nokogiri扩展。若没有,请输入命令 gem install nokogiri。

由于作者的不勤奋及时间关系,YiduFreeTxt要求大家输入天涯易读帖子url中的article id,不是直接输入url。举例来说,下面这个帖子

http://tianyayidu.com/article-a-102005.html中的102005就是该帖子的article id,使用时请注意。

关于代码

这里贴出该工具的相关代码,供有兴趣的同学研究。为什么没有一行注释?没办法,作者太懒,什么注释都没留下。

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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
require 'rubygems'
 
require 'nokogiri'
 
require 'open-uri'
 
require 'logger'
 
class String
 
    def br_to_new_line
 
        self.gsub('<br>', "\n")
 
    end
 
 
 
    def strip_tag
 
        self.gsub(%r[<[^>]*>], '')
 
    end
 
 
 
end #String
 
module YiDu
 
    class UrlBuilder
 
        attr_reader :domain, :id, :article
 
        attr_reader :end_type
 
        def initialize id
 
            @domain = %q[http://tianyayidu.com/]
 
            @article = 'article'
 
            @end_type = '.html'
 
            @id = id.to_s
 
        end    
 
 
 
        def article_url
 
            @domain + @article + '-'+ id + @end_type
 
        end #article_url       
 
 
 
        def build_article_url page
 
            page = page.to_s
 
            "#{@domain}#{@article}-#{@id}-#{page+@end_type}"
 
        end #build_article_url     
 
    end #UrlBuilder
 
 
 
    class ContentWorker
 
        attr_reader :url, :doc, :retry_time
 
        attr_accessor :page_css, :content_css
 
 
 
        class << self
 
            def log=(log_file)
 
                @@log = log_file
 
            end #log=
 
 
 
            def log
 
                @@log
 
            end
 
        end #class
 
 
 
        def initialize url
 
            @url = url
 
            define_max_retry_time
 
            define_page_css
 
            define_content_css
 
            get_nokogiri_doc
 
            exit if @doc.nil?
 
            log_or_output_info
 
        end #initialize    
 
 
 
        def log_or_output_info
 
            msg = "processing #{@url}"
 
            if @@log
 
                @@log.debug msg
 
            else
 
                puts msg
 
            end #if
 
        end #log_or_output_info
 
 
 
        def get_nokogiri_doc
 
            times = 0
 
            begin
 
                @doc = Nokogiri::HTML(open(@url).read.strip)
 
            rescue
 
                @@log.error "Can Not Open [#{@url}]" if @@log
 
                times += 1
 
                retry if(times < @retry_time)
 
            end #begin
 
        end #get_nokogiri_doc
 
 
 
        def define_max_retry_time
 
            @retry_time = 3
 
        end #define_max_retry_time
 
 
 
        def define_page_css
 
            @page_css = %q[div.pageNum2]
 
        end
 
 
 
        def define_content_css
 
            @content_css = %q[li.at.c.h2]
 
        end #define_content_css
 
 
 
        def total_page
 
            page = ''
 
            doc.css(page_css).each do |p|
 
                m = p.content.match(/\d+/)             
 
                page = m[0] if m                               
 
            end #each
 
            page.to_i
 
        end #total_page
 
 
 
        def build_content &blk
 
            @doc.css(@content_css).each do |li|
 
                if block_given?
 
                    blk.call(li.to_html.br_to_new_line.strip_tag)
 
                else
 
                    puts li.to_html.br_to_new_line.strip_tag
 
                end #if
 
            end #each
 
        end #build_content
 
 
 
    end #ContentWorker
 
 
 
    class IoFactory
 
        attr_reader :file
 
        def self.init file
 
            @file = file
 
            if @file.nil?
 
                puts 'Can Not Init File To Write'
 
                exit
 
            end #if
 
            File.open @file, 'a'
 
        end    
 
    end #IoFactory
 
 
 
    class Runner       
 
            attr_reader :url_builder, :start_url
 
            attr_reader :total_page, :file_to_write
 
 
 
            def initialize id
 
                init_logger
 
                @url_builder = UrlBuilder.new(id)              
 
                get_start_url
 
                get_total_page
 
                create_file_to_write id            
 
                output_content
 
            end #initialize
 
 
 
            def self.go(id)
 
                self.new(id)
 
            end
 
 
 
            def create_file_to_write id
 
                file_path = File.join('.', id.to_s.concat('.txt'))
 
                @file_to_write = IoFactory.init(file_path)
 
            end #create_file_to_write
 
 
 
            def init_logger
 
                logger_file = IoFactory.init('./log.txt')
 
                logger = Logger.new logger_file
 
                ContentWorker.log = logger
 
            end #init_logger
 
 
 
            def get_start_url              
 
                @start_url = @url_builder.article_url
 
            end #get_start_url
 
 
 
            def get_total_page
 
                @total_page = ContentWorker.new(@start_url).total_page
 
                if @total_page.nil?
 
                    puts 'Can not get total page'
 
                    exit
 
                end #if
 
            end # get_total_page
 
 
 
            def output_content             
 
                @total_page.times do |part|
 
                    a_url = @url_builder.build_article_url(part+1)
 
                    ContentWorker.new(a_url).build_content do |c|
 
                        @file_to_write.puts c
 
                        @file_to_write.puts '*' * 40
 
                    end # build_content
 
                end #times
 
            end #output_content
 
 
 
    end #Runner
 
end #YiDu
 
 
 
include YiDu
 
id = 102005
 
Runner.go id

代码结构分析

为了帮助大家学习ruby,小弟还是画蛇添足的分析一下代码好了。

YiduFreeTxt主要由3个模块构成:UrlBuilder,ContentWorker和Runner。

  • UrlBuilder主要用来生成易读全贴各个分页的url及首页的url;

  • ContentWorker则负责使用nokogiri从html页面中拿到帖子的所有分页数和每个分页的主体内容;

  • Runner的作用是协调UrlBuilder和ContentWorker,使其协同工作,并将获取的内容写入文件;

代码亮点

写的很烂,没啥亮点,唯一有点成就感的就是build_content方法可以将&blk传入block,这点以前没有注意到。

版权

未经许可,也可转载。

扩展

写了个看似没啥作用的IoFactory实际上是考虑到以后的扩展性,如果需要把内容输出到pdf文件的话,那么只需要继承IoFactory,并使其返回的文件句柄响应puts方法既可,算是实现了一个丑陋的Adapter模式。

posted @   乙醇  阅读(1870)  评论(1编辑  收藏  举报
编辑推荐:
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
阅读排行:
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· 程序员常用高效实用工具推荐,办公效率提升利器!
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 【译】WinForms:分析一下(我用 Visual Basic 写的)

友情链接 虫师的blog  测试教程网  重定向科技  省略

点击右上角即可分享
微信分享提示