今天使用python爬虫实现了自动抓取糗事百科的段子,因为糗事百科不需要登录,抓取比较简单。程序每按一次回车输出一条段子,代码参考了 http://cuiqingcai.com/990.html 但该博主的代码似乎有些问题,我自己做了修改,运行成功,下面是代码内容:
1 # -*- coding:utf-8 -*- 2 __author__ = 'Jz' 3 import urllib2 4 import re 5 6 #糗事百科爬虫类 7 class QSBK: 8 #初始化 9 def __init__(self): 10 self.pageIndex = 1 11 self.user_agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64)' 12 self.headers = {'User-Agent': self.user_agent} 13 #joke的每一个元素是每一页的段子 14 self.joke = [] 15 #判断是否继续运行 16 self.enable = False 17 18 def getPage(self, pageIndex): 19 try: 20 URL = 'http://www.qiushibaike.com/hot/page/' + str(pageIndex) 21 request = urllib2.Request(url = URL, headers = self.headers) 22 response = urllib2.urlopen(request) 23 pageContent = response.read().decode('utf-8') 24 return pageContent 25 except urllib2.URLError, e: 26 if hasattr(e, 'reason'): 27 print '段子抓取失败,失败原因:', e.reason 28 return None 29 30 def getJokeList(self, pageIndex): 31 pageContent = self.getPage(pageIndex) 32 if not pageContent: 33 print '段子获取失败...' 34 return None 35 #第三个组中的内容用于判断段子是否附带图片 36 pattern = re.compile(r'<div.*?class="author">.*?<a.*?>.*?<img.*?/> (.*?) </a>.*?</div>.*?<div class="content"> (.*?) <!--.*?-->.*?</div>' + 37 r'(.*?)class="stats">.*?<span.*?class="stats-vote"><i.*?class="number">(.*?)</i>' 38 , re.S) 39 jokes = re.findall(pattern, pageContent) 40 pageJokes = [] 41 #过滤带有图片的段子 42 for joke in jokes: 43 hasImg = re.search('img', joke[2]) 44 #joke[0]为发布人,joke[1]为段子内容,joke[3]为点赞数量 45 if not hasImg: 46 pageJokes.append([joke[0].strip(), joke[1].strip(), joke[3].strip()]) 47 return pageJokes 48 49 def loadPage(self): 50 if self.enable == True: 51 #若当前已看的页数少于两页,则加载新的一页 52 if len(self.joke) < 2: 53 pageJokes = self.getJokeList(self.pageIndex) 54 if pageJokes: 55 self.joke.append(pageJokes) 56 self.pageIndex += 1 57 58 #每输入一次回车,打印一条段子 59 def getOneJoke(self, pageJokes, page): 60 jokes = pageJokes 61 for joke in jokes: 62 userInput = raw_input('请输入回车键或Q/q: ') 63 self.loadPage() 64 if userInput == 'Q' or userInput == 'q': 65 self.enable = False 66 print '退出爬虫...' 67 return 68 print u'段子内容:%s 第%d页 发布人:%s 赞:%s' % (joke[1], page, joke[0], joke[2]) 69 70 def start(self): 71 print '正在从糗事百科抓取段子,按回车键查看新段子,按Q/q退出...' 72 self.enable = True 73 self.loadPage() 74 page = 0 75 while self.enable: 76 if len(self.joke) > 0: 77 pageJokes = self.joke[0] 78 page += 1 79 #删除已经读取过的段子页 80 del self.joke[0] 81 self.getOneJoke(pageJokes, page) 82 83 spider = QSBK() 84 spider.start()
注释已经附上,其中有几点需要注意的地方:
1.需要加上header验证进行伪装,否则无法抓取网页内容
2.正则表达式的书写,需要将内容提取出来以验证是否有附带图片(代码中已用红色标注)
3.getOneJoke函数中格式化输出段子时(已用红色标注),需要在字符串前加上u,否则会报如下错误:
Traceback (most recent call last): File "D:coding_filepython_fileTestPythonsrcTestQSBK.py", line 84, in <module> spider.start() File "D:coding_filepython_fileTestPythonsrcTestQSBK.py", line 81, in start self.getOneJoke(pageJokes, page) File "D:coding_filepython_fileTestPythonsrcTestQSBK.py", line 68, in getOneJoke print '段子内容:%s 第%d页 发布人:%s 赞:%s' % (joke[1], page, joke[0], joke[2]) UnicodeDecodeError: 'ascii' codec can't decode byte 0xe7 in position 3: ordinal not in range(128)
这是因为Python默认编码方式为Unicode,所以joke[0]等也是Unicode编码,为了格式化输出,前面的字符串也需要转换成Unicode编码