[PYTHON]-用Scrapy爬虫遍历百度贴吧,本地保存文字版【PART 2】
2015-10-24 11:11
806 查看
这里紧接着上一节的内容讲:
上一节在这里:/article/3619553.html
上次我们定义了item.py\pipelines.py\settings.py,了解了布隆过滤。今天我们定义重头戏:定义蜘蛛
我们首先在脑海里回想一下我们的目标:
抓取所有的《中山大学吧》帖子标题+内容
我们先来到百度贴吧的中山大学主页。
我们看到,主页的URL是:
http://tieba.baidu.com/f?ie=utf-8&kw=%E4%B8%AD%E5%B1%B1%E5%A4%A7%E5%AD%A6&pn=1
这个怎么解析呢?首先,ie=utf-8是解码格式,kw=%E4%B8%AD%E5%B1%B1%E5%A4%A7%E5%AD%A6是“中山大学”编码后的字符串
pn=1是指当前的主页(mainpage)从第几个帖子开始显示,经过实验,pn可以等于任何一个正整数,也就是说当pn=25 主页就从第25个帖子开始显示,pn=44就从第44个帖子开始显示。
好。我们先想一下我们的结构:
【注意】所有的url都要先放入布隆过滤器,如果重复就不再抓取!
OK,我们想好了结构,就用chrome先来看主页的源代码,找找如何执行:主页==》某个帖子。
我们搜索帖名:
然后看到
我们分析,发现这是一个
的标签a,其包含的@href性质,就是我们要抓的网页。
所以用XPath表示是:
由于这里抓到的只是tieba.baidu.com/后面的一部分,所以要加回前面的域名。
现在我们进入了帖子!
问题在于:如何抓帖子的content ? 这个我们来看看具体帖子的源代码!
大家看到,黑色的是楼主发的文字。
红色的是独一无二的标签,匹配的是整个页面所有人发的内容。
我们只要其中“黑色的文本文字”,于是我们就这样抓取吧!
然后对于每个内容,我们输出到item里:
好了。然后我们想想,如果一个帖子有很多页,怎么抓每一页的东西呢?
光是想想不出来的,我们来看看具体的帖子:
有办法了,我们就利用这个”下一页“按钮去抓下一页!
截取的源代码:
我们可以看到,网址是a标签的href属性,而这个属性的父标签是
经过多个页面的观察发现,pager这个标签可以是_theme_4也可能是_theme_5 之类,不确定,所以我们用contains进行模糊匹配
我们之前说过,要用bloom fliter查重!所以我们这样处理:
第三个问题:如何获取标题?
我们说过,文档名就是贴子标题,这个要如何获取呢?
老办法了,抓源代码,看标签,用Xpath:
我们这里用
然后对于每个title,存到item里
太容易了!
最后一个问题:如何更新主页?
为什么要更新主页呢?我们知道,一个主页如果pn=0不更新的话,最多也就抓50个帖子
我可是要把百度贴吧爬下来的男人!怎么能容忍这个缺陷呢!
于是,我们做个计数器:每进去10次帖子,就把主页更新一遍。为了方便呢,我们把mainpage作为类的成员,计数器也做成类的成员。
然后我们只要,每次self.count%10==0,我们就把mainpage更新,pn=str(self.count)。
实现如下:
大功告成!
到了这一步,我们基本把蜘蛛文件的大部分搞定了,只需要回到pipelines看看之前没有认真完成的管道:
我们知道,我们在这个文件里做的,content和title定义成是utf-8编码的字符串,所以我们要做的
1.解码
2.write
很简单了是不是!
NO!
注意!!!! 每个帖子从第二页开始,标题格式都是这样的“回复:XXXXX”
如果简单地这样保存会使你的每个帖子有两个文件,文件名分别是【 帖名.txt】、【回复:帖名.txt】
我可是要把百度贴吧爬下来的男人!怎么能容忍这个缺陷呢!
好,我们要怎么做?
我们让key=”回复:”
我们用str的rfind(key)函数,返回字符串匹配key的最后一个下标,然后用split(key),取出第二部分,就大功告成了!代码如下
pipelines.py:
我们蜘蛛要设置两个解析函数:一个parse_mainPage负责处理主页,一个parse_inPage负责处理帖子。
parse_mainPage的作用:取出帖子的地址!
parse_inPage的作用:抓取title\content,然后抓取下一页,直到抓完。
蜘蛛完善好,把上面说到的几个Xpath放进去
对了,别忘了把bloom fliter.py保存到本地,【PART1】有源代码
最后贴上蜘蛛代码:
然后呢?
然后我们就等!
由于网速有限,我们几乎不可能高效率地爬下整个百度贴吧,只能爬某一两个吧。
有人会问了,露珠我是新手,感觉我很难一次写出这么长的代码啊
晕,我也是新手,所以我的建议如下:
一步步完成程序、由低到高实现功能!
什么意思?
本例:我是这样做的:
步骤一:把http://tieba.baidu.com/p/4117846868 这样的页面,单页面的内容爬下来
步骤二:升级代码,使得代码能自动把上述url的第二页、第三页…都抓下来
步骤三:升级item.py,标题过滤“回复:”关键字。
步骤四:升级代码,加入bloom fliter.py
步骤五:升级代码,使得“访问主页”变成第一步,改用start_request()
步骤六:升级代码,加入parse_inPage和parse_mainpage,加入计数器用于更新主页
建议!
一步一步学习,遇到不懂就百度
基本了解就开始实践,不要永远卡在理论阶段!
本程序可提升部分:
1. 多线程执行任务
2. 分布式抓取
3. 优化文本布局(这个最简单hhhh)
以后有提升会更新的,现在我要试着去爬你懂磁力链接了hhhhhhhh
(误 新世界的大门
上一节在这里:/article/3619553.html
上次我们定义了item.py\pipelines.py\settings.py,了解了布隆过滤。今天我们定义重头戏:定义蜘蛛
我们首先在脑海里回想一下我们的目标:
抓取所有的《中山大学吧》帖子标题+内容
我们先来到百度贴吧的中山大学主页。
我们看到,主页的URL是:
http://tieba.baidu.com/f?ie=utf-8&kw=%E4%B8%AD%E5%B1%B1%E5%A4%A7%E5%AD%A6&pn=1
这个怎么解析呢?首先,ie=utf-8是解码格式,kw=%E4%B8%AD%E5%B1%B1%E5%A4%A7%E5%AD%A6是“中山大学”编码后的字符串
pn=1是指当前的主页(mainpage)从第几个帖子开始显示,经过实验,pn可以等于任何一个正整数,也就是说当pn=25 主页就从第25个帖子开始显示,pn=44就从第44个帖子开始显示。
好。我们先想一下我们的结构:
【注意】所有的url都要先放入布隆过滤器,如果重复就不再抓取!
OK,我们想好了结构,就用chrome先来看主页的源代码,找找如何执行:主页==》某个帖子。
我们搜索帖名:
然后看到
我们分析,发现这是一个
class="j_th_tit "
的标签a,其包含的@href性质,就是我们要抓的网页。
所以用XPath表示是:
[code]sel.xpath('//a[@class="j_th_tit "]/@href').extract()
由于这里抓到的只是tieba.baidu.com/后面的一部分,所以要加回前面的域名。
[code] sel=Selector(response) sites=sel.xpath('//a[@class="j_th_tit "]/@href').extract() for site in sites: urls = "http://tieba.baidu.com"+site
现在我们进入了帖子!
问题在于:如何抓帖子的content ? 这个我们来看看具体帖子的源代码!
大家看到,黑色的是楼主发的文字。
红色的是独一无二的标签,匹配的是整个页面所有人发的内容。
我们只要其中“黑色的文本文字”,于是我们就这样抓取吧!
[code] contentList=site.xpath('//div[@class="d_post_content j_d_post_content "]/text()').extract()
然后对于每个内容,我们输出到item里:
[code] for floor in contentList: utfcontent=floor.encode('utf-8') item["content"] += utfcontent item["content"] += "\n"
好了。然后我们想想,如果一个帖子有很多页,怎么抓每一页的东西呢?
光是想想不出来的,我们来看看具体的帖子:
有办法了,我们就利用这个”下一页“按钮去抓下一页!
截取的源代码:
我们可以看到,网址是a标签的href属性,而这个属性的父标签是
<li>,标签性质class里含有pager关键字。
经过多个页面的观察发现,pager这个标签可以是_theme_4也可能是_theme_5 之类,不确定,所以我们用contains进行模糊匹配
[code] urlList=site.xpath('//li[contains(@class,"l_pager")]/a/@href').extract()
我们之前说过,要用bloom fliter查重!所以我们这样处理:
[code] for t in urlList: if (self.bf.is_element_exist("http://tieba.baidu.com"+t)==False): #判断是否已经保存过? yield Request("http://tieba.baidu.com"+t,callback=self.parse_inPage) else: continue
第三个问题:如何获取标题?
我们说过,文档名就是贴子标题,这个要如何获取呢?
老办法了,抓源代码,看标签,用Xpath:
我们这里用
[code] titleList=site.xpath('//h3[contains(@class,"core_title_txt")]/@title').extract()
然后对于每个title,存到item里
[code] for t in titleList: title=t.encode('utf-8') item["title"] += title yield item
太容易了!
最后一个问题:如何更新主页?
为什么要更新主页呢?我们知道,一个主页如果pn=0不更新的话,最多也就抓50个帖子
我可是要把百度贴吧爬下来的男人!怎么能容忍这个缺陷呢!
于是,我们做个计数器:每进去10次帖子,就把主页更新一遍。为了方便呢,我们把mainpage作为类的成员,计数器也做成类的成员。
然后我们只要,每次self.count%10==0,我们就把mainpage更新,pn=str(self.count)。
实现如下:
[code] self.count+=1 if self.count%10==0: self.mainpage="http://tieba.baidu.com/f?kw=%E4%B8%AD%E5%B1%B1%E5%A4%A7%E5%AD%A6&ie=utf-8&pn="+str(self.count) yield Request(self.mainpage,callback=self.parse_mainPage)
大功告成!
到了这一步,我们基本把蜘蛛文件的大部分搞定了,只需要回到pipelines看看之前没有认真完成的管道:
我们知道,我们在这个文件里做的,content和title定义成是utf-8编码的字符串,所以我们要做的
1.解码
2.write
很简单了是不是!
NO!
注意!!!! 每个帖子从第二页开始,标题格式都是这样的“回复:XXXXX”
如果简单地这样保存会使你的每个帖子有两个文件,文件名分别是【 帖名.txt】、【回复:帖名.txt】
我可是要把百度贴吧爬下来的男人!怎么能容忍这个缺陷呢!
好,我们要怎么做?
我们让key=”回复:”
我们用str的rfind(key)函数,返回字符串匹配key的最后一个下标,然后用split(key),取出第二部分,就大功告成了!代码如下
pipelines.py:
[code]class SysuPipeline(object): def __init(self): pass def process_item(self, item, spider): key='回复:' index=item["title"].rfind(key) #rfind() return the last index! if(index==-1): #not found! title=item["title"].decode('utf-8') #item['title'] has been encoded to utf8 else: t=item["title"].split(key)[1] title=t.decode('utf-8') path='D:\\guagua\\SYSU\\DATA\\'+ title +'.txt' output = open(path,'a') output.write(item["content"]) output.close() return item
我们蜘蛛要设置两个解析函数:一个parse_mainPage负责处理主页,一个parse_inPage负责处理帖子。
parse_mainPage的作用:取出帖子的地址!
parse_inPage的作用:抓取title\content,然后抓取下一页,直到抓完。
蜘蛛完善好,把上面说到的几个Xpath放进去
对了,别忘了把bloom fliter.py保存到本地,【PART1】有源代码
最后贴上蜘蛛代码:
[code]from scrapy.spider import Spider from scrapy.http import Request from scrapy.selector import Selector from SYSU.items import SysuItem from SYSU.bloomfliter import BloomFilter class SYSUSpider(Spider): name = 'SYSUSpider' allowed_domains=["tieba.baidu.com"] def __init__(self, name=None, **kwargs): if name is not None: self.name = name elif not getattr(self, 'name', None): raise ValueError("%s must have a name" % type(self).__name__) self.__dict__.update(kwargs) if not hasattr(self, 'start_urls'): self.start_urls = [] self.bf=BloomFilter(0.0001,100000) self.mainpage="http://tieba.baidu.com/f?kw=%E4%B8%AD%E5%B1%B1%E5%A4%A7%E5%AD%A6&ie=utf-8" self.count=0 def start_requests(self): yield Request(self.mainpage,callback=self.parse_mainPage) def parse_inPage(self,response): sel=Selector(response) site=sel url='' item=SysuItem() item["title"]='' item["content"]='' c=site.xpath('//link[@rel="canonical"]/@href').extract() currentUrl=c[0] self.bf.insert_element(currentUrl) contentList=site.xpath('//div[@class="d_post_content j_d_post_content "]/text()').extract() for floor in contentList: utfcontent=floor.encode('utf-8') item["content"] += utfcontent item["content"] += "\n" titleList=site.xpath('//h3[contains(@class,"core_title_txt")]/@title').extract() for t in titleList: title=t.encode('utf-8') item["title"] += title yield item urlList=site.xpath('//li[contains(@class,"l_pager")]/a/@href').extract() for t in urlList: if (self.bf.is_element_exist("http://tieba.baidu.com"+t)==False): # reduce a / yield Request("http://tieba.baidu.com"+t,callback=self.parse_inPage) else: continue self.count+=1 if self.count%10==0: self.mainpage="http://tieba.baidu.com/f?kw=%E4%B8%AD%E5%B1%B1%E5%A4%A7%E5%AD%A6&ie=utf-8&pn="+str(self.count) yield Request(self.mainpage,callback=self.parse_mainPage) def parse_mainPage(self,response): sel=Selector(response) sites=sel.xpath('//a[@class="j_th_tit "]/@href').extract() for site in sites: urls = "http://tieba.baidu.com"+site if(self.bf.is_element_exist(urls)==False): yield Request(urls,callback=self.parse_inPage) else: continue # cmd: guagua\SYSU>scrapy crawl SYSUSpider
然后呢?
然后我们就等!
由于网速有限,我们几乎不可能高效率地爬下整个百度贴吧,只能爬某一两个吧。
有人会问了,露珠我是新手,感觉我很难一次写出这么长的代码啊
晕,我也是新手,所以我的建议如下:
一步步完成程序、由低到高实现功能!
什么意思?
本例:我是这样做的:
步骤一:把http://tieba.baidu.com/p/4117846868 这样的页面,单页面的内容爬下来
步骤二:升级代码,使得代码能自动把上述url的第二页、第三页…都抓下来
步骤三:升级item.py,标题过滤“回复:”关键字。
步骤四:升级代码,加入bloom fliter.py
步骤五:升级代码,使得“访问主页”变成第一步,改用start_request()
步骤六:升级代码,加入parse_inPage和parse_mainpage,加入计数器用于更新主页
建议!
一步一步学习,遇到不懂就百度
基本了解就开始实践,不要永远卡在理论阶段!
本程序可提升部分:
1. 多线程执行任务
2. 分布式抓取
3. 优化文本布局(这个最简单hhhh)
以后有提升会更新的,现在我要试着去爬你懂磁力链接了hhhhhhhh
(误 新世界的大门
相关文章推荐
- 分分钟学会一门语言之Python篇
- Python WSGI详解
- 在VS中配置Python编译环境
- python知识框架
- cx_freeze3.4.3 打包python3.4 出现 _fix_up_module 错误
- python批量下载淘宝图片3
- sublime text 2 调试python时结果空白
- 基于Python的江苏大学校园网暴力破解
- python 性能- and-or 学习技能
- python批量下载图片3
- Python集合(set)类型的操作
- leetcode之Invert Binary Tree
- leetcode之Same Tree
- [PYTHON]-用Scrapy爬虫遍历百度贴吧,本地保存文字版【PART 1】
- Python -- 序列化
- python下安装numpy、matplotlib、scipy过程
- Python 书籍推荐
- Python数据类型之间的转换
- VIM配置Python环境
- Python模块包中__init__.py文件 精髓