Python 实现 CNKI批量下载 和FireFox Extension 入门学习笔记
由于需要也是为了督促自己学习新的东西,我原本想要尝试着写一个爬虫程序,能够在cnki上自动得将论文进行批量下载,学习过程中遇到了诸多情况,cnki也真是专业,不得不佩服cnki的强大。
下面进入正题:
学习、实验环境:ubuntu 14.04
工具:Eclipse , FireFox, FireBug,HttpFox
编程语言:python, js, XUL
由于我是中山大学学生,使用校园网进入cnki进行下载是不用登录的,所以我也就没进行模拟登录等方面的实现,有需要的朋友可以使用splinter进行模拟登录,也算快速便捷哈。
首先我们要实现使用python对cnki的论文实现批量下载,我们首先要对客户端和服务器之间相互传递的信息进行了解,这里我第一次使用的是wireshark,但是这个工具虽然强大,但是真不利于我们进行分析,它抓取的是我们电脑端口的信息,而不能只抓取当前网页的,在多次尝试之后我选择了HttpFox进行数据包抓取:
如图我们可以发现cnki对一个下载进行了3此redirect,我们通过对获得的数据和再一次发送的数据对比可以发现:前一次获得的url就是下一次get的url,所以我们就可以写出下载部分的核心代码,只有一点点是自己写的,许多网上一搜就有。
# -*- coding: UTF-8 -*- import sys import urllib2 import cookielib import string import re import time cj = cookielib.CookieJar() cookie_support = urllib2.HTTPCookieProcessor(cj) opener = urllib2.build_opener(cookie_support, urllib2.HTTPHandler) urllib2.install_opener(opener) url = 'http://epub.cnki.net/kns/download.aspx?filename=MBnRwcDMudGaItiamtScHVzb2Y0ZrVWRCBjaykEVXRTOjV0QHxUcTh3KNFXbHBDTyQVMNR2cP9kYGh1SXZnQnp3b3I2YqBnbUJ3KSNnRvpEUwhWTVJjNKBDcXVnSmZUayMkRVJUdytCe1AjT1ImTYdWdth1RtlGb&tablename=CMFDLAST2013' h = { 'Referer' : url, 'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:30.0) Gecko/20100101 Firefox/30.0' } r = urllib2.urlopen(urllib2.Request(url,headers = h)) r = urllib2.urlopen(urllib2.Request(r.geturl(),headers = h)) r = urllib2.urlopen(urllib2.Request(r.geturl(),headers = h)) r = urllib2.urlopen(urllib2.Request(r.geturl(),headers = h)) data = r.read() print data
使用FireBug获取网页源代码,随意找几个下载图标的链接url粘贴到代码中试一试成功了。
下载部分的代码成功了,那下面就是,如何从cnki的网页中获取到下载的url了,在这部分的实现中,我原本想要使用纯python的实现,使用python写一个爬虫,直接上cnki进行抓取,但是具体实现的时候碰到了很大的问题,例如:1、爬虫网页动态生成的数据困难重重;2、cnki网页在浏览器显示的url一直都是:”http://epub.cnki.net/kns/brief/default_result.aspx”;3、我们需要的网页是放在一个iframe标签里面的,这就非常蛋疼了,一直没找到获取办法
在同学的提示下,我发现了也许FireFox Extension是可行的解决方案。
学习FireFox Extension的话首先需要能至少看懂XUL代码,然后基本的模范着写,然后呢,就得学会javascript了,当然也有不用js实现的插件,例如使用python写,那使用PyXPCOM,但是我还没学会,这里页不需要哈。
(一)FF extension总体认知
FF是基于mozilla内核的,其浏览器引擎功能主要由C语言实现,但其浏览器界面及其界面上的相关操作都是由JS和XUL完成的,这就是FF的强大之处,同时它也为大家提供了很好的扩展机制。
FF扩展的开发目录结构:
可以到官网上仔细看看。
FF的工作方式:
对于任何一种新的开发语言或环境,大家通常都试图去寻找程序的入口点。可以很负责任地跟大家说,任何编译型(解释型程序)开发语言的程序都有类似C中的Main函数、Java中的主类(public static void main函数)、C#中的入口函数(static void/int Main)等作为程序入口。
这里我们呢了解这些也就够了。
这里我的目录结构是:
--chrome
--content
--overlay.js
--sample.xul
--chrome.manifest
--install.rdf
其中install.rdf代码为:
<?xml version="1.0"?> <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#"> <Description about="urn:mozilla:install-manifest"> <em:id>[email protected]</em:id> <em:version>1.0</em:version> <em:type>2</em:type> <!-- Target Application this extension can install into, with minimum and maximum supported versions. --> <em:targetApplication> <Description> <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <em:minVersion>1.5</em:minVersion> <em:maxVersion>4.0.*</em:maxVersion> </Description> </em:targetApplication> <!-- Front End MetaData --> <em:name>DownLoadPaper</em:name> <em:description>A test extension</em:description> <em:creator>Liang</em:creator> <em:homepageURL>http://www.example.com/</em:homepageURL> </Description> </RDF>
chrome.manifest代码为
overlay chrome://browser/content/browser.xul chrome://sample/content/sample.xul content sample chrome/content/
overlay.js代码为:
function getURL() { if ( window.content.document.location.href != " { alert("Worng address"); } else { var myWin = window.content.document.getElementById('iframeResult').contentWindow; var hre = myWin.document.getElementsByClassName('brief_downloadIcon'); var path = prompt("save address","eg. //****/****"); path = path + "/test.csv"; try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); } catch (e) { alert("no permisson..."); } var file = Components.classes["@mozilla.org/file/local;1"] .createInstance(Components.interfaces.nsILocalFile); file.initWithPath(path); if ( file.exists() == false ) { file.create( Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 420 ); } var outputStream = Components.classes["@mozilla.org/network/file-output-stream;1"] .createInstance( Components.interfaces.nsIFileOutputStream ); outputStream.init( file, 0x04 | 0x08 | 0x20, 420, 0 ); var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"] .createInstance(Components.interfaces.nsIScriptableUnicodeConverter); for (var i = 0; i < hre.length; i++) { converter.charset = 'UTF-8'; var convSource = converter.ConvertFromUnicode(hre[i].href+"\n"); var result = outputStream.write( convSource, convSource.length ); } outputStream.close(); alert("File was saved in "+path); } }
sample.xul代码如下:
<?xml version="1.0"?> <overlay id="sample" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <script type="application/x-javascript" src="overlay.js"/> <menupopup id="menu_ToolsPopup"> <menuitem id="DownLoadPaper" label="DownLoadPaper" oncommand="getURL();"/> </menupopup> </overlay>
至此,这个粗糙的辅助插件就编写完了,将文件打包压缩成zip格式,然后改名后缀为.xpi,再拖入FF就能安装了,这里需要修改一下FF的权限,使得插件能够修改本地文件,这样,工具栏里会多出一个工具,点击就能把论文下载的URL下载到一个test.csv的文件里了,windows下的用户就不要存到C盘里哈。
完整python文件为:
# -*- coding: UTF-8 -*- import sys import urllib2 import cookielib import string import re import time f = open('test.csv', 'r') i = 1 for line in f: print line try: cj = cookielib.CookieJar() cookie_support = urllib2.HTTPCookieProcessor(cj) opener = urllib2.build_opener(cookie_support, urllib2.HTTPHandler) urllib2.install_opener(opener) url = line refer = 'http://epub.cnki.net/kns/brief/brief.aspx?pagename=ASP.brief_default_result_aspx&dbPrefix=SCDB&dbCatalog=%e4%b8%ad%e5%9b%bd%e5%ad%a6%e6%9c%af%e6%96%87%e7%8c%ae%e7%bd%91%e7%bb%9c%e5%87%ba%e7%89%88%e6%80%bb%e5%ba%93&ConfigFile=SCDBINDEX.xml&research=off&t=1431252221059&keyValue=python&S=1' h = { 'Referer' : refer, 'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:30.0) Gecko/20100101 Firefox/30.0' } r = urllib2.urlopen(urllib2.Request(url,headers = h)) r = urllib2.urlopen(urllib2.Request(r.geturl(),headers = h)) r = urllib2.urlopen(urllib2.Request(r.geturl(),headers = h)) r = urllib2.urlopen(urllib2.Request(r.geturl(),headers = h)) data = r.read() if data[:8] == '[TARGET]': with open(str(i)+".caa", "wb") as up: up.write(data) up.close else: with open(str(i)+".caj", "wb") as up: up.write(data) up.close print str(i)+' '+'succeed' time.sleep(2) except: print str(i)+' '+'failed' i = i + 1
这样,将test.csv与python文件放于相同目录下,运行python文件就可以下载了。