学习python有一段时间,一直都是看书学,写点书上的小例子,终于把书看完了,想着写一个爬虫锻炼一下。其实用python写爬虫还是挺简单的,一个难点是正则表达式,因为一般用python把网页整个源代码爬下来,然后根据网页的内容结构把我们需要的内容给抓出来。作为一名后端程序员,平时也只是偶尔写写前端页面,偶尔遇上一些需要正则表达式的,也就是在网上找了一下,没有怎么学,所以写这个之前还补了一下正则表达式。
urllib2模块和urllib模块类似,用来打开URL并从中获取数据。与urllib模块不同的是,urllib2模块不仅可以使用urlopen()函数还可以自定义opener来访问网页。但同时要注意:urlretrieve()函数是urllib模块中的,urllib2模块中不存在该函数。但是使用urllib2模块时一般都离不开urllib模块,因为post的数据需要使用urllib.urlencode()函数来编码。
先来介绍一下程序中用到的urllib2方法:
urlopen(url, [,data, [timeout]])
urlopen()是最简单的请求方式,它打开url并返回类文件对象,并且使用该对象可以读取返回的内容。参数url可以是包含url的字符串,也可以是urllib2.request类的实例。data是经过编码的post数据(一般使用urllib.urlencode()来编码)。timeout是可选的超时期(以秒为单位),供所有阻塞操作内部使用。
假设urlopen()返回的文件对象u,它支持下面的这些常用的方法:
u.read([nbytes]) 以字节字符串形式读取nbytes个数据
u.readline() 以字节字符串形式读取单行文本
u.readlines() 读取所有输入行然后返回一个列表
u.close() 关闭链接
u.getcode() 返回整数形式的HTTP响应代码,比如成功返回200,未找到文件时返回404
u.geturl() 返回所返回的数据的实际url,但是会考虑发生的重定向问题
u.info() 返回映射对象,该对象带有与url关联的信息,对HTTP来说,返回的服务器响应包含HTTP包头。对于FTP来说,返回的报头包含'content-length'。对于本地文件,返回的报头包含‘content-length’和'content-type'字段。
要注意的是,类文件对象u以二进制模式操作。如果需要以文本形式处理响应数据,则需要使用codecs模块或类似方式解码数据。
关于正则表达式,可以参考一下这篇文章:Python正则表达式指南
需要注意的是是否匹配多行模式以及贪婪模式与非贪婪模式。
二、这个衔接是《暗时间》的豆瓣介绍
http://book.douban.com/subject/6709809/
通过查看源码分析,就会发现
有class="ua-windows ua-webkit book-new-nav"的页面是图书介绍页面,而其中标题结构为<title>暗时间 (豆瓣)</title>
出版社、出版年、页数、定价等结构如下:
<span class="pl">出版社</span> (.*)<br/>
稍微难一点的是作者:
<span>
<span class="pl"> 作者</span>:
<a class="" href="/search/%E5%88%98%E6%9C%AA%E9%B9%8F">刘未鹏</a>
</span><br/>
涉及到了换行,开始使用
<span class="pl"> 作者</span>.*<a class="" href=.*>(.*)</a>去匹配,查出来的作者不是空就是豆瓣广告,这首因为默认使用贪婪模式匹配,后来改成如下使用非贪婪模式就正确了
<span class="pl"> 作者</span>.*?<a class="" href=.*?>(.*?)</a>
全部代码如下:
#coding=utf-8 """ 这是一个抓取豆瓣图片源代码,并从中提取相关内容的python小爬虫 """ import sys import urllib2 import re def get_book(packet): tmp = re.search(r'<html lang(.*) book-new-nav">',packet) if tmp is not None: print "It's Book" title = re.search(r'<title>(.*)\(豆瓣\)</title>',packet) print '书名:' + title.group(1).strip() author = re.search(r'<span class="pl"> 作者</span>.*?<a class="" href=.*?>(.*?)</a>',packet,re.S) if author: print '作者:' + author.group(1).strip() cbs = re.search(r'<span class="pl">出版社:</span>(.*)<br/>',packet) if cbs: print '出版社:' + cbs.group(1).strip() price = re.search(r'<span class="pl">定价:</span>(.*)<br/>',packet) print '定价:' + price.group(1).strip() else: print "It's not Book" def douban_book(url): try: response = urllib2.urlopen(url) content = response.read() get_book(content) except urllib2.URLError,e: if hasattr(e,"reason"): print 'Failed to reach the server' print "The reason:",e.reason elif hasattr(e,"code"): print "The server couldn't fulfill the request" print "Error code:",e.code print "Return content:",e.read() else: pass if __name__ == '__main__': url = 'http://book.douban.com/subject/222222/' packet = douban_book(url) if packet is None: sys.exit(0)
拓展:可以看到豆瓣图书的url都是http://book.douban.com/subject/加上一个数字,所以可以写一个循环,就可以统计一下豆瓣有多少图书了,有空试一下。
可以看到豆瓣图书的url都是http://book.douban.com/subject/加上一个数字,所以可以写一个循环,就可以统计一下豆瓣有多少图书了,有空试一下。
这个是不太准确的,因为有很多是not found。因为豆瓣的连接是http://xxx.douban.com/subject/+数字
XXX 可以是movie,book…
嗯,你说的我知道,循环里面要进行判断的,如果是not found就排除掉,不算统计的。