无忧启动论坛

标题: 用批处理整理读览天下网站中部分杂志的精选文章 [打印本页]

作者: namejm    时间: 2011-5-7 16:22
标题: 用批处理整理读览天下网站中部分杂志的精选文章
  最近用批处理抓取/整理网络数据成瘾,应网友的请求,写了个整理读览天下(http://www.dooland.com)网站中部分杂志精选文章的代码,在本人机器上跑了几天,测试后较为满意,发出来与大家一起分享。至于txt文本的用途,大家自行发掘,不在本文的讨论范畴。

  与写教程相比,写代码是件比较惬意的事情。

  写代码,我可以天马行空胡思乱想,不求他人看得懂,惟愿功能可实现。写教程,还得考虑广大受众的技术水平:写简洁了,老鸟们会连声叫好,小鸟们会不知所云;写详细了,老鸟们就觉得长不可耐,小鸟们可能会觉得正中下怀,但也不见得就能解决了所有的疑惑。对我而言,写短了,任务轻松,但会觉得有点失败,因为肯定有大部分人看不懂;写长了,还得费尽心思在何处尽力着墨,在何处可以大大俭省,还得考虑语言通俗易懂、生动有趣……无论是长是短,都会有人痛不欲生。

  我非地藏菩萨,没有“我不入地狱谁入地狱”的大无畏精神,只能“牺牲大多数,幸福我一人”,在此处只能简要地提一提这段代码的前世今生了。

  最开始,该网友想下载所有杂志的文字版本。

  进网站四处指指戳戳,折腾了一圈,发现每种杂志都提供了图片预览,但并不是所有的杂志都提供文字版本,只有为数不多的杂志才有“精选文章”——即使是文字版,也不是全部的文字,而是经过了“精选”的;即使提供了“精选文章”的杂志,也不是都可以用本文所提到的代码来下载,需要做一些微调方可,比如《意林》杂志。

  既然不能抓取全部的文字版,那退而求其次,就抓“精选文章”吧。

  理想中的状态是:在某个地方,找到所有提供了“精选文章”的杂志名列表网址,然后顺藤摸瓜,进入每一本杂志的历年列表网址中,从每一期的“精选文章”中抓取具体文章的网页文件,最后,对这些网页文件做去除html代码、提取文章正文内容等一系列处理,得到“精选文章”文字版。

  所有操作的关键,就是要找到各种网页的真实下载地址。

  那就开始摸索吧。

  首先,要寻找有哪些杂志提供了“精选文章”。

  这是个体力活,没有任何技巧可言,能否找全,不需要人品,也不需要智商,只需要耐心,一个一个地点开来看吧。要担心找漏或找重复了,就在网站首页的左侧,找到“杂志分类”,一个类别一个类别地点开来找吧。想当初,我花了两晚上,终于把提供“精选文章”的杂志找全了,度过了一个因枯燥乏味而令人难以忘怀的5.1劳动节。如果你不想再次重复这个乏味的过程,那就直接使用我整理出来的杂志列表吧。一个额外的收获是:这个网站的分类真的不咋地,有的重复了,有的分错了——您说“计算机世界”是不是应该划到“教育科技”下的“计算机”分类里去?但是这个网站它偏不,它就有胆量给划到“商业财经”的“互联网”中去,对我这种出于职业习惯而喜欢对万事万物进行分类的人而言,这是个令人无法容忍的错误,不过希望大家见了之后置之一笑即可,我可不希望大家都像我这样是个偏执狂^_^。

  其次,对提供了“精选文章”的杂志,需要列举每一期的网址。

  以《三联生活周刊》为例。在读览天下网站首页右上角处,搜索框中搜索“三联”字样,在候选框中选择“三联生活周刊”,点搜索按钮,来到《三联生活周刊》历年各期列表页面,网址为:http://lifeweeker.dooland.com。这个列表有两个页面,点“下一页”按钮,来到第二页,注意观察地址栏里网址的变化,已经变为:http://lifeweeker.dooland.com/index.php?p=2&id=7990。点“前一页”按钮返回第一页,网址已经变为:http://lifeweeker.dooland.com/index.php?p=1&id=7990。稍作比较,很快就会发现页码和网址的对应关系:“index.php?p=”后及“&”之前的数字就是页码号码。如果把网址栏中网址的“&id=7990”去掉,再按回车,当前页面不会发生任何变化。由此可以推知,这个id号应当是这个杂志在网站数据库中的流水编号,在网址中是否有它都不会影响网页查询结果。而到了最后一页,网页中不再出现“下一也”按钮。换成其他的杂志来测试,可以证明上述推测是正确的。

  既然是这样,那么,如果我们把这些杂志的首页地址整理出来了,要找到该杂志所有期别的下载链接,当手到擒来。

  那就用代码来验证一下以上的推测过程吧。

  1.下载《三联生活周刊》各期网址链接页面的首页:
  1. wget -O 1.html "http://lifeweeker.dooland.com/index.php?p=1"
复制代码

  2.检测当前下载的页面是不是最后一页;如果不是最后一页,则继续下载下一页:
  1. :loop
  2. set /a page+=1
  3. findstr /i "index\.php\?p=.*下一页" %page%.html >nul&&(
  4.     wget -O %page%.html "http://lifeweeker.dooland.com/index.php?p=%page%"
  5.     goto loop
  6. )
复制代码

  期待中的第2页网页文件2.html并没有出现。

  赶紧排查,原来下载回来的html文件是utf-8编码,而findstr无法读取,需要把html文件转为ASCII编码才行。

  找来白杨大侠写的文件编码转换工具wfr.exe,修改一下代码:
  1. :loop
  2. set /a page+=1
  3. wfr %page%.html /any /force /encin:utf-8 /encout:gbk>nul
  4. findstr /i "index\.php\?p=.*下一页" %page%.html >nul&&(
  5.     wget -O %page%.html "http://lifeweeker.dooland.com/index.php?p=%page%"
  6.     goto loop
  7. )
复制代码

  从头到尾把这本杂志各期列表网页都下载回来了。

  分析这些网页,找到各期的具体网址(以1.html里的链接为例):
  1. setlocal enabledelayedexpansion
  2. for /f "tokens=3,6 delims== " %%i in ('findstr /ib "<a.href=.http:\/\/www\.dooland\.com\/magazine.*三联生活周刊" 1.html') do (
  3.     set issue="%%k
  4.     set "issue=!issue:~1,-1!"
  5.     md html\!issue! 2>nul
  6.     wget -nv -O html\!issue!\%%~ni %%i
  7. )
复制代码

  每一期都会在html文件夹下建立以当前期别为名的文件夹,里面存放一个无后缀名的文本格式的文件,比如《三联生活周刊》11年第18期,它的存放路径是:html\11年第18期\21422——实际上,21422是一个html网页文件,只是保存的时候没有给它添加后缀名,以便和后来下载回来的文章网页文件区分开来。

  代码验证完毕,接下来就得下载具体文章了。

  从每一期的文章目录页中读取文章的具体链接下载之(以上一例的21422文件为例):
  1. for /f "tokens=3 delims== " %%i in ('findstr /ib "<h2>.*article_.*title" html\11年第18期\21422') do (
  2.     wget -nv -O html\11年第18期\%%~ni.html "http://www.dooland.com/magazine/%%~i"
  3. )
复制代码

  下载回来的文件都保存在“html\11年第18期”下,文件名形如article_127096.html、article_127097.html。

  先把网页文件中的html代码都剔除掉,转换为txt文本,并继续用wfr.exe来改变文件的编码:
  1. htox32c /IP /O0 html\11年第18期\*.html
  2. wfr html\11年第18期\*.txt /any /force /encin:utf-8 /encout:gbk
复制代码

  经过html→txt、utf-8→gbk的转换之后,正文内容方可被正确提取出来(关键中的关键:文章标题含有“TITLE : ”字样,正文内容都以两个全角的空格打头):
  1. md txt 2>nul
  2. (for %%i in (html\11年第18期\*.txt) do (
  3.     (
  4.     for /f "delims=: tokens=2*" %%j in ('findstr /ic:"TITLE : 三联生活周刊:" %%i') do (
  5.         echo %%k&echo.
  6.     )
  7.     findstr /ib "  " %%i
  8.     echo.)
  9. ))>txt\11年第18期.txt
复制代码


  回头梳理一下完整的整理流程:

  1.整理一份配置表,里面包含杂志名和历年各期列表网址的首页;比如“三联生活周刊 http://lifeweeker.dooland.com/index.php
  2.下载上一步整理出来的首页文件,保存为无后缀名的文件1;
  3.根据文件1中是否含有“下一页”按钮,来决定是否下载后一页,直至所有的页面都下载完毕;这些页面文件,我们暂且称之为:索引页。
  4.根据索引页中的链接,提取出该杂志每一期的期别名和具体网址,并下载这一期别的首页,我们暂且将这样的网页文件称之为为:目录页。
  5.从目录页中,提取出每一篇“精选文章”的下载链接,并下载它们的网页文件;
  6.把“精选文章”的网页文件经过html→txt和utf-8→gbk的转换之后,提取出正文内容,保存到txt文件夹下,并以该期别名命名这个txt文件。
作者: namejm    时间: 2011-5-7 16:23
完整代码(需配合附件里的内容方可使用,请看文末的注意事项)
  1. @echo off
  2. title 读览天下网站部分杂志精选文章整理脚本
  3. setlocal enabledelayedexpansion
  4. :: 网址严格区分大小写
  5. :: 即使没有账号密码,完整的正文内容仍然被嵌在网页源文件中,只不过采用css限制了完整内容的阅览
  6. :: 所以,无需使用账号即可阅览完整的精选文章
  7. set www=http://www.dooland.com/magazine

  8. :Main
  9. cls
  10. title 读览天下杂志精选文章下载脚本
  11. echo.&echo.
  12. echo     选择每个类别前的数字序号
  13. echo     将进入该类别的具体分类进行指定杂志精选文章的下载
  14. echo     每次只能选择一个类别
  15. echo     请勿输入错误的格式,否则,将引发不可预知的错误
  16. echo.
  17. echo ==============================================================
  18. echo.
  19. set num=0
  20. set ConfigDir=config
  21. for %%i in (%ConfigDir%\*.txt) do (
  22.     set /a num+=1
  23.     set /a mod=!num!%%3
  24.     set /p=!num!.%%~ni  <nul
  25.     if !mod! equ 0 echo.&echo.
  26. )
  27. echo.
  28. echo ==============================================================
  29. echo.
  30. set ClassID=
  31. set /p ClassID=     请选择类别(1/2/3/……):
  32. if not defined ClassID goto Main
  33. set Class=
  34. set ClassConfig=
  35. set num=0
  36. for %%i in (%ConfigDir%\*.txt) do (
  37.     set /a num+=1
  38.     if "!num!"=="%ClassID%" (
  39.         set Class=%%~ni
  40.         set ClassConfig=%%i
  41.     )
  42. )

  43. cls
  44. title 准备整理 %Class% 分类下的精选文章
  45. echo.
  46. echo     选择每个项目前的数字编号
  47. echo     可以下载该杂志所有期别的精选文章
  48. echo     可以多选,但是必须以空格分隔
  49. echo     多选时不用考虑先后次序
  50. echo     0与其他选项组合时会造成重复下载
  51. echo     已经整理过的期别不会重复整理(即保留整理进度)
  52. echo     请勿输入错误的格式,否则会引发不可预知的错误
  53. echo.
  54. echo ==============================================================
  55. echo.
  56. set num=0
  57. for /f "tokens=1-3" %%i in (%ClassConfig%) do (
  58.     set /a num+=1
  59.     set /a mod=!num!%%3
  60.     if !num! leq 9 (
  61.         set /p=%%i.%%j  <nul
  62.         if !mod! equ 0 echo.&echo.
  63.     )
  64. )
  65. if %num% gtr 9 (
  66.     echo.
  67.     echo     更多杂志的编号,请按照打开的文本文件中的内容进行输入
  68.     start "" %ClassConfig%
  69. )
  70. echo.&echo.
  71. set /p=  0.前面的所有杂志     返回上一步请直接回车<nul
  72. echo.
  73. echo ==============================================================
  74. echo.
  75. set MagazineID=
  76. set /p MagazineID=     请输入选择代码(0/1/2/3……):
  77. if not defined MagazineID goto Main
  78. for %%i in (%MagazineID%) do (
  79.     for /f "tokens=1-3" %%j in (%ClassConfig%) do (
  80.         set Magazine=%%k
  81.         set UrlIndex=%%l
  82.         if "%%i"=="0" (
  83.             call :DownArticle
  84.         ) else (
  85.             if "%%i"=="%%j" (
  86.                 call :DownArticle
  87.             )
  88.         )
  89.     )
  90. )
  91. pause
  92. goto Main

  93. :DownArticle
  94. cls
  95. md %Class%\%Magazine%\html 2>nul
  96. del /a /f /q %Class%\%Magazine%\html\*.* 2>nul
  97. set page=0
  98. :DownPages
  99. set /a page+=1
  100. title 正在下载 《%Magazine%》 期别索引页中的第 %page% 页
  101. wget -nv -O %Class%\%Magazine%\html\%page% "%UrlIndex%?p=%page%"
  102. :: 下载所有的索引页
  103. wfr %Class%\%Magazine%\html\%page% /any /force /encin:utf-8 /encout:gbk>nul
  104. findstr /i "index\.php\?p=.*下一页" "%Class%\%Magazine%\html\%page%">nul&&(
  105.     goto DownPages
  106. )
  107. :: 需要防止文章标题中含空格的情况
  108. :: 已经生成了txt的期别不再下载
  109. :: 如果在期别一栏没有数据,则忽略该期的下载,否则,脚本将无法运行下去
  110. for %%i in (%Class%\%Magazine%\html\*.*) do (
  111.     for /f "tokens=3,6 delims== " %%j in ('findstr /ib "<h1><a.href=.http:\/\/www\.dooland\.com\/magazine.*%Magazine%" %%i') do (
  112.         set issue="%%k
  113.         set "issue=!issue:~1,-1!"
  114.         if defined issue (
  115.             md %Class%\%Magazine%\html\!issue! 2>nul
  116.             if not exist %Class%\!Magazine!\txt\!issue!.txt (
  117.                 del /a /f /q %Class%\%Magazine%\html\!issue! 2>nul
  118.                 title 正在下载 《%Magazine%》!issue! 的文章列表页面
  119.                 wget -nv -O %Class%\%Magazine%\html\!issue!\%%~nj %%j
  120.                 wfr %Class%\%Magazine%\html\!issue!\%%~nj /any /force /encin:utf-8 /encout:gbk
  121.                 call :GetHtml !issue! %Class%\%Magazine%\html\!issue!\%%~nj
  122.                 if defined article call :GetTxt !issue!
  123.             )
  124.         )
  125.     )
  126.     for /f "tokens=3,6 delims== " %%j in ('findstr /ib "<a.href=.http:\/\/www\.dooland\.com\/magazine.*%Magazine%" %%i') do (
  127.         set issue="%%k
  128.         set "issue=!issue:~1,-1!"
  129.         if defined issue (
  130.             md %Class%\%Magazine%\html\!issue! 2>nul
  131.             if not exist %Class%\!Magazine!\txt\!issue!.txt (
  132.                 del /a /f /q %Class%\%Magazine%\html\!issue! 2>nul
  133.                 title 正在下载 《%Magazine%》!issue! 的文章列表页面
  134.                 wget -nv -O %Class%\%Magazine%\html\!issue!\%%~nj %%j
  135.                 wfr %Class%\%Magazine%\html\!issue!\%%~nj /any /force /encin:utf-8 /encout:gbk
  136.                 call :GetHtml !issue! %Class%\%Magazine%\html\!issue!\%%~nj
  137.                 if defined article call :GetTxt !issue!
  138.             )
  139.         )
  140.     )
  141. )
  142. :: 若需要保留网页文件,请把下一条语句注释掉或删掉
  143. rd /q /s %Class%\%Magazine%\html 2>nul

  144. cls
  145. title 处理完毕
  146. echo 处理完毕
  147. goto :eof

  148. :GetHtml
  149. cls
  150. title 正在下载 《%Magazine%》%issue% 的精选文章html数据
  151. set "article="
  152. for /f "tokens=3 delims== " %%i in ('findstr /ib "<h2>.*article_.*title" %2') do (
  153.     set article=exist
  154.     wget -nv -O %Class%\%Magazine%\html\%issue%\%%~ni.html "%www%/%%~i"
  155. )
  156. goto :eof

  157. :GetTxt
  158. cls
  159. title 正在转换 《%Magazine%》%issue% 的数据格式
  160. htox32c /ip /o0 %Class%\%Magazine%\html\%issue%\*.html
  161. wfr %Class%\%Magazine%\html\%issue%\*.txt /any /force /encin:utf-8 /encout:gbk

  162. cls
  163. title 正在生成 《%Magazine%》%issue% 的文本文件
  164. echo.&echo     正在生成 《%Magazine%》%issue% 的文本文件
  165. echo.&echo     请稍候...
  166. md %Class%\%Magazine%\txt 2>nul
  167. (for %%i in (%Class%\%Magazine%\html\%issue%\*.txt) do (
  168.     (
  169.     for /f "delims=: tokens=2*" %%j in ('findstr /ic:"TITLE : %Magazine%:" %%i') do (
  170.         echo %%k&echo.
  171.     )
  172.     findstr /ib "  " %%i
  173.     echo.)
  174. ))>%Class%\%Magazine%\txt\%issue%.txt
  175. del /a /f /q %Class%\%Magazine%\html\%issue%\*.txt 2>nul
  176. goto :eof
复制代码
配置文件内容示例(为了配合以上代码,配置文件必须放入指定目录下,请下载附件使用):
;以分号打头的行为注释行
;仅保存含有精选文章的杂志
;条目格式为:序号 杂志名 杂志索引首页
;字段之间以空格分隔
;杂志名称中不能含有不能作为计算机文件名的各种符号,如半角状态下的:、/、\、>、<、?等
;杂志名称中不能含有半角状态下的空格、等号、分号
;杂志名称必须与网页中的名称保持一致,不得从杂志的封面中获取,否则,将下载不到文章内容

;整理时间 2011.4.30

;科普
1 轻兵器 http://qbq.dooland.com/index.php
2 百科知识 http://bkzs.dooland.com/index.php

;科教
3 大学明天 http://dxmt.dooland.com/index.php
  
注意事项:

  1.本代码需要wget.exe、wfr.exe和HtoX32c.exe的支持:wget下载网页、wfr对文件编码进行转换、HtoX32c转html为txt;附件已经包含所需的全部文件;
  2.配置文件必须严格按照其中的说明进行整理,修改配置文件的路径必须在代码中做相应的更改;
  3.读览天下网站中的“精选文章”,无需账号密码也可下载完整内容——当某天这条不成立的时候,配合账号密码才能使用本代码。
  4.本文仅谈思路和原理,与读览天下网站类似的网站都可以用这个思路和原理来抓取文本。本人精力有限,不能针对众多网站一一编写代码,请自行修改代码来适应那些类似的网站。



[ 本帖最后由 namejm 于 2011-5-7 17:19 编辑 ]

分类下载读览天下的精选文章.rar

7.3 KB, 下载次数: 54, 下载积分: 无忧币 -2

wget.rar

317.57 KB, 下载次数: 69, 下载积分: 无忧币 -2

wfr.rar

183.42 KB, 下载次数: 71, 下载积分: 无忧币 -2

HtoX32c.rar

55.46 KB, 下载次数: 70, 下载积分: 无忧币 -2






欢迎光临 无忧启动论坛 (http://wuyou.net./) Powered by Discuz! X3.3