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模式。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· 程序员常用高效实用工具推荐,办公效率提升利器!
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 【译】WinForms:分析一下(我用 Visual Basic 写的)