用PyQuery抓取图书信息建立自己的家庭图书馆
董立达
(济南市长清区供电公司 山东 济南 250001)
随着互联网的普及和数字设备的应用日益广泛,相信有很多朋友都喜欢利用手机、平板电脑等随身设备看书学习,那么样相应的书箱资源就成为一个让大家比较头疼的问题,毕竟没有那么多的时间去收集整理浩如烟海的书籍,而通过搜索引擎到的链接很多情况下都带有广告的性质,而且内容相当分散,让人花费了很多的时间,得到的效果却事倍功半。
在实际工作学习中,笔者也同样遇到这样的困惑。在搜集整理书籍的过程中,我发现了一个比较不错的网站(HTTP://),内容翔实,关键是网站的工作人员维护及时,更新快捷,某本书可以存在多个版本,且有不同的网友对排版质量进行评述,最重要的是还可以下载哦!尽管对于非VIP会员有一天三本书的下载限制,但基本可以满足个人需要。在使用该网站的过程中,笔者萌生了一个利用该站建立个人小型图书馆的想法。试想,如果把这个网站中图书资料的链接都“抓”回来的话,即便简单地使用Excel表格将它们集合起来,也可以大大地方便您个人书、看书的需求啊。
在实现这个想法的过程中,积累了一些经验,现在共享给书友们,读者朋友也可以根据同样的思路,继续扩展自己的“家庭图书馆”!
1概述
通常情况下,爬取网页后最主要的工作量都集中在对网页文件的解析过程,一般情况下都是用正则表达式或者是Beautiful4Soup控件包,而我在学习过程中发现了另外一个使用更简单易懂、效率也更高的工具包PyQuery,它几乎是全程模拟了jQuery的语法形式,更接近于大家平时制作网页的流程。而相对而言,PyQuery的相关资料,或者说深入应用的中文资料较少,那么就请读者跟随笔者的步伐,在这个小小的实践应用中体会一下PyQuery的魅力!
另外说一下我的试验环境是Ubuntu 16.04,使用的开发工具是Pycharm 2016.2最新版,Python的版本为v.3.5.1,相配套的工具包主要有Requests、PyQuery,另外还有将抓取结果写入Excel文件的工具包xlwt等。
2浏览器如何与网站“打交道”
HTTP协议(Hyper Text Transfer Protocol,超文本传输协议)是用于从WEB服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更加高效,使网络传输减少。HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型。HTTP是一个无状态的协议。
在用户访问网页时,不论是通过URL输入域名或IP,还是点击链接,浏览器向WEB服务器发出了一个H
TTP请求(HTTP Request),WEB服务器接收到客户端浏览器的请求之后,就会响应客户端的请求,并发回相应的响应信息(HTTP Response),然后浏览器通过解析、排版引擎分析返回的内容,并最终呈现在浏览器页面中。
WEB应用程序在于服务器交互的过程中,HTTP 请求和响应时发送的都是一个消息结构。这两种类型的消息都是由起始行、消息头、指示消息头结束的空行和可选的消息体组成。HTTP请求消息中,起始行包括请求方法、请求的资源、HTTP协议的版本号、消息头包含各种属性、消息体包含数据等。HTTP响应消息中,起始行包括HTTP协议版本、HTTP状态码和状态、消息头包含各种属性、消息体包含服务器返回的数据内容。
HTTP协议永远都是客户端发起请求,服务器回送响应。这样就限制了使用HTTP协议,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端。同时,由于HTTP协议是一个无状态的协议,那么同一个客户端的这次请求和上次请求是没有对应关系。为了解决这个问题,WEB程序引入了Cookie机制来维护状态,Cookie记录用户的登录状态,通常WEB服务器会
摘  要:互联网的普及和数字设备的广泛应用,利用手机、平板电脑等随身设备看书学习,过搜索引擎
到的相应书箱资源链接很多情况下都带有广告的性质,而且内容相当分散,让人花费了很多的时间,
得到的效果却事倍功半。笔者通过实践,谈了如何把不错网站中图书资料的链接都“抓”回来的话,简
单地使用Excel表格将它们集合起来,从而构建出一个实用性强的家庭图书馆,大大地方便个人书、看
书的需求。
关键词:PyQuery;图书信息;家庭图书馆
中图分类号:G251文献标识码:C文章编号:2096-1995(2016)23-0016-05
作者简介:董立达,计算机编程爱好者,高级程序员,曾于2003年6月由机械工业出版社出版《Delphi 7 Web开发与应用》一书。
- 16 -
在用户登录成功后,下发一个签名来标记Session(会话)的有效性,这样便免去用户多次认证和登录网站时记录用户的访问状态。
由此可以看出,要想取得某个网站的数据内容,那么模拟客户端登录是一个至关重要步骤,下面我们就一起来分析一下登录网站的过程。
3分析过程
是一个图书类网站,拥有大量的图书资源,而且最重要的是由于维护人员的负责,所有书籍都可以安全无忧的下载。当然,普通的注册用户有每天三本书的限制,而对于Pro用户,则无任何限制,相当于你拥有了几万本书的小型图书馆。
笔者使用Firefox浏览器,该浏览器自带一套分析工具Web Developer,在Firefox界面下,按下组合键Ctrl+Shift+Q即可打开这个工具,请见图
1:
图 1 Web开发者工具的初始页面
我们主要观测的项目是Web Developer的“网络”标签页,此页内容会记录下客户端与WEB服务器之间的所有通讯内容。下面点击右上角的“登录”按钮,会看到Web Developer的窗口将不断有内容滚动出来,请见图
2:
图 2 Web Developer的消息头
在图2当中,左侧的列表是你点击“登录”按钮之后,Firefox与远程的WEB服务器之间的通讯的记录,点击第一条记录,那么在右侧就会显示出相关的详情,分为消息头、Cookie、参数、响应、耗时及预览等六个项目,这里我们最关心的是消息头、Cookie和参数这三个选项页。
表格制作excel手机版下载在消息头选项页中,可以看到作为客户端的Firefox 发送给远程服务器的具体内容,如果我们用Python语言来模拟登录的话,那么想要骗过远程服务器,那么也需要你设置Python的发送信息,以便让远程服务器“认为”是某个浏览器发送了请求,否则的话,远程服务器会拒绝非浏览器发送的请求。
再来看一下参数选项页,见图
3:
图 3 Firefox发送的POST参数
在图3中,可以看到发送给远程服务器的数据有三
项,csrf通常称为跨站请求伪造,是一种防止对网站恶
意攻击的保护手段,详细解释请读者度娘。我们只需要
知道如果模拟登录的话,仅有email地址和password
口令字还是远远不够的,还需要有这样一个csrf令牌,
很明显后面是一个随机的字符串,我们要到哪里去这
个东东呢?很自然,你可能会想到第二选项页里面的“Cookie”,那么就来看看Cookie选项页里面有些什
么内容呢?请见图
4:
图 4 Cookie内容
在Cookie选项页里面,你发现了那个熟悉的
身影——csrftoken,此项内容与参数选项页中的csrfmiddlewaretoken是一样的,那么就让我们借助于Python的Requests库拿到这个内容就可以模拟出用户
登录了。
4具体实现
Python语言在互联网的世界里有着独特的优势,其
设计理念,尤其是其拥有的海量的工具包,都为学习者
进行网络开发工作打下了坚实的基础。在介绍具体的实
现过程之前,笔者先为大家介绍一下编码过程中用到的
两个重要的工具包:Requests、PyQuery。
4.1 Requests包
Requests包的宣言是HTTP for Humans,中文大
概的意思就是为地球人所使用的网络库,这话初听起来
似乎有点过于托大,但如果你用过其它的网络应用包之
后,再使用Requests的话,就知道其简捷、方便的程
序会有多大的优势了。使用这个工具包大体分为以下几
个步骤:
第一步,安装配置Requests库。如果安装有pip的话,
可以直接这样使用:
pip install requests
第二步,在应用程序中导入该包:
- 17 -
import requests
第三步,设置消息头headers及登录时用到的数据:login_data={‘email’: os.getenv(‘EMAIL’),
‘password’: os.getenv(‘PASSWORD’)}
在这里说明一下,一般为了安全的需要,不要将您的私人信息写到代码里面。可以利用系统的环境变量将密码等关键信息保存到您的开发机器上面。在系统环境下执行下面的代码即可:
export EMAIL=‘你的EMAIL登录帐户’
export password=‘你的登录口令’
当然,如果要想在Python中读取系统环境变量,还需要用到os工具包,请使用下面的命令导入之:import os
以上是关于登录信息的,那么仅有这些就够了么?答案是否定的!如果让远程的WEB服务器承认客户端的模拟登录,则还需要设置消息头,亦即在图2中显示的相关内容,可以直接从浏览器中复制过来:
headers = {
'Host': '',
'U s e r-A g e n t':'M o z i l l a/5.0(X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0',
'A c c e p t':'t e x t/h t m l,a p p l i c a t i o n/ xhtml+xml,application/xml;q=0.9,*/*;q=0.8',  'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'Referer': '/accounts/ signin/',
'Connection': 'keep-alive'
}
第四步,初始化requests的sessions,亦即“会话”对象:
session=requests.session()
第五步,利用requests包的post方法发送相关信息到远程的WEB服务器:
res = self.session.post(self.login_url, self.login_ data, headers=self.headers)
下面到了涉及登录过程中最关键的代码部分,res代表从远程服务器返回的对象,kies里面即存储着相关的cookies信息,需要将其包含的csrftoken信息写入到login_data字典中,然后再重新登录即可:self.login_data['csrfmiddlewaretoken'] = res. cookies['csrftoken']
res = self.session.post(self.login_url, data=self.login_data, headers=self.headers)
此返回的res内容即是正常登陆后得到的页面内容。
4.2 PyQuery包
模拟登录只是抓取图书信息的第一步,而更重要的工作在于分析网站的数据结构,并获取到所需要的有用信息。
先大体分析一下的结构,首先是一个页面列表,包含有该站最新更新的20本书,共计4行5列,最下端是不同的页面。因为在该站同一本书,不同的时期的上传会产生不同的版本,不同的版本也具有不同的分享、下载及评论数,此列表页面显示的数值为这些数据的累加值。评分来源于豆瓣图书,这些对于图书的选择还是具有较大的影响的。见图
5:
图 5 网站的图书列表
点击某张图片或者是图书名字的链接,就可以进入到该书的详情页面。详情页面主要包含Tag、类似书籍及其URL地址、各个不同的版本等信息,可根据自己的需要去抓取。见图
6:
图 6 图书的详情页面
- 18 -
图书的Tag算是其身份标签,可以根据此标签查到具有同类标签的其它书籍,下载数及评论一般可以了解此版本是否清晰等等内容。下面先介绍一下要使用的主要工具包PyQuery,然后结合PyQuery的特点分析一下这两个页面内容该如何抓取。
①PyQuery简介
简单而言,PyQuery是大名鼎鼎的jQuery的Python实现,语法使用上严格遵循jQuery规范,所以可用jQuery风格来遍历XML文档,基本上可以放弃正则表达式以及BeautifulSoup的悭吝难懂了。
第一步,当然是要安装此工具包:
pip install pyquery
第二,在程序中引用此工具包:
from pyquery import PyQuery as pq
然后在程序中初始化PyQuery,其参数则可加载一段HTML字符串,或一个HTML文件,或是一个url 地址,例:
doc = pq("<html><title>hello</title></ html>")
doc = pq(filename=path_to_html_file)
doc = pq(url='www.baidu')
其具体应用笔者将结合网页的解析,给大家详细解读。
②获取列表页面内容
为了更便捷的分析网页,通常情况下在Firefox 浏览器窗口右键单击→选择“查看网页源代码”,可获得当前网页的HTML源码;然后将这些代码复制到Python的IDE环境PyCharm中,并保存为list. html,便于后面解析HTML代码。见图
7:
图 7 图书列表页面解析
在PyCharm的HTML编辑器中,单击鼠标右键→Folding→Collapse All,将所有代码折叠,然后再逐层展开,可清楚地看到,88行的container标签是包含页面主要内容的容器,包括4行5列共计20本书的信息,而其后90行至128行的li标签则表示某本书的的信息。
这里面最重要的获取某本图书的URL,得到这个URL地址后才可以获取关于此书的更多详情,如图6所示,包括图书的简介、Tag标签、类似书籍及各种版
本的真正下载地址等相关内容。
选择P yC h ar m的菜单Vie w→T o o l Windows→Python Console,即可在PyCharm的
IDE环境中打开Python的交互式控制台窗口,键入以
下代码:
图 8 获取图书详情的URL地址
在这里用的是图7所示的94行的<a class="pjax" href="/book/25982198/">标签,您可能会发现这里
有个小问题,94行代表的封面图片,而99行代表的图
书名称,两者用了相同的标签也标示,键入:
图 9 图书列表项目的个数
在这里可以明显看出doc(‘a.pjax’)的条数为40条,
是20本书的两倍,那么如何详细地拿到封面图片的地
址URL和图书名称呢?见图
10:
图 10 获取封面图片地址及图书名称
这里利用了len方法首先获取所有a.pjax标签,然
后用for制作一个步长为2的循环,用eq(i)取得第一条
项目,用eq(i+1)取得第二条项目。注意一下,因为取
得第一条a.pjax之后若想取得其后的img标签的src属
性,则需添加(‘img’).attr(‘src’)语句即可。
对于封面图片的地址获取,当然也可以这样来实现,
见图
11:
图 11 获取封面图片地址
- 19 -
当然,我们也可以充分发挥Python语言的特性,直接通过items()获取所有.book-cover的标签,利用for循环通过方法attr拿到所有该集合中全部条目的图片地址,并形成list列表。
我们大体了解到PyQuery的几个常用的重要方法,items()是取得某个标签集合的条目,eq()是访问该集合中的某个条目,len()是取得集体中条目的长度,attr()是获取标签的某个属性值。
③获取单本书籍详情
经过对列表页面的分析,我们完全可以拿到某本书的详情页面,或者从Firefox中通过浏览网页源代码也可以看到HTML源码,按上面的处理步骤,同样在PyCharm中保存成详情页面文件“detail.html”,可用同样的方法分析一下。见图
12:
图 12 图书详情源代码结构分析
利用下面的代码获取书籍的简介,见图
13:
图 13 获取某本书的简介
split()方法将得到的简介文本按回车符分隔,然后对列表的每一条目去掉前后空格,让文本显示得更漂亮些。
我们定义一个base_url,用以指明当前的网站基本地址,获取相似的几本书籍的相关信息,见图
14:
图 14 获取相似书籍信息
请注意,如果想访问id标签,那么请使用前缀#号,如果想访问某标签的子标签,请直接在其后加标签名称即可,就如此例所示,想访问#similar-books下的li 标签,就直接写成doc(‘#similar-books’)(‘li’)。此处还直接将相似书籍的引用地址与书籍的标题组合成一个字典内容,便于以后对该数据的处理。
Tag的处理与上面非常类似,见图
15:
图 15 获取某本书的Tag标签列表
下面来看看如何取得一本书不同版本的下载信息,见图
16:
图 16 获取某一版本下载信息
可以发现<h4>标签表明了此书有4种不同的版本,点击292行行首的折叠标志,即可打开折叠的代码,然后再次点击293行,再次打开折叠代码,即可发现book-down标签的href属性表明了其下载的确切地址。再向下的form表示其提交评论,与我们讨论的内容相关不大,则不予考虑。代码见图
17:
图 17 获取不同版本的下载地址
利用attr()可以很容易地获得a.book-down标签集合中该书的4个不同版本的下载地址及其书名。
5进一步提升
在第四部分,我们详细分析了如何利用Requests包登录网站,以及用PyQuery解析网页的过程,将这些分析过程组合起来就是获取网站图书信息的程序,详细的代码请参考:github.
com/bnpysse/readfree。当然,这个程序还有很大的提升空间,譬如说可以利用多线程提高爬取速度,可以利用数据库程序把相关的数据保存起来,可以对爬取的数据进行分析,获得您想需要的各种数据等等。
6结束语
总之,本篇小文只是起到抛砖引玉之作用,在当前大数据概念流行之际,从公众网爬取数据可以说是所有问题的起始,爬取数据之后,如何让这些数据为我所用才是更为广泛的研究领域。笔者水平有限,敬请读者多提宝贵意见,我的EMail:bnpysse@aliyun。
- 20 -