最近一直在看Python方面的知识,包括数据分析中常用的numpy、pandas、scipy等模块;数据库API接口,如常见的pymysql、pymssql等模块;爬虫方面所需要的urllib、bs4等模块;还有正则表达式re模块。在之前的几期中我们已经详细介绍了numpy、pandas和pymysql、pymssql模块的应用,具体可参见下文:
Python数据分析之numpy学习(一)
Python数据分析之numpy学习(二)
Python数据分析之pandas学习(一)
Python数据分析之pandas学习(二)
利用Python读取外部数据文件
今天我们就来讲讲有关爬虫的一点点知识和应用,文章内容相对简单,可操作比较强,后期我也将会更加深入的挖掘Python实现爬虫的方法和应用。接下来我们就以一个例子开始吧。
豆瓣是我常去的一个网站,上面提供了图书、电影、音乐等方面的推荐、评论和价格比较。如果我需要在买一本Python方面的书,我会去豆瓣看看都有哪些被推荐的书,以及这些书的评分、评价等,从而最终确定我该买那本书。下面这幅图就是豆瓣阅读中搜索Python的返回信息:
于是我突发奇想,是否可以将自己学习的urllib、bs4和pymysql模块应用于一个简单的爬虫呢?将这些书籍的名称、作者、价格、分类、评分等信息爬取下来,并存放到数据库中。于是我开始动手准备,为完成这个简单的爬虫,我所使用的工具是Python3.5和MySQL5.6。
导入开发所需的模块
In [1]: import urllib
In [2]: from bs4 import BeautifulSoup
In [3]: import pymysql
我们以url为https://read.douban.com/search?q=Python&start=0为例,实际上我搜出来的有3页,随后通过循环的方式一次性爬取3个页面的内容,这里先以第一页的内容为例,看看如何爬取该页面的内容。
使用urllib的子模块request中的urlopen方法打开源代码,并使用read方法将源代码读取下来:
In [4]: url = 'https://read.douban.com/search?q=Python&start=0'
In [5]: content = urllib.request.urlopen(url).read()
#打印内容
In [6]: print(content)
发现一个非常严重的问题:所有的中文都没有能够正常的显示出来!没关系,我们只需要将content的字符集改为utf8即可。
In [7]: content = content.decode('utf-8')
In [8]: print(content)
哈哈,这会中文都回来了吧~
接下来我们就使用bs4模块下的BeautifulSoup函数解析上面读取下来的网页内容,并通过prettify的方法将网页内容以嵌套的方式打印出来,显得好看一些。
In [9]: soup = BeautifulSoup(content,'html.parser')
In [10]: print(soup.prettify())
非常好!接下来就是我该如何截取出我想搜集的字段内容,如书的名称、作者等信息。如果你使用Chrom浏览器的话,你可以通过右击的方式,选择“审查元素”,就可以进入下图所示的页面。具体操作如下:
1)点击搜索图标;
2)选择需要爬取的大块内容,如页面中第一本书的所有信息;
3)回到下方的网页源代码,逐个展开你就会发现具体的类、标签所对应的书本信息,而且最下方也会显示div.info的字样哦。这个字样就是告诉你,这一本书的内容全部包含在class为info的标签里面。
好了,知道如何获取整本书的class了,接下来就是看每一个所需信息的标签是什么就可以啦。方法类似,你不妨试试~如有问题可以联系我。
下面我们使用select方法抓取出书本的相关信息,由于这一个页面中,有10本书,所以就需要通过循环的方式把这一页所有书籍的相关信息抓取出来:
In [12]: for content in soup.select('.info'): #info为大块内容对应的class
...: title = content.select('.title')[0].text #titlew为书籍名称对应的class
...: author = content.select('.author-item')[0].text
...: price = content.select('.price-tag')[0].text
...: category = content.select('.category')[0].text[3:]
...: rating = content.select('.rating-average')[0].text
...: eveluate_nums = content.select('.ratings-link')[0].text[:-3]
...: desc = content.select('.article-desc-brief')[0].text
...:
...: print(title,author,price,category,rating,eveluate_nums,desc)
是不是很兴奋!一下子就把这一个页面的内容全部抓取下来了,接下来是不是该处理第二页的内容啦,方法还是一样一样的,只要最后将所有页的内容拼接起来就OK了。你当我傻啊!这要是有个上百页上千页的,我一个星期都在搞这个,是不是效率太低了!唉~我们接下来只要发现每一页url的规律是不是就可以通过循环的方式解决这个一页一页处理的笨方法啦。我们发现原来这三个页面的url是这样的:
https://read.douban.com/search?q=Python&start=0
https://read.douban.com/search?q=Python&start=10
https://read.douban.com/search?q=Python&start=20
规律很明显,就不说啦!这里继续上代码,实现翻页般的爬虫:
生成有规律的url:
In [13]: urls = []
...: for i in (0,10,20):
...: urls.append('https://read.douban.com/search?q=Python&start=' + str(i))
In [15]: for url in urls:
...: #读取网页内容
...: req = urllib.request.Request(url)
...: res = urllib.request.urlopen(req).read().decode('utf-8')
...: #解析网页
...: soup = BeautifulSoup(res, 'html.parser')
...: #循环的方式读取所需字段
...: for content in soup.select('.info'):
...: title = content.select('.title')[0].text
...: if content.select('.author-item') == []:
...: anthor = None
...: else:
...: author = content.select('.author-item')[0].text
...: price = content.select('.price-tag')[0].text
...: category = content.select('.category')[0].text[3:]
...: if content.select('.rating-average')==[]:
...: rating = None
...: else:
...: rating = content.select('.rating-average')[0].text
...: if content.select('.ratings-link') == []:
...: eveluate_nums = None
...: else:
...: eveluate_nums = content.select('.ratings-link')[0].text[:-3]
...: if content.select('.article-desc-brief') == []:
...: desc = None
...: else:
...: desc = content.select('.article-desc-brief')[0].text
...:
...: print(title,author,price,category,rating,eveluate_nums,desc)
OK,到目前为止,大功已完成90%。哎?发现上图中的第二段for循环与这个不一样啊!为什么第二段循环里多出来这么对if...else的结构啊?
好,这个问题问的非常有水平,这是因为有些书没有人评价,或者有人评价但没有评分。对于这样的问题,如果没有if...else就会报错,最终导致你的程序无法搜集到所需的数据,不信,你试试~
最后,我们得将爬取出来的数据导入到我们的数据库。前往记得,在导入数据库之前,需要在MySQL中建立好一张存数据的表:
接下来使用pymysql模块将数据导入到数据库中(关于该模块的使用,可以参考利用Python读取外部数据文件),具体脚本如下(如果您操作的话,千万记得下面的代码是紧跟着print语句后面的,跟print语句齐平):
In [16]: connect = pymysql.connect(
...: host = 'localhost',
...: user = 'root',
...: password = 'snake',
...: port = 3306,
...: database = 'test',
...: charset = 'utf8')
...: sql = 'insert into `books`(`title`,`author`,`price`,\
...: `category`,`rating`,`eveluate_nums`,`describtion`) \
...: values (%s,%s,%s,%s,%s,%s,%s)'
...:
...: try:
...: with connect.cursor() as cursor:
...: cursor.execute(sql,(title,author,price,
...: category,rating,eveluate_nums,desc))
...: connect.commit()
...: cursor.close()
...: finally:
...: connect.close()
效果上图:
OK,如果你也按照上面的操作的话,我想你的结果应该是跟我的一样。看,这样一个简单的爬虫工作就让你实现了,是不是有点小兴奋。关于爬虫部分,还有很多需要深入学习和了解的,有兴趣的朋友可以互相交流,一起往更深的爬虫黑洞进军!
最后,再跟大家致歉,原本这期计划写《运用R语言的caret包进行特征选择》,但由于理论部分实在太难,需要一段时间消化,所以将爬虫提前推送出来。期待朋友们继续关注即将推送的《运用R语言的caret包进行特征选择》一期内容。
学习与分享,取长补短,欢迎关注博客:每天进步一点点2015