python爬虫之百度贴吧
第一步:通过urllib2,Request命令和urlopen命令爬取贴吧网页源码,并通过这则表达式,选取自己想要的数据
在这里需要注意几个地方
1.编码问题,这里对中文编码不再进行过多阐述。
网页源码是utf-8,我在编写程序前# -*- coding:utf-8 -*-,那么显示在网页上,我只需要在urllib2.urlopen(res).read()后面加上.decode('utf-8') 即可
如果出现鏂囦欢鍚嶏細 这样的中文乱码,实际上只需要在print后加上u""就可以了
2.当抓取完页面,无论在输出是写入print或retrun,都没有关系,但在后面的正则表达式操作时需要注意要使用return返回
3.编码实在想不清楚,可以在项目开头写入
import sys
reload(sys)
sys.setdefaultencoding('utf8')
代码如下
1 #coding=utf-8 2 3 import urllib2 4 import urllib 5 import re 6 import sys 7 reload(sys) 8 sys.setdefaultencoding('utf8') 9 10 class BDTB: 11 def __init__(self,baseUrl,seeLZ): 12 self.baseUrl=baseUrl 13 self.seeLZ='?see_lz='+str(seeLZ) 14 15 16 def getTitle(self): 17 html=self.getPage(1) 18 pattern=re.compile(r'<h3 class="core_title_txt.*?">(.*?)</h3>') 19 title=re.search(pattern,html) 20 if title: 21 return title.group(1) 22 else: 23 return None 24 25 def getPageNum(self): 26 html=self.getPage(1) 27 pattern=re.compile(r'<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>') 28 nums=re.search(pattern,html) 29 if nums: 30 return nums.group(1) 31 else: 32 return None 33 def getContent(self,html): 34 pattern=re.compile(r'<div id="post_content.*?">(.*?)</div>') 35 contents=re.findall(pattern,html) 36 for item in items: 37 print item 38 39 def getPage(self,pageNum): 40 try: 41 url=self.baseUrl+str(self.seeLZ)+'&pn='+str(pageNum) 42 res=urllib2.Request(url) 43 res.add_header('User-Agent','Mozilla 5.10') 44 html=urllib2.urlopen(res).read().decode('utf-8') 45 print html 46 except urllib2.URLError,e: 47 if hasattr(e,"reason"): #判断一个对象里面是否有name属性或者name方法,返回布尔 48 print u"错误",e.reason 49 return None 50 51 baseUrl='http://tieba.baidu.com/p/3138733512' 52 53 bdtb=BDTB(baseUrl,1) 54 bdtb.getPage(1)
第二步:实现获取界面规划并写入文本中
这里需要注意几个地方
1.TypeError: getPageNum() takes exactly 1 argument (2 given)
getPageNum这个函数需要2个参数才可以,调用时只给了1个
所以我将def getPageNum(self):改为def getPageNum(self,page):其中page在执行语句page=self.getPage(1)
2.为甚要用self
self是类方法的一个位置参数,它就是类的实例对象自己,当实例调用方法时:
instance = X()
instance.f('a', 'b')
等同于:
X.f(instance, 'a', 'b')
第一个参数是实例自己。
3.TypeError: expected string or buffer
这里,我所遇到的问题就是在第一步中将return和print的使用弄混,导致出现这样参数传入错误的error,网上其他人的错误有的是因为网址些错误,导致传入的网址参数错误。我的修改很简单,将print修改成return,错误就消失了。
将html=urllib2.urlopen(res).read().decode('utf-8')
print html
改成html=urllib2.urlopen(res).read().decode('utf-8')
return html
就好了
为什么呢
return作用之一是返回计算的值
print是输出数据到控制端,额这说了我也不明白
直接上代码吧
x=1
y=2
def add(x,y):
z=x+y
print z
print add(x,y)
结果是 3
None
而x=1
y=2
def add(x,y):
z=x+y
return z
print add(x,y)
结果是 3
在交互模式下,return的结果会自动打印出来,而作为脚本单独运行时则需要print函数才能显示。
而为什么错误是传入的参数有问题呢,因为我们使用request获取该页的代码,用return的意思就是执行完这条语句之后不再执行语句,并返回所传的数据
而print是执行完这条语句后,在屏幕显示内容
我们之后需要进行获取标题内容正则表达式等操作,而我们若使用print,则相当于对空的内容进行操作,所以报错,说返回参数有错误
4.对于文本的理解
实际上就是使用writeline()方法,可以参考http://www.runoob.com/python/file-writelines.html
作用:向文件中写入一序列的字符串
这一序列字符串是由可以迭代对象产生
语法:file.writeline([str])
参数:str 要写入文件的字符串
返回值:这方法没有返回值
就我的理解就是用file = open(“tb.txt”,”w”)建立文本,txt格式,再将字符串写入str,使用writeline()方法写进文本,而file相当于一个全局变量
代码如下
1 # -*- coding:utf-8 -*- 2 import urllib2 3 import urllib #urllib2 4 import re #正则表达式 5 6 import sys 7 reload(sys) 8 sys.setdefaultencoding('utf-8') 9 class Tool: 10 removeImg = re.compile('<img .*?>',re.S) 11 removeAddr = re.compile('<a .*?>(.*?)</a>',re.S) 12 replaceLine = re.compile('<tr>|<div>|</div>|</p>',re.S) 13 replaceTD = re.compile('<td>',re.S) 14 replacePara = re.compile('<p .*?>',re.S) 15 replaceBR = re.compile('<br><br>|<br>',re.S) 16 removeExtraTag = re.compile('<.*?>',re.S) 17 18 def replace(self,x): 19 x=re.sub(self.removeImg,"",x) 20 x = re.sub(self.removeAddr, "", x) 21 x = re.sub(self.replaceLine, "\n", x) 22 x = re.sub(self.replaceTD, "\t", x) 23 x = re.sub(self.replacePara, "\n ", x) 24 x = re.sub(self.replaceBR, "\n", x) 25 x = re.sub(self.removeExtraTag, "", x) 26 27 return x.strip() 28 class BDTB: 29 30 def __init__(self,baseUrl,see_lz): #这一行什么意思 31 self.baseUrl=baseUrl 32 self.seelz='?see_lz='+str(see_lz) 33 self.tool=Tool() 34 self.floor=1#楼层标号,初始为1 35 self.defaultTitle=u'百度贴吧' 36 #是否写入分层的标记 37 self.floorTag=floorTag 38 self.file=None#全局file变量,文件写入操作对象 39 #传入页码,获取该页帖子代码 40 def getPage(self,pageNum): #为甚么总有一个self 41 try: 42 #url是网页规范,这一行的意思是在前面定义的基地址下加上是否查看楼主的seelz+页码,而页码是通过 43 44 url=self.baseUrl+str(self.seelz)+'&pn='+str(pageNum) 45 res=urllib2.Request(url) 46 html=urllib2.urlopen(res).read().decode('utf-8') 47 return html 48 except urllib2.UrlError,e:#为什么这么写 49 if hasattr(e,"reason"):#hasattr是什么意思 50 print u'错误',e.reason 51 return None 52 53 54 #页码 55 56 def getPageNum(self,page): 57 page=self.getPage(1) 58 pattern=re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S) 59 result=re.search(pattern,page) 60 if result: 61 return result.group(1).strip() 62 else: 63 return None 64 65 #标题 66 def getTitle(self,page): 67 page=self.getPage(1) 68 pattern=re.compile('<h3 class="core_title_txt.*?">(.*?)</h3>',re.S) 69 result=re.search(pattern,page) 70 if result: 71 return result.group(1).strip() 72 else: 73 return None 74 75 76 77 #获取内容 78 def getContent(self,page): 79 pattern=re.compile('<div id="post_content_.*?">(.*?)</div>',re.S) 80 items=re.findall(pattern,page) 81 #for item in items: 82 # print item 83 #print self.tool.replace(items[1]) 84 #floor=1 85 contents=[]#将匹配的内容都放到数组里面 86 for item in items: 87 #print floor,u"楼-------------------------------------------------\n" 88 #print self.tool.replace(item) 89 #floor +=1 90 content="\n"+self.tool.replace(item)+"\n" 91 contents.append(content.encode('utf-8')) 92 return contents 93 #以下都是将内容发送到文本里面的时候怎么操作 94 def setFileTitle(self,title):#这是将标题获取,放到记事本里面 95 if title is not None:#如果title不是None 96 self.file=open(title+".txt","w+")#这一句什么意思 97 else : 98 self.file=open(self.defaultTitle+".txt","w+") 99 #以下是将楼层写进去 100 def writeData(self,contents): 101 for item in contents: 102 if self.floorTag=='1': 103 floorLine="\n"+str(self.floor)+u"-------------------------\n" 104 self.file.write(floorLine) 105 self.file.write(item) 106 self.floor +=1 107 #交互界面 108 def start(self): 109 indexPage=self.getPage(1)#获取该页页码 110 pageNum=self.getPageNum(indexPage)#h获取一共有多少页 111 title=self.getTitle(indexPage)#获取该页码的标题 112 self.setFileTitle(title) 113 if pageNum==None: 114 print "URL已失效,请重试" 115 return #结束 116 try: 117 print u"该帖子共有"+str(pageNum)+'页' 118 for i in range(1,int(pageNum)+1): 119 print u"正在写入第"+str(i)+"页数据" 120 page=self.getPage(i) 121 contents=self.getContent(page) 122 self.writeData(contents) 123 #出现写入异常 124 except IOError,e: 125 print u"正在写入第"+str(i)+"页数据" 126 finally: 127 print u"写入任务完成" 128 print u"请写入帖子代号" 129 baseUrl='http://tieba.baidu.com/p/'+str(raw_input(u'http://tieba.baidu.com/p/')) 130 seelz=raw_input(u"是否获取楼主发言,是输入1,否输入0\n") 131 floorTag=raw_input(u"是否写入楼层信息,是输入1,否输入0\n") 132 133 134 135 bdtb=BDTB(baseUrl, 1)#这里为什么为1 136 #bdtb.getContent(bdtb.getPage(1)) 137 bdtb.start()
整个思路就是这样,参考崔庆才大大的博客,原网址如下
http://cuiqingcai.com/993.html
还有在实践过程中遇到的几个小问题
1.AttributeError: 'module' object has no attribute 'Requset'
拼写错误
2.这个是正则表达式发生错误,修改匹配正确源码正则表达式即可
2.这个是只获得第一页内容,如果要获得其他页内容可以修改items【a】a的值,如果要获得全部内容,则注释掉此句,上面被注释的就是
3.这里就是一页内容和所有内容的写法,所有内容用到了for in循环
下一步可能是学习怎么将这个打包成exe应用
1.写好python工程
2.在setup.py上修改""中的文件,写上已经编好了的.py
3.cmd命令-->d:-->cd python ---->cd Script----->python setup.py py2exe
生成dist文件,将dist文件里面所有东西全部打包
不过有个小问题,同样的代码爬取帖子,有的帖子是中文乱码,有的却是正常的,且都是utf-8格式
然后,我把这个exe删了重新生成新的exe文件,结果乱码的帖生成txt显示正常