数据采集实验-爬取李开复博客并保存在csv和mongodb中
文章目录
- 实验目的
- 实验要求
- 实验程序及运行结果
- 实验内容概述
- 代码解说
- 翻页:通过回调函数实现翻页。
- 显示页数:利用response.xpath获得当前页数。
- 爬取目录页:标题、时间、链接。
- 爬取动态数据:阅读数,喜欢数,评论数,转发数,收藏数。
- 爬取详情页:标题,时间,分类,标签。
- 处理时间和标题数据:def chuli(item)。
- 数据存入csv和mongodb:不同item存入不同文件、表。
- 绘制进度条:利用sys绘制(伪)进度条。
- 运行结果:
- 控制台
- csv文件
- mongodb数据库
- 实验总结
- 实验中遇到的部分错误与解决方法:
- pycharm安装scrapy
- scrapy运行
- pymongo安装
- 实验感悟:
实验目的
- 掌握python中获取网页的基本库,解析网页的基本方法。
- 掌握使用Scrapy等爬虫工具编写爬虫程序的基本思路。
- 掌握抓取列表+详情的静态组合页面的方法。
实验要求
- 抓取目标。可以选择以下网站作为抓取目标,也可以寻找自己感兴趣的抓取目标:
1)“李开复的博客”:http://blog.sina.com.cn/kaifulee
2)腾讯公益:http://gongyi.qq.com
3)人民网滚动新闻:http://news.people.com.cn - 任务要求。
1) 分析页面结构,确定待抓取的数据项,抓取文章标题、发表时间、正文内容、文章URL、作者、新闻来源、责编
评分标准:满分35分;每个数据项5分,能够获取主要数据项4项,30分,抓取额外数据项目2项以上给满分;
2) 正确处理目录页面和正文页面,能够自动抓取至少100篇网页内容。
评分标准:满分25分;每20条数据5分,根据爬取的数量给分超过100篇,满分;
3) 数据持久化。将数据存入磁盘文件,Scrapy可以参考下列文章:
https://blog.csdn.net/qy20115549/article/details/52575291
评分标准:满分20分;根据无写入,写入文本或Excel,写入数据库确定分数;无写入0分,写入文本或Excel 10分;写入数据库20;
4) 数据处理
评分标准:满分15分;提到数据处理5分;每个数据处理问题5分,解决两个以上数据处理问题的给予15分;
5) 报告整体情况:
评分标准:报告格式整洁程度5分;
加分:实验报告中未要求的但在实验过程中发现新的问题,每个加5分,不超过10分。
实验程序及运行结果
实验内容概述
本程序使用scrapy架构,共采集6页李开复博客目录与252篇博文信息,实现了数据清洗处理,在控制台输出问题数据,并将修改后的数据分别存入mongodb数据库和csv文件,其间有进度条显示程序运行进度。
代码解说
(温馨提示:本文为实验报告,其中代码为方便呈现稍有改动,代码完整版位于github上,选择版本:爬虫-1)
翻页:通过回调函数实现翻页。
def parse(self, response):# 翻页next_url = response.xpath("//div[@class='SG_page']/ul/li[@class='SG_pgnext']/a/@href").extract_first()if next_url is not None:yield scrapy.Request(next_url, callback=self.parse, dont_filter=True)pass
显示页数:利用response.xpath获得当前页数。
由于添加了进度条,这块代码被注释,使控制台输出更美观。
# 显示当前页数
num_url = response.xpath("//div[@class='SG_page']/ul/li[@class='SG_pgon']/text()").extract_first()
print("\n正在爬取第", num_url, "页")
爬取目录页:标题、时间、链接。
链接用于爬取动态数据与详情页,数据存在item1(MuLuItem)。
node_list = response.xpath('//div[@class="articleList"]')
for node in node_list:item1 = MuLuItem()# 爬取目录页(标题、时间、链接)if node.xpath('//p[@class="atc_main SG_dot"]/span[2]/a/text()').extract():item1['标题'] = node.xpath('//p[@class="atc_main SG_dot"]/span[2]/a/text()').extract()if node.xpath('//p[@class="atc_info"]/span[2]/text()').extract():item1['时间'] = node.xpath('//p[@class="atc_info"]/span[2]/text()').extract()if node.xpath('//p[@class="atc_main SG_dot"]/span[2]/a/@href').extract():item1['链接'] = node.xpath('//p[@class="atc_main SG_dot"]/span[2]/a/@href').extract()yield item1
爬取动态数据:阅读数,喜欢数,评论数,转发数,收藏数。
利用item1的链接组合得到动态数据api,调用函数爬虫,数据存在item2(ShuJvItem),利用if判断是否为空,非空才爬。
# 爬取详情页数据
for num in range(len(item1['链接'])):w = str(item1['链接'][num][-21:-13])q = str(item1['链接'][num][-11:-5])detail_url = 'http://comet.blog.sina.com.cn/api?maintype=num&uid=' + w + '&aids=' + qitem2 = ShuJvItem()item2['链接'] = 'http://blog.sina.com.cn/s/blog_' + w + '01' + q + '.html'# 请求详情页动态数据(阅读数,喜欢数,评论数,转发数,收藏数)request = scrapy.Request(url=detail_url, callback=self.post_detail)request.meta['item2'] = item2yield request# 请求详情页动态数据(阅读数,喜欢数,评论数,转发数,收藏数)
def post_detail(self, response):item2 = response.meta.get('item2')pattern = re.compile(r"\d+")reads = re.findall(pattern, response.text)if reads[-5]:item2['收藏'] = reads[-5]if reads[-4]:item2['喜欢'] = reads[-4]if reads[-3]:item2['阅读'] = reads[-3]if reads[-2]:item2['转载'] = reads[-2]if reads[-1]:item2['评论'] = reads[-1]yield item2
爬取详情页:标题,时间,分类,标签。
利用item1的链接调用函数爬虫,数据存在item3(WenZhangItem),利用if判断是否为空,非空才爬(由于标题和时间不为空,标题和时间数据在pipelines中进行处理)。
# 请求详情页静态数据(标题,时间,分类,标签)
item3 = WenZhangItem()
request1 = scrapy.Request(url=str(item1['链接'][num]), callback=self.post_detail2)
request1.meta['item3'] = item3
yield request1# 请求详情页静态数据(标题,时间,分类,标签)
def post_detail2(self, response):item3 = response.meta.get('item3')item3['标题'] = response.xpath('//div[@class="articalTitle"]/h2/text()').extract()item3['时间'] = response.xpath('//div[@class="articalTitle"]/span[2]/text()').extract()if response.xpath('//div[@class="articalTag"]/table/tr/td[@class="blog_class"]/a/text()').extract():item3['分类'] = response.xpath('//div[@class="articalTag"]/table/tr/td[@class="blog_class"]/a/text()').extract()if response.xpath('//div[@class="articalTag"]/table/tr/td[@class="blog_tag"]/h3/text()').extract():item3['标签'] = response.xpath('//div[@class="articalTag"]/table/tr/td[@class="blog_tag"]/h3/text()').extract()yield item3
处理时间和标题数据:def chuli(item)。
根据观察发现爬下来的时间普遍带括号,部分时间为空(\n\t\t\t\t),部分标题中存在\xa0、\u200b、\u3000,其余数据没有问题。利用正则表达式提取时间,利用translate替换标题中的特殊字符,并输出修改的标题。
def chuli(item):if isinstance(item, WenZhangItem):adapter = ItemAdapter(item)old_bt = adapter['标题']old_sj = adapter['时间']# 处理标题:去除标题中的\xa0、\u200b、\u3000try:if str(adapter['标题']) != "":move = dict.fromkeys((ord(c) for c in u"\xa0\u200b\u3000"))new = adapter['标题'][0].translate(move) # new 是字符串adapter['标题'] = new.split('\n') # 转为列表if str(old_bt) != str(adapter['标题']):print("有问题的的标题", old_bt)print("处理后的结果", adapter['标题'])else:raise DropItem(f"{item}该项缺乏标题")except:raise DropItem(f"{item}该项缺乏标题")# 处理时间:丢弃没有时间的 item, 将时间外的小括号去除try:# (2015-02-15 21:30:41)——去掉小括号,利用正则表达式匹配# print("前",adapter['时间'])# 前['\n\t\t\t\t', '\t\n\t\t\t']# 后[]if adapter['时间'] != ['\n\t\t\t\t', '\t\n\t\t\t']:adapter['时间'] = re.findall(r"(\d{4}-\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2})", str(adapter['时间']))# print("后",adapter['时间'])else:raise DropItem(f"{item}该项缺乏时间")# 由于带括号的时间数目过多,为减少if old_sj != adapter['时间']:print("\n处理前的时间数据为:", old_sj)print("处理后的时间数据为:", adapter['时间'])except:raise DropItem(f"{item}该项缺乏时间")return item
数据存入csv和mongodb:不同item存入不同文件、表。
代码参考自scrapy官方文档。
class collectBlogsPipeline(object):def open_spider(self, spider):# 打开/创建 csv 文件self.f1 = open("文章.csv", "w", encoding='utf-8')self.f2 = open("数据.csv", "w", encoding='utf-8')self.f3 = open("目录.csv", "w", encoding='utf-8')# 准备 mongodbhost = spider.settings.get("MONGODB_HOST", "localhost")port = spider.settings.get("MONGODB_PORT", 27017)db_name = spider.settings.get("MONGODB_NAME", "mydb2")collecton_name_one = spider.settings.get("MONGODB_COLLECTON", "文章")collecton_name_two = spider.settings.get("MONGODB_COLLECTON", "数据")collecton_name_three = spider.settings.get("MONGODB_COLLECTON", "目录")# 连接Mongodb,得到一个客户端对象self.db_client = pymongo.MongoClient(host=host, port=port)# 指定数据库,得到一个数据库对象self.db = self.db_client[db_name]# 指定集合,得到一个集合对象self.db_collecton1 = self.db[collecton_name_one]self.db_collecton2 = self.db[collecton_name_two]self.db_collecton3 = self.db[collecton_name_three]def process_item(self, item, spider):if isinstance(item, WenZhangItem):chuli(item)item_dict = dict(item) # 将item转换成字典self.db_collecton1.insert_one(item_dict) # 将数据插入到集合elif isinstance(item, ShuJvItem):item_dict = dict(item)self.db_collecton2.insert_one(item_dict)elif isinstance(item, MuLuItem):item_dict = dict(item)self.db_collecton3.insert_one(item_dict)# 将数据存入csv文件if isinstance(item, WenZhangItem):chuli(item)content = json.dumps(dict(item), ensure_ascii=False) + ",\n"self.f1.write(content)elif isinstance(item, ShuJvItem):content = json.dumps(dict(item), ensure_ascii=False) + ",\n"self.f2.write(content)elif isinstance(item, MuLuItem):content = json.dumps(dict(item), ensure_ascii=False) + ",\n"self.f3.write(content)def close_spider(self, spider):self.db_client.close()self.f1.close()self.f2.close()self.f3.close()
绘制进度条:利用sys绘制(伪)进度条。
由于process_item函数被多次调用,所以进度条多次出现。
class collectBlogsPipeline(object):progress = 0def process_item(self, item, spider):# 显示进度条self.progress += 1print("\r", end="\r")# 5.1是因为函数共运行510次,10是由于进度条显示效果太长,所以改为5,都是通过测试得出的,不能通用print("总程序进度: {:.2f}%: ,正在写入数据".format(self.progress / 5.1), "▋" * (self.progress // 7), end="\r")# 刷新缓冲区sys.stdout.flush()
运行结果:
控制台
csv文件
mongodb数据库
实验总结
实验中遇到的部分错误与解决方法:
pycharm安装scrapy
大部分人不会遇到这个困扰(因为周围只有我一个遇到了
数据采集实验-爬取李开复博客并保存在csv和mongodb中相关推荐
- python requests爬虫——爬取李开复博客信息(包括javascript部分信息)
今天是国庆假期第二天,已经玩了一天了,今天整理一下前两天写的数据分析作业思路,给实验报告打一下底稿.供对爬虫有兴趣的小伙伴们参考,也希望给实验没完成的同学提供一点小小的帮助. 任务要求. 1)分析页面 ...
- 爬取李开复博客并导入mongodb数据库
1.实验目的 l 掌握使用Scrapy等爬虫工具编写爬虫程序的基本思路: l 掌握抓取列表+详情的静态组合页面的方法 2.实验要求 抓取目标.可以选择以下网站作为抓取目标,也可以自行寻找自己感兴趣的抓 ...
- Python爬虫小实践:爬取任意CSDN博客所有文章的文字内容(或可改写为保存其他的元素),间接增加博客访问量...
Python并不是我的主业,当初学Python主要是为了学爬虫,以为自己觉得能够从网上爬东西是一件非常神奇又是一件非常有用的事情,因为我们可以获取一些方面的数据或者其他的东西,反正各有用处. 这两天闲 ...
- 使用Python爬取CSDN历史博客文章列表,并生成目录
使用Python爬取CSDN历史博客文章列表,并生成目录 这篇博客将介绍如何使用Python爬取CSDN历史博客文章列表,并生成目录. 2020年 2020年04月 cv2.threshold() 阈 ...
- Selenium3+python3自动化(四十三)--爬取我的博客园粉丝的名称,并写入.text文件...
爬取目标 1.爬取目标网站:我的博客:https://home.cnblogs.com/u/canglongdao/followers/ 爬取内容:爬取我的博客的所有粉丝的名称,并保存到txt 3.由 ...
- python爬虫教程:基于python框架Scrapy爬取自己的博客内容过程详解
前言 python中常用的写爬虫的库常有urllib2.requests,对于大多数比较简单的场景或者以学习为目的,可以用这两个库实现.这里有一篇我之前写过的用urllib2+BeautifulSou ...
- Python Ajax爬取微博个人博客数据
文章目录 利用request.pyquery.xlwings等库抓取微博个人博客数据. (1)抓取[目标网址](https://m.weibo.cn/u/2830678474) (2)用 Chrome ...
- 开发记录_自学Python写爬虫程序爬取csdn个人博客信息
每天刷开csdn的博客,看到一整个页面,其实对我而言,我只想看看访问量有没有上涨而已... 于是萌生了一个想法: 想写一个爬虫程序把csdn博客上边的访问量和评论数都爬下来. 打算通过网络各种搜集资料 ...
- python爬虫入门教程-Python爬虫入门教程——爬取自己的博客园博客
互联网时代里,网络爬虫是一种高效地信息采集利器,可以快速准确地获取网上的各种数据资源.本文使用Python库requests.Beautiful Soup爬取博客园博客的相关信息,利用txt文件转存. ...
最新文章
- mysql5.6 错误日志_MySQL5.6.36 日志文件之错误日志,二进制日志,慢日志
- 用反向传导模拟共振并用共振频率作分类
- Spring/Spring Boot微服务项目 集成Druid 实现监控功能
- boost::all_degree_centralities用法的测试程序
- 关于 paddingFactor 及 COLLMOD 的设置值
- undefined reference to `boost::system::generic_category()
- c++的文件输入/输出
- python读取数据库数据类型_Python实现从SQL型数据库读写dataframe型数据的方法【基于pandas】...
- 究极探秘!你知道程序员最喜欢做的四件事是什么吗?
- java中常量定义在interface中好还是定义在class中
- 设计模式(二 三)工厂模式:1-简单工厂模式
- 在vuex的mutations中使用vue的小技巧
- Navicat连接Oracle数据库
- linux fdisk指定ext4,如何在Linux中创建新的Ext4文件系统(分区)
- 后场村的加班别有不同
- 死链接处理,seo站长必会
- 《打地鼠》游戏简单制作
- LOJ 534 花团(线段树+dfs栈)
- 动态拼图怎么做?如何将多张动图拼接在一起?
- 供应链金融服务平台系统开发-成熟、稳定、节本、增效,一站式信息交易管理平台