分享

[Python]网络爬虫(九)(十)

本帖最后由 pig2 于 2014-12-18 21:57 编辑

问题导读
问题1:用Python写的百度贴吧的网络爬虫的原理及过程?
问题2:一个爬虫的诞生的全过程是怎样的?








[Python]网络爬虫(九):百度贴吧的网络爬虫(v0.4)源码及解析
更新:感谢评论中朋友的提醒,百度贴吧现在已经改成utf-8编码了吧,需要把代码中的decode('gbk')改成decode('utf-8')。


百度贴吧的爬虫制作和糗百的爬虫制作原理基本相同,都是通过查看源码扣出关键数据,然后将其存储到本地txt文件。

源码下载:
http://download.csdn.net/detail/wxg694175346/6925583


项目内容:用Python写的百度贴吧的网络爬虫。

使用方法:
新建一个BugBaidu.py文件,然后将代码复制到里面后,双击运行。

程序功能:
将贴吧中楼主发布的内容打包txt存储到本地。

原理解释:

首先,先浏览一下某一条贴吧,点击只看楼主并点击第二页之后url发生了一点变化,变成了:
http://tieba.baidu.com/p/2296712428?see_lz=1&pn=1

可以看出来,see_lz=1是只看楼主,pn=1是对应的页码,记住这一点为以后的编写做准备。
这就是我们需要利用的url。接下来就是查看页面源码。
首先把题目抠出来存储文件的时候会用到。
可以看到百度使用gbk编码,标题使用h1标记:

  1. <h1 class="core_title_txt" title="【原创】时尚首席(关于时尚,名利,事业,爱情,励志)">【原创】时尚首席(关于时尚,名利,事业,爱情,励志)</h1>
复制代码

同样,正文部分用div和class综合标记,接下来要做的只是用正则表达式来匹配即可。
运行截图:
20130516135934681.png


生成的txt文件:
20130516140007820.png





  1. # -*- coding: utf-8 -*-  
  2. #---------------------------------------  
  3. #   程序:百度贴吧爬虫  
  4. #   版本:0.5  
  5. #   作者:why  
  6. #   日期:2013-05-16  
  7. #   语言:Python 2.7  
  8. #   操作:输入网址后自动只看楼主并保存到本地文件  
  9. #   功能:将楼主发布的内容打包txt存储到本地。  
  10. #---------------------------------------  
  11.    
  12. import string  
  13. import urllib2  
  14. import re  
  15.   
  16. #----------- 处理页面上的各种标签 -----------  
  17. class HTML_Tool:  
  18.     # 用非 贪婪模式 匹配 \t 或者 \n 或者 空格 或者 超链接 或者 图片  
  19.     BgnCharToNoneRex = re.compile("(\t|\n| |<a.*?>|<img.*?>)")  
  20.       
  21.     # 用非 贪婪模式 匹配 任意<>标签  
  22.     EndCharToNoneRex = re.compile("<.*?>")  
  23.   
  24.     # 用非 贪婪模式 匹配 任意<p>标签  
  25.     BgnPartRex = re.compile("<p.*?>")  
  26.     CharToNewLineRex = re.compile("(<br/>|</p>|<tr>|<div>|</div>)")  
  27.     CharToNextTabRex = re.compile("<td>")  
  28.   
  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("gbk")  
  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('gbk'))  
  109.               
  110.   
  111.     # 将内容从页面代码中抠出来  
  112.     def deal_data(self,myPage):  
  113.         myItems = re.findall('id="post_content.*?>(.*?)</div>',myPage,re.S)  
  114.         for item in myItems:  
  115.             data = self.myTool.Replace_Char(item.replace("\n","").encode('gbk'))  
  116.             self.datas.append(data+'\n')  
  117.   
  118.   
  119.   
  120. #-------- 程序入口处 ------------------  
  121. print u"""#---------------------------------------
  122. #   程序:百度贴吧爬虫
  123. #   版本:0.5
  124. #   作者:why
  125. #   日期:2013-05-16
  126. #   语言:Python 2.7
  127. #   操作:输入网址后自动只看楼主并保存到本地文件
  128. #   功能:将楼主发布的内容打包txt存储到本地。
  129. #---------------------------------------
  130. """  
  131.   
  132. # 以某小说贴吧为例子  
  133. # bdurl = 'http://tieba.baidu.com/p/2296712428?see_lz=1&pn=1'  
  134.   
  135. print u'请输入贴吧的地址最后的数字串:'  
  136. bdurl = 'http://tieba.baidu.com/p/' + str(raw_input(u'http://tieba.baidu.com/p/'))   
  137.   
  138. #调用  
  139. mySpider = Baidu_Spider(bdurl)  
  140. mySpider.baidu_tieba()  
复制代码



[Python]网络爬虫(十):一个爬虫的诞生全过程(以山东大学绩点运算为例)


先来说一下我们学校的网站:
http://jwxt.sdu.edu.cn:7777/zhxt_bks/zhxt_bks.html
查询成绩需要登录,然后显示各学科成绩,但是只显示成绩而没有绩点,也就是加权平均分。


20130711223605906.png


显然这样手动计算绩点是一件非常麻烦的事情。所以我们可以用python做一个爬虫来解决这个问题。


1.决战前夜

先来准备一下工具:HttpFox插件。
这是一款http协议分析插件,分析页面请求和响应的时间、内容、以及浏览器用到的COOKIE等。


以我为例,安装在火狐上即可,效果如图:
20130711224321968.png


可以非常直观的查看相应的信息。
点击start是开始检测,点击stop暂停检测,点击clear清除内容。
一般在使用之前,点击stop暂停,然后点击clear清屏,确保看到的是访问当前页面获得的数据。



2.深入敌后


下面就去山东大学的成绩查询网站,看一看在登录的时候,到底发送了那些信息。
先来到登录页面,把httpfox打开,clear之后,点击start开启检测:


20130711224811968.png


输入完了个人信息,确保httpfox处于开启状态,然后点击确定提交信息,实现登录。

这个时候可以看到,httpfox检测到了三条信息:

20130711225923828.png

这时点击stop键,确保捕获到的是访问该页面之后反馈的数据,以便我们做爬虫的时候模拟登陆使用。


3.庖丁解牛

乍一看我们拿到了三个数据,两个是GET的一个是POST的,但是它们到底是什么,应该怎么用,我们还一无所知。
所以,我们需要挨个查看一下捕获到的内容。

先看POST的信息:

20130711233017953.png


既然是POST的信息,我们就直接看PostData即可。
可以看到一共POST两个数据,stuid和pwd。
并且从Type的Redirect to可以看出,POST完毕之后跳转到了bks_login2.loginmessage页面。

由此看出,这个数据是点击确定之后提交的表单数据。

点击cookie标签,看看cookie信息:

20130711233913640.jpg


没错,收到了一个ACCOUNT的cookie,并且在session结束之后自动销毁。
那么提交之后收到了哪些信息呢?

我们来看看后面的两个GET数据。
先看第一个,我们点击content标签可以查看收到的内容,是不是有一种生吞活剥的快感-。-HTML源码暴露无疑了:


20130711233913640.jpg


看来这个只是显示页面的html源码而已,点击cookie,查看cookie的相关信息:

20130711234342468.png


啊哈,原来html页面的内容是发送了cookie信息之后才接受到的。

再来看看最后一个接收到的信息:

20130711234436812.png


大致看了一下应该只是一个叫做style.css的css文件,对我们没有太大的作用。

4.冷静应战


既然已经知道了我们向服务器发送了什么数据,也知道了我们接收到了什么数据,基本的流程如下:
  • 首先,我们POST学号和密码--->然后返回cookie的值
  • 然后发送cookie给服务器--->返回页面信息。
  • 获取到成绩页面的数据,用正则表达式将成绩和学分单独取出并计算加权平均数。

OK,看上去好像很简单的样纸。那下面我们就来试试看吧。
但是在实验之前,还有一个问题没有解决,就是POST的数据到底发送到了哪里?

再来看一下当初的页面:

20130712100513078.png


很明显是用一个html框架来实现的,也就是说,我们在地址栏看到的地址并不是右边提交表单的地址。
那么怎样才能获得真正的地址-。-右击查看页面源代码:
嗯没错,那个name="w_right"的就是我们要的登录页面。
网站的原来的地址是:
http://jwxt.sdu.edu.cn:7777/zhxt_bks/zhxt_bks.html
所以,真正的表单提交的地址应该是:
http://jwxt.sdu.edu.cn:7777/zhxt_bks/xk_login.html

输入一看,果不其然:


20130712100815500.png


靠居然是清华大学的选课系统。。。目测是我校懒得做页面了就直接借了。。结果连标题都不改一下。。。
但是这个页面依旧不是我们需要的页面,因为我们的POST数据提交到的页面,应该是表单form的ACTION中提交到的页面。

也就是说,我们需要查看源码,来知道POST数据到底发送到了哪里:

20130712111417671.png


嗯,目测这个才是提交POST数据的地址。
整理到地址栏中,完整的地址应该如下:
http://jwxt.sdu.edu.cn:7777/pls/wwwbks/bks_login2.login

(获取的方式很简单,在火狐浏览器中直接点击那个链接就能看到这个链接的地址了)


5.小试牛刀


接下来的任务就是:用python模拟发送一个POST的数据并取到返回的cookie值。


关于cookie的操作可以看看这篇博文:http://blog.csdn.net/wxg694175346/article/details/8925978

我们先准备一个POST的数据,再准备一个cookie的接收,然后写出源码如下:

  1. # -*- coding: utf-8 -*-
  2. #---------------------------------------
  3. #   程序:山东大学爬虫
  4. #   版本:0.1
  5. #   作者:why
  6. #   日期:2013-07-12
  7. #   语言:Python 2.7
  8. #   操作:输入学号和密码
  9. #   功能:输出成绩的加权平均值也就是绩点
  10. #---------------------------------------
  11. import urllib  
  12. import urllib2
  13. import cookielib
  14. cookie = cookielib.CookieJar()  
  15. opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
  16. #需要POST的数据#
  17. postdata=urllib.urlencode({  
  18.     'stuid':'201100300428',  
  19.     'pwd':'921030'  
  20. })
  21. #自定义一个请求#
  22. req = urllib2.Request(  
  23.     url = 'http://jwxt.sdu.edu.cn:7777/pls/wwwbks/bks_login2.login',  
  24.     data = postdata
  25. )
  26. #访问该链接#
  27. result = opener.open(req)
  28. #打印返回的内容#
  29. print result.read()   
复制代码

如此这般之后,再看看运行的效果:

20130712111913906.png


ok,如此这般,我们就算模拟登陆成功了。

6.[size=-1]偷天换日

[size=-1]

接下来的任务就是用爬虫获取到学生的成绩。
再来看看源网站。

开启HTTPFOX之后,点击查看成绩,发现捕获到了如下的数据:

20130712112705250.png

点击第一个GET的数据,查看内容可以发现Content就是获取到的成绩的内容。

而获取到的页面链接,从页面源代码中右击查看元素,可以看到点击链接之后跳转的页面(火狐浏览器只需要右击,“查看此框架”,即可):

20130712120338562.png


从而可以得到查看成绩的链接如下:

http://jwxt.sdu.edu.cn:7777/pls/wwwbks/bkscjcx.curscopre


7.万事俱备

现在万事俱备啦,所以只需要把链接应用到爬虫里面,看看能否查看到成绩的页面。
从httpfox可以看到,我们发送了一个cookie才能返回成绩的信息,所以我们就用python模拟一个cookie的发送,以此来请求成绩的信息:

  1. # -*- coding: utf-8 -*-
  2. #---------------------------------------
  3. #   程序:山东大学爬虫
  4. #   版本:0.1
  5. #   作者:why
  6. #   日期:2013-07-12
  7. #   语言:Python 2.7
  8. #   操作:输入学号和密码
  9. #   功能:输出成绩的加权平均值也就是绩点
  10. #---------------------------------------
  11. import urllib  
  12. import urllib2
  13. import cookielib
  14. #初始化一个CookieJar来处理Cookie的信息#
  15. cookie = cookielib.CookieJar()
  16. #创建一个新的opener来使用我们的CookieJar#
  17. opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
  18. #需要POST的数据#
  19. postdata=urllib.urlencode({  
  20.     'stuid':'201100300428',  
  21.     'pwd':'921030'  
  22. })
  23. #自定义一个请求#
  24. req = urllib2.Request(  
  25.     url = 'http://jwxt.sdu.edu.cn:7777/pls/wwwbks/bks_login2.login',  
  26.     data = postdata
  27. )
  28. #访问该链接#
  29. result = opener.open(req)
  30. #打印返回的内容#
  31. print result.read()
  32. #打印cookie的值
  33. for item in cookie:  
  34.     print 'Cookie:Name = '+item.name  
  35.     print 'Cookie:Value = '+item.value
  36.    
  37. #访问该链接#
  38. result = opener.open('http://jwxt.sdu.edu.cn:7777/pls/wwwbks/bkscjcx.curscopre')
  39. #打印返回的内容#
  40. print result.read()
复制代码

按下F5运行即可,看看捕获到的数据吧:

20130712121647796.png


既然这样就没有什么问题了吧,用正则表达式将数据稍稍处理一下,取出学分和相应的分数就可以了。


8.手到擒来


这么一大堆html源码显然是不利于我们处理的,下面要用正则表达式来抠出必须的数据。

关于正则表达式的教程可以看看这个博文:
http://blog.csdn.net/wxg694175346/article/details/8929576


我们来看看成绩的源码:

20130712125757031.png

既然如此,用正则表达式就易如反掌了。

我们将代码稍稍整理一下,然后用正则来取出数据:
  1. # -*- coding: utf-8 -*-
  2. #---------------------------------------
  3. #   程序:山东大学爬虫
  4. #   版本:0.1
  5. #   作者:why
  6. #   日期:2013-07-12
  7. #   语言:Python 2.7
  8. #   操作:输入学号和密码
  9. #   功能:输出成绩的加权平均值也就是绩点
  10. #---------------------------------------
  11. import urllib  
  12. import urllib2
  13. import cookielib
  14. import re
  15. class SDU_Spider:  
  16.     # 申明相关的属性  
  17.     def __init__(self):   
  18.         self.loginUrl = 'http://jwxt.sdu.edu.cn:7777/pls/wwwbks/bks_login2.login'   # 登录的url
  19.         self.resultUrl = 'http://jwxt.sdu.edu.cn:7777/pls/wwwbks/bkscjcx.curscopre' # 显示成绩的url
  20.         self.cookieJar = cookielib.CookieJar()                                      # 初始化一个CookieJar来处理Cookie的信息
  21.         self.postdata=urllib.urlencode({'stuid':'201100300428','pwd':'921030'})     # POST的数据
  22.         self.weights = []   #存储权重,也就是学分
  23.         self.points = []    #存储分数,也就是成绩
  24.         self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookieJar))
  25.     def sdu_init(self):
  26.         # 初始化链接并且获取cookie
  27.         myRequest = urllib2.Request(url = self.loginUrl,data = self.postdata)   # 自定义一个请求
  28.         result = self.opener.open(myRequest)            # 访问登录页面,获取到必须的cookie的值
  29.         result = self.opener.open(self.resultUrl)       # 访问成绩页面,获得成绩的数据
  30.         # 打印返回的内容
  31.         # print result.read()
  32.         self.deal_data(result.read().decode('gbk'))
  33.         self.print_data(self.weights);
  34.         self.print_data(self.points);
  35.     # 将内容从页面代码中抠出来  
  36.     def deal_data(self,myPage):  
  37.         myItems = re.findall('<TR>.*?<p.*?<p.*?<p.*?<p.*?<p.*?>(.*?)</p>.*?<p.*?<p.*?>(.*?)</p>.*?</TR>',myPage,re.S)     #获取到学分
  38.         for item in myItems:
  39.             self.weights.append(item[0].encode('gbk'))
  40.             self.points.append(item[1].encode('gbk'))
  41.             
  42.     # 将内容从页面代码中抠出来
  43.     def print_data(self,items):  
  44.         for item in items:  
  45.             print item
  46.             
  47. #调用  
  48. mySpider = SDU_Spider()  
  49. mySpider.sdu_init()  
复制代码

水平有限,,正则是有点丑,。运行的效果如图:

20130712133142421.png

ok,接下来的只是数据的处理问题了。。



9.凯旋而归


完整的代码如下,至此一个完整的爬虫项目便完工了。
  1. # -*- coding: utf-8 -*-
  2. #---------------------------------------
  3. #   程序:山东大学爬虫
  4. #   版本:0.1
  5. #   作者:why
  6. #   日期:2013-07-12
  7. #   语言:Python 2.7
  8. #   操作:输入学号和密码
  9. #   功能:输出成绩的加权平均值也就是绩点
  10. #---------------------------------------
  11. import urllib  
  12. import urllib2
  13. import cookielib
  14. import re
  15. import string
  16. class SDU_Spider:  
  17.     # 申明相关的属性  
  18.     def __init__(self):   
  19.         self.loginUrl = 'http://jwxt.sdu.edu.cn:7777/pls/wwwbks/bks_login2.login'   # 登录的url
  20.         self.resultUrl = 'http://jwxt.sdu.edu.cn:7777/pls/wwwbks/bkscjcx.curscopre' # 显示成绩的url
  21.         self.cookieJar = cookielib.CookieJar()                                      # 初始化一个CookieJar来处理Cookie的信息
  22.         self.postdata=urllib.urlencode({'stuid':'201100300428','pwd':'921030'})     # POST的数据
  23.         self.weights = []   #存储权重,也就是学分
  24.         self.points = []    #存储分数,也就是成绩
  25.         self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookieJar))
  26.     def sdu_init(self):
  27.         # 初始化链接并且获取cookie
  28.         myRequest = urllib2.Request(url = self.loginUrl,data = self.postdata)   # 自定义一个请求
  29.         result = self.opener.open(myRequest)            # 访问登录页面,获取到必须的cookie的值
  30.         result = self.opener.open(self.resultUrl)       # 访问成绩页面,获得成绩的数据
  31.         # 打印返回的内容
  32.         # print result.read()
  33.         self.deal_data(result.read().decode('gbk'))
  34.         self.calculate_date();
  35.     # 将内容从页面代码中抠出来  
  36.     def deal_data(self,myPage):  
  37.         myItems = re.findall('<TR>.*?<p.*?<p.*?<p.*?<p.*?<p.*?>(.*?)</p>.*?<p.*?<p.*?>(.*?)</p>.*?</TR>',myPage,re.S)     #获取到学分
  38.         for item in myItems:
  39.             self.weights.append(item[0].encode('gbk'))
  40.             self.points.append(item[1].encode('gbk'))
  41.     #计算绩点,如果成绩还没出来,或者成绩是优秀良好,就不运算该成绩
  42.     def calculate_date(self):
  43.         point = 0.0
  44.         weight = 0.0
  45.         for i in range(len(self.points)):
  46.             if(self.points[i].isdigit()):
  47.                 point += string.atof(self.points[i])*string.atof(self.weights[i])
  48.                 weight += string.atof(self.weights[i])
  49.         print point/weight
  50.             
  51. #调用  
  52. mySpider = SDU_Spider()  
  53. mySpider.sdu_init()  
复制代码



相关内容:

[Python]网络爬虫(一)、(二)

[Python]网络爬虫(三)、(四)

[Python]网络爬虫(五)、(六)

[Python]网络爬虫(七)(八)

[Python]网络爬虫(11)(12)




欢迎加入about云群90371779322273151432264021 ,云计算爱好者群,亦可关注about云腾讯认证空间||关注本站微信

已有(4)人评论

跳转到指定楼层
anyhuayong 发表于 2014-12-18 08:07:12
很喜欢,楼主辛苦
回复

使用道具 举报

三木公 发表于 2014-12-26 09:53:36
真心,感觉不错啊。谢谢分享啊
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条