在抓取网页信息时,经常要对网页中特定位置信息进行查找,比如查找标题所在的位置,正文内容所在位置,需要用到类似于操作系统下*和?这样的通配符,在python中把类似的一些符号组成的表达式称为正则表达式,要在程序中使用正则表达式需先使用import re进行说明,加载re模块。
常用的正则表达式符号有:
符号 | 含义 | 举例 | 完整匹配的字符串 |
. | 匹配除\n外的任意一个字符 | a.c | abc |
\t | 匹配tab符号 | ||
\n | 匹配换行符 | ||
\d | 匹配任意一个数字 | a\dc | a1c |
传送门:1.Python正则表达式指南 2.Python中的正则表达式(re)
下面这个例子可以在百度帖吧的指定帖子中爬取楼主发布的所有内容:
1 #coding:utf-8 2 ''' 3 Created on 2016年12月16日 4 5 @author: liukain 6 ''' 7 # -*- coding: utf-8 -*- 8 #--------------------------------------- 9 # 程序:百度贴吧爬虫 10 11 # 语言:Python 2.7 12 # 操作:输入网址后自动只看楼主并保存到本地文件 13 # 功能:将楼主发布的内容打包txt存储到本地。 14 #--------------------------------------- 15 16 import string 17 import urllib2 18 import re 19 #----------- 处理页面上的各种标签 ----------- 20 class HTML_Tool: 21 # 用非 贪婪模式 匹配 \t 或者 \n 或者 空格 或者 超链接 或者 图片 22 BgnCharToNoneRex = re.compile("(\t|\n| |<a.*?>|<img.*?>)") 23 # 用非 贪婪模式 匹配 任意<>标签 24 EndCharToNoneRex = re.compile("<.*?>") 25 # 用非 贪婪模式 匹配 任意<p>标签 26 BgnPartRex = re.compile("<p.*?>") 27 CharToNewLineRex = re.compile("(<br/>|</p>|<tr>|<div>|</div>)") 28 CharToNextTabRex = re.compile("<td>") 29 # 将一些html的符号实体转变为原始符号 30 replaceTab = [("<","<"),(">",">"),("&","&"),("&","\""),(" "," ")] 31 32 def Replace_Char(self,x): 33 x = self.BgnCharToNoneRex.sub("",x) 34 x = self.BgnPartRex.sub("\n ",x) 35 x = self.CharToNewLineRex.sub("\n",x) 36 x = self.CharToNextTabRex.sub("\t",x) 37 x = self.EndCharToNoneRex.sub("",x) 38 39 for t in self.replaceTab: 40 x = x.replace(t[0],t[1]) 41 return x 42 43 class Baidu_Spider: 44 # 申明相关的属性 45 def __init__(self,url): 46 self.myUrl = url + '?see_lz=1' 47 self.datas = [] 48 self.myTool = HTML_Tool() 49 print u'已经启动百度贴吧爬虫,咔嚓咔嚓' 50 51 # 初始化加载页面并将其转码储存 52 def baidu_tieba(self): 53 # 读取页面的原始信息并将其从gbk转码 54 myPage = urllib2.urlopen(self.myUrl).read().decode("utf_8") 55 # 计算楼主发布内容一共有多少页 56 endPage = self.page_counter(myPage) 57 # 获取该帖的标题 58 title = self.find_title(myPage) 59 print u'文章名称:' + title 60 # 获取最终的数据 61 self.save_data(self.myUrl,title,endPage) 62 63 #用来计算一共有多少页 64 def page_counter(self,myPage): 65 # 匹配 "共有<span class="red">12</span>页" 来获取一共有多少页 66 myMatch = re.search(r'class="red">(\d+?)</span>', myPage, re.S) 67 if myMatch: 68 endPage = int(myMatch.group(1)) 69 print u'爬虫报告:发现楼主共有%d页的原创内容' % endPage 70 else: 71 endPage = 0 72 print u'爬虫报告:无法计算楼主发布内容有多少页!' 73 return endPage 74 75 # 用来寻找该帖的标题 76 def find_title(self,myPage): 77 # 匹配 <h1 class="core_title_txt" title="">xxxxxxxxxx</h1> 找出标题 78 myMatch = re.search(r'<h1.*?>(.*?)</h1>', myPage, re.S) 79 title = u'暂无标题' 80 if myMatch: 81 title = myMatch.group(1) 82 else: 83 print u'爬虫报告:无法加载文章标题!' 84 # 文件名不能包含以下字符: \ / : * ? " < > | 85 title = title.replace('\\','').replace('/','').replace(':','').replace('*','').replace('?','').replace('"','').replace('>','').replace('<','').replace('|','') 86 return title 87 88 89 # 用来存储楼主发布的内容 90 def save_data(self,url,title,endPage): 91 # 加载页面数据到数组中 92 self.get_data(url,endPage) 93 # 打开本地文件 94 f = open(title+'.txt','w+') 95 f.writelines(self.datas) 96 f.close() 97 print u'爬虫报告:文件已下载到本地并打包成txt文件' 98 print u'请按任意键退出...' 99 raw_input(); 100 101 # 获取页面源码并将其存储到数组中 102 def get_data(self,url,endPage): 103 url = url + '&pn=' 104 for i in range(1,endPage+1): 105 print u'爬虫报告:爬虫%d号正在加载中...' % i 106 myPage = urllib2.urlopen(url + str(i)).read() 107 # 将myPage中的html代码处理并存储到datas里面 108 self.deal_data(myPage.decode('utf-8')) 109 110 # 将内容从页面代码中抠出来 111 def deal_data(self,myPage): 112 myItems = re.findall('id="post_content.*?>(.*?)</div>',myPage,re.S) 113 for item in myItems: 114 data = self.myTool.Replace_Char(item.replace("\n","").encode('utf-8')) 115 self.datas.append(data+'\n') 116 117 #-------- 程序入口处 ------------------ 118 print u"""#--------------------------------------- 119 # 程序:百度贴吧爬虫 120 121 # 语言:Python 2.7 122 # 操作:输入网址后自动只看楼主并保存到本地文件 123 # 功能:将楼主发布的内容打包txt存储到本地。 124 #--------------------------------------- 125 """ 126 127 # 以某小说贴吧为例子 128 # bdurl = 'http://tieba.baidu.com/p/2296712428?see_lz=1&pn=1' 129 print u'请输入贴吧的地址最后的数字串:' 130 bdurl = 'http://tieba.baidu.com/p/' + str(raw_input(u'http://tieba.baidu.com/p/')) 131 #调用 132 mySpider = Baidu_Spider(bdurl) 133 mySpider.baidu_tieba()
第22行:re.compile将常用的正则表达式规则存储为一个对象,方便后面使用。
问题:第22行,超链接为什么是a打头?第30行replaceTab的用法没看懂。
第33行,对象.sub用法:sub是substitute的所写,表示替换,用来实现通过正则表达式,实现比普通字符串的replace更加强大的替换功能。
关于sub用法的传送门:详解Python中的re.sub
运行效果如下:
下面这个例子应该可以从糗事百科中提取里面的内容,但是我没有运行成功,总是卡在“正在加载中请稍候....”那,就没反应了,以后有时间再研究一下:
1 # -*- coding: utf-8 -*- 2 ''' 3 Created on 2016年12月16日 4 5 @author: liukain 6 ''' 7 #--------------------------------------- 8 # 程序:糗百爬虫 9 # 语言:Python 2.7 10 # 操作:输入quit退出阅读糗事百科 11 # 功能:按下回车依次浏览今日的糗百热点 12 # 更新:解决了命令提示行下乱码的问题 13 #--------------------------------------- 14 import urllib2 15 import urllib 16 import re 17 import thread 18 import time 19 20 #----------- 处理页面上的各种标签 ----------- 21 class HTML_Tool: 22 # 用非 贪婪模式 匹配 \t 或者 \n 或者 空格 或者 超链接 或者 图片 23 BgnCharToNoneRex = re.compile("(\t|\n| |<a.*?>|<img.*?>)") 24 # 用非 贪婪模式 匹配 任意<>标签 25 EndCharToNoneRex = re.compile("<.*?>") 26 # 用非 贪婪模式 匹配 任意<p>标签 27 BgnPartRex = re.compile("<p.*?>") 28 CharToNewLineRex = re.compile("(<br/>|</p>|<tr>|<div>|</div>)") 29 CharToNextTabRex = re.compile("<td>") 30 # 将一些html的符号实体转变为原始符号 31 replaceTab = [("<","<"),(">",">"),("&","&"),("&","\""),(" "," ")] 32 def Replace_Char(self,x): 33 x = self.BgnCharToNoneRex.sub("",x) 34 x = self.BgnPartRex.sub("\n ",x) 35 x = self.CharToNewLineRex.sub("\n",x) 36 x = self.CharToNextTabRex.sub("\t",x) 37 x = self.EndCharToNoneRex.sub("",x) 38 for t in self.replaceTab: 39 x = x.replace(t[0],t[1]) 40 return x 41 #----------- 处理页面上的各种标签 ----------- 42 #----------- 加载处理糗事百科 ----------- 43 class HTML_Model: 44 def __init__(self): 45 self.page = 1 46 self.pages = [] 47 self.myTool = HTML_Tool() 48 self.enable = False 49 50 # http://m.qiushibaike.com/hot/将所有的段子都扣出来,添加到列表中并且返回列表 http://www.qiushibaike.com/hot/ 51 def GetPage(self,page): 52 myUrl = "http://m.qiushibaike.com/hot/" + page 53 myResponse = urllib2.urlopen(myUrl) 54 myPage = myResponse.read() 55 #encode的作用是将unicode编码转换成其他编码的字符串 56 #decode的作用是将其他编码的字符串转换成unicode编码 57 unicodePage = myPage.decode("utf-8") 58 # 找出所有class="content"的div标记 59 #re.S是任意匹配模式,也就是.可以匹配换行符 60 myItems = re.findall('<div.*?class="content".*?title="(.*?)">(.*?)</div>',unicodePage,re.S) 61 items = [] 62 for item in myItems: 63 # item 中第一个是div的标题,也就是时间 64 # item 中第二个是div的内容,也就是内容 65 items.append([item[0].replace("\n",""),item[1].replace("\n","")]) 66 return items 67 # 用于加载新的段子 68 def LoadPage(self): 69 # 如果用户未输入quit则一直运行 70 while self.enable: 71 # 如果pages数组中的内容小于2个 72 if len(self.pages) > 2: 73 try: 74 # 获取新的页面中的段子们 75 myPage = self.GetPage(str(self.page)) 76 self.page += 1 77 self.pages.append(myPage) 78 except: 79 print '无法链接糗事百科!' 80 else: 81 time.sleep(1) 82 83 def ShowPage(self,q,page): 84 for items in q: 85 print u'第%d页' % page , items[0] 86 print self.myTool.Replace_Char(items[1]) 87 myInput = raw_input() 88 if myInput == "quit": 89 self.enable = False 90 break 91 def Start(self): 92 self.enable = True 93 page = self.page 94 95 print u'正在加载中请稍候......' 96 97 # 新建一个线程在后台加载段子并存储 98 thread.start_new_thread(self.LoadPage,()) 99 100 #----------- 加载处理糗事百科 ----------- 101 while self.enable: 102 # 如果self的page数组中存有元素 103 if self.pages: 104 nowPage = self.pages[0] 105 del self.pages[0] 106 self.ShowPage(nowPage,page) 107 page += 1 108 109 #----------- 程序的入口处 ----------- 110 print u""" 111 --------------------------------------- 112 程序:糗百爬虫 113 版本:0.1 114 115 语言:Python 2.7 116 操作:输入quit退出阅读糗事百科 117 功能:按下回车依次浏览今日的糗百热点 118 --------------------------------------- 119 """ 120 121 122 print u'请按下回车浏览今日的糗百内容:' 123 raw_input(' ') 124 myModel = HTML_Model() 125 myModel.Start()
我的运行效果: