今天闲来无事,就想接触一下爬虫的基本知识,搞大数据,没有数据是万万不可的,而数据的来源,有很大一部分是通过网络爬虫来抓取的。
关于爬虫,似乎python的框架会比较多,比如scrapy。不过刚开始接触,用python一些原生的库就可以完成一些简单的小爬虫程序了,就比如我刚刚写的冷笑话爬虫。
在这之前,得大概搞清楚爬虫的工作原理,其实也很简单。首先是对你的目标网站发出http请求,目标网站会返回一个html文档给你,也就是我们俗称的网页,原本,这些文档都是由一大堆标签构成的,但浏览器配合上css,js,就形成了我们看到的各种各样的网页。但我们的爬虫不需要这些外衣,我们只需要内容,所以爬虫接下来的工作是处理这个html文档,提取我们要的信息,然后继续爬取下一个网页,如此往复循环。
不多说,先上我写的一段python脚本,爬取<我们爱讲冷笑话>的随机页面,网址为:http://lengxiaohua.com/random,这个页面每次刷新都有20则不同的随机冷笑话,我的程序功能是:输入数字1到20看对应的笑话,输入new重新爬取另外20则新笑话,输入quit退出程序。代码如下:
#-*- coding:utf-8 -*-
__author__ = '李鹏飞'
import urllib2
import re
class randomJoke:
#初始化方法
def __init__(self):
self.url = 'http://lengxiaohua.com/random'
self.user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
#初始化headers
self.headers = { 'User-Agent' : self.user_agent }
#笑话内容
self.content = []
#获取网页源代码
def getSourceCode(self):
try:
request = urllib2.Request(url = self.url, headers=self.headers)
response = urllib2.urlopen(request)
sourceCode = response.read().decode('utf-8')
return sourceCode
except urllib2.URLError, e:
if hasattr(e,"reason"):
print u"网络错误...",e.reason
return None
#获取笑话
def setContent(self):
sourceCode = self.getSourceCode()
if not sourceCode:
print('获取网页内容失败~!')
quit()
pattern = re.compile(' <pre.*?js="joke_summary".*?"first_char">(.*?)</span>(.*?)</pre>
.*?class="user_info">.*?<a.*?>(.*?)</a>.*?
(.*?)
',re.S)
items = re.findall(pattern,sourceCode)
self.content = items
print u"已经爬取源代码...正在解析源代码..."
#返回笑话
def getContent(self):
return self.content
#打印一则笑话
def printAJoke(self,number):
joke = self.content[number]
print u"作者:%s" %(joke[2])
print u'发表于:'+ joke[3]
#item[0]和item[1]组成完整的内容
print joke[0]+joke[1]
randomJoke = randomJoke()
notQuit = True
print u"你好,这里是随机笑话!"
print u"---------------------"
randomJoke.setContent()
print u"...."
print u"笑话池已经装满,20/20"
print u"输入1到20看笑话~~,输入quit退出,输入new重新爬取新笑话"
while notQuit:
input = raw_input()
if input == "quit" :
print u"bye!"
notQuit = False
elif input == "new":
randomJoke.setContent() #重新抓取笑话内容
print u"...."
print u"笑话池已经装满,20/20"
print u"输入1到20看笑话~~,输入quit退出,输入new重新爬取新笑话"
else:
input = int(input)
randomJoke.printAJoke(input-1)
print u"--------------------------------------------------"
print u"输入1到20看笑话~~,输入quit退出,输入new重新爬取新笑话"
print u"您已经成功退出!"
quit()
保存这段脚本,运行,效果如下:
说明一下,有些笑话是带图片的,而我没有抓取图片,仅仅抓取了作者,发表日期,和内容。
下面对程序稍微进行一下解释:
引入了urllib2
和re
两个模块,urllib2
是用来发送http请求的,re
是正则表达式的相关模块。
类randomJoke
是程序的主要部分,下面就所有的方法,进行简单的说明。
__init__方法
位置:9-15行
这个是初始化方法,定义了四个变量,url
为目标网址;user_agent
是我们发送http请求的时候伪造的头部的一个客户端信息;headers
是头部信息,这里只把user_agent
信息加进去就行了;content[]
是一个列表,用来存放笑话内容。
getSourceCode方法
位置:18-27行
这个方法是用来获取网页的源代码的。在第20行,发起一个http请求,接着读取返回的源代码,并return源代码。
setContent方法
位置:30-44行
这个方法是比较关键的一个方法,主要是利用正则表达式匹配源代码,并把我们需要的内容爬取下来。第35-41行代码就是正则表达式的匹配模式,我们要抓取网页中的20个笑话,其中的.*?
是匹配任意字符,多一个括号,即(.*?)
表示匹配的内容是我们需要的内容,在这个片段当中,出现了4个(.*?)
,因此,程序的第36行,item
表示的是一个列表,这个列表的形式是这样的:[[[item1-1],[item1-2],[item1-3],[item1-4]],[…],[…],…[[item20-1],[item20-2],[item20-3],[item20-4]]]。即这个列表有20个元素(代表这个网页的20个笑话),每个元素又是一个列表–列表中有4个元素(代表爬取的4个内容,对应前面说的4个(.*?)
。这里附上其中的一小部分网页源代码,对应着网页的html源代码,我们就能感受到这个简单的正则表达式的作用了:
<pre js="joke_summary"><span class="first_char">来</span>表哥家吃饭,小侄子一边看西游记一边手舞足蹈上蹦下跳学猴哥。
我哥:“傻儿子,你是孙悟空吗?”
侄子:“正是,你孙爷爷在此。”
打到现在还在哭。</pre>
</div>
<div class="para_info clearfix">
<div class="user_info">
<img src="http://joke-image.b0.upaiyun.com/img/user/cover/630347-299927_40x40.jpg" class="left corner4px" height="40" width="40"/>
<a href="/user/630347">离愁▍ Feast aw</a>
<p>15天前</p>
一共有20个类似的html片段,正则表达式的作用就是把笑话内容,作者和发表日期提取出来。
printAJoke方法
位置:51-56行
打印一则笑话,在这里我们更能感受到content[]
的结构了,因为printAJoke
这个方法需要传一个number
参数进来,范围是1到20,我们就可以读取对应的笑话了,比如content[0]
代表第一则笑话,和java中的数组类似。
53行以后就是程序运行的基本逻辑了,比较简单,就不多解释了。
就这样,一个简单的爬虫就完成了!