热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

用Scrapy爬虫框架爬取食品论坛数据并存入数据库

导读这篇文章主要给大家介绍了食品网站的数据采集和存储过程,详解了如何分析网页结构、爬虫策略、网站类型、层级关系、爬虫方法和数据存储过程,最终实现将帖子的每条评论爬取到数据库中,并且
导读 这篇文章主要给大家介绍了食品网站的数据采集和存储过程,详解了如何分析网页结构、爬虫策略、网站类型、层级关系、爬虫方法和数据存储过程,最终实现将帖子的每条评论爬取到数据库中,并且做到可以更新数据,防止重复爬取,反爬等,干货满满。

网络爬虫(又称为网页蜘蛛,网络机器人),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。------百度百科

说人话就是,爬虫是用来海量规则化获取数据,然后进行处理和运用,在大数据、金融、机器学习等等方面都是必须的支撑条件之一。

目前在一线城市中,爬虫的岗位薪资待遇都是比较客观的,之后提升到中、高级爬虫工程师,数据分析师、大数据开发岗位等,都是很好的过渡。

本此介绍的项目其实不用想的太过复杂,最终要实现的目标也就是将帖子的每条评论爬取到数据库中,并且做到可以更新数据,防止重复爬取,反爬等措施。

这部分主要是介绍本文需要用到的工具,涉及的库,网页等信息等

软件:PyCharm

需要的库:Scrapy, selenium, pymongo, user_agent,datetime

目标网站:

http://bbs.foodmate.net 

插件:chromedriver(版本要对)

简而言之:确定网站的加载方式,怎样才能正确的一级一级的进入到帖子中抓取数据,使用什么格式保存数据等。

其次,观察网站的层级结构,也就是说,怎么根据板块,一点点进入到帖子页面中,这对本次爬虫任务非常重要,也是主要编写代码的部分。

目前我知道的爬虫方法大概有如下(不全,但是比较常用):

1)request框架:运用这个http库可以很灵活的爬取需要的数据,简单但是过程稍微繁琐,并且可以配合抓包工具对数据进行获取。但是需要确定headers头以及相应的请求参数,否则无法获取数据;很多app爬取、图片视频爬取随爬随停,比较轻量灵活,并且高并发与分布式部署也非常灵活,对于功能可以更好实现。

2)scrapy框架:scrapy框架可以说是爬虫最常用,最好用的爬虫框架了,优点很多:scrapy 是异步的;采取可读性更强的 xpath 代替正则;强大的统计和 log 系统;同时在不同的 url 上爬行;支持 shell 方式,方便独立调试;支持写 middleware方便写一些统一的过滤器;可以通过管道的方式存入数据库等等。这也是本次文章所要介绍的框架(结合selenium库)。

首先解释一下是什么意思,看什么网站,首先要看网站的加载方式,是静态加载,还是动态加载(js加载),还是别的方式;根据不一样的加载方式需要不同的办法应对。然后我们观察今天爬取的网站,发现这是一个有年代感的论坛,首先猜测是静态加载的网站;我们开启组织 js 加载的插件,如下图所示。

用Scrapy爬虫框架爬取食品论坛数据并存入数据库

刷新之后发现确实是静态网站(如果可以正常加载基本都是静态加载的)。

其次,我们今天要爬取的网站是食品论坛网站,是静态加载的网站,在之前分析的时候已经了解了,然后是层级结构:

用Scrapy爬虫框架爬取食品论坛数据并存入数据库

大概是上面的流程,总共有三级递进访问,之后到达帖子页面,如下图所示。

用Scrapy爬虫框架爬取食品论坛数据并存入数据库

部分代码展示:

一级界面:

def parse(self, response): 
    self.logger.info("已进入网页!") 
    self.logger.info("正在获取版块列表!") 
    column_path_list = response.css('#ct > div.mn > div:nth-child(2) > div')[:-1] 
    for column_path in column_path_list: 
        col_paths = column_path.css('div > table > tbody > tr > td > div > a').xpath('@href').extract() 
        for path in col_paths: 
            block_url = response.urljoin(path) 
            yield scrapy.Request( 
                url=block_url, 
                callback=self.get_next_path, 
            ) 

二级界面:

def get_next_path(self, response): 
    self.logger.info("已进入版块!") 
    self.logger.info("正在获取文章列表!") 
    if response.url == 'http://www.foodmate.net/know/': 
        pass 
    else: 
        try: 
            nums = response.css('#fd_page_bottom > div > label > span::text').extract_first().split(' ')[-2] 
        except: 
            nums = 1 
        for num in range(1, int(nums) + 1): 
            tbody_list = response.css('#threadlisttableid > tbody') 
            for tbody in tbody_list: 
                if 'normalthread' in str(tbody): 
                    item = LunTanItem() 
                    item['article_url'] = response.urljoin( 
                        tbody.css('* > tr > th > a.s.xst').xpath('@href').extract_first()) 
                    item['type'] = response.css( 
                        '#ct > div > div.bm.bml.pbn > div.bm_h.cl > h1 > a::text').extract_first() 
                    item['title'] = tbody.css('* > tr > th > a.s.xst::text').extract_first() 
                    item['spider_type'] = "论坛" 
                    item['source'] = "食品论坛" 
                    if item['article_url'] != 'http://bbs.foodmate.net/': 
                        yield scrapy.Request( 
                            url=item['article_url'], 
                            callback=self.get_data, 
                            meta={'item': item, 'content_info': []} 
                        ) 
        try: 
            callback_url = response.css('#fd_page_bottom > div > a.nxt').xpath('@href').extract_first() 
            callback_url = response.urljoin(callback_url) 
            yield scrapy.Request( 
                url=callback_url, 
                callback=self.get_next_path, 
            ) 
        except IndexError: 
            pass 

三级界面:

def get_data(self, response): 
    self.logger.info("正在爬取论坛数据!") 
    item = response.meta['item'] 
    content_list = [] 
    divs = response.xpath('//*[@id="postlist"]/div') 
    user_name = response.css('div > div.pi > div:nth-child(1) > a::text').extract() 
    publish_time = response.css('div.authi > em::text').extract() 
    floor = divs.css('* strong> a> em::text').extract() 
    s_id = divs.xpath('@id').extract() 
    for i in range(len(divs) - 1): 
        cOntent= '' 
        try: 
 
            strOng= response.css('#postmessage_' + s_id[i].split('_')[-1] + '').xpath('string(.)').extract() 
            for s in strong: 
                content += s.split(';')[-1].lstrip('/r/n') 
            datas = dict(cOntent=content,  # 内容 
                         reply_id=0,  # 回复的楼层,默认0 
                         user_name=user_name[i],  # ⽤户名 
                         publish_time=publish_time[i].split('于 ')[-1],  # %Y-%m-%d %H:%M:%S' 
                         id='#' + floor[i],  # 楼层 
                         ) 
            content_list.append(datas) 
        except IndexError: 
            pass 
    item['content_info'] = response.meta['content_info'] 
    item['scrawl_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 
    item['content_info'] += content_list 
 
    data_url = response.css('#ct > div.pgbtn > a').xpath('@href').extract_first() 
    if data_url != None: 
        data_url = response.urljoin(data_url) 
        yield scrapy.Request( 
            url=data_url, 
            callback=self.get_data, 
            meta={'item': item, 'content_info': item['content_info']} 
        ) 
    else: 
        item['scrawl_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 
        self.logger.info("正在存储!") 
        print('储存成功') 
        yield item 

由于是静态网页,首先决定采用的是scrapy框架直接获取数据,并且通过前期测试发现方法确实可行,不过当时年少轻狂,小看了网站的保护措施,由于耐心有限,没有加上定时器限制爬取速度,导致我被网站加了限制,并且网站由静态加载网页变为:动态加载网页验证算法之后再进入到该网页,直接访问会被后台拒绝。

但是这种问题怎么会难道我这小聪明,经过我短暂地思考(1天),我将方案改为scrapy框架 + selenium库的方法,通过调用chromedriver,模拟访问网站,等网站加载完了再爬取不就完了,后续证明这个方法确实可行,并且效率也不错。

实现部分代码如下:

def process_request(self, request, spider): 
    chrome_optiOns= Options() 
    chrome_options.add_argument('--headless')  # 使用无头谷歌浏览器模式 
    chrome_options.add_argument('--disable-gpu') 
    chrome_options.add_argument('--no-sandbox') 
    # 指定谷歌浏览器路径 
    self.driver = webdriver.Chrome(chrome_optiOns=chrome_options, 
                                   executable_path='E:/pycharm/workspace/爬虫/scrapy/chromedriver') 
    if request.url != 'http://bbs.foodmate.net/': 
        self.driver.get(request.url) 
        html = self.driver.page_source 
        time.sleep(1) 
        self.driver.quit() 
        return scrapy.http.HtmlResponse(url=request.url, body=html.encode('utf-8'), encoding='utf-8', 
                                        request=request) 

这部分不用多说,根据自己需求,将需要爬取的数据格式设置在items.py中。在工程中引用该格式保存即可

class LunTanItem(scrapy.Item): 
    """ 
        论坛字段 
    """ 
    title = Field()  # str: 字符类型 | 论坛标题 
    content_info = Field()  # str: list类型 | 类型list: [LunTanContentInfoItem1, LunTanContentInfoItem2] 
    article_url = Field()  # str: url | 文章链接 
    scrawl_time = Field()  # str: 时间格式 参照如下格式 2019-08-01 10:20:00 | 数据爬取时间 
    source = Field()  # str: 字符类型 | 论坛名称 eg: 未名BBS, 水木社区, 天涯论坛 
    type = Field()  # str: 字符类型 | 板块类型 eg: '财经', '体育', '社会' 
    spider_type = Field()  # str: forum | 只能写 'forum' 

本次项目选择保存的数据库为mongodb,由于是非关系型数据库,优点显而易见,对格式要求没有那么高,可以灵活储存多维数据,一般是爬虫优选数据库(不要和我说redis,会了我也用,主要是不会)

代码:

import pymongo 
 
class FMPipeline(): 
    def __init__(self): 
        super(FMPipeline, self).__init__() 
        # client = pymongo.MongoClient('139.217.92.75') 
        client = pymongo.MongoClient('localhost') 
        db = client.scrapy_FM 
        self.collection = db.FM 
 
    def process_item(self, item, spider): 
        query = { 
            'article_url': item['article_url'] 
        } 
        self.collection.update_one(query, {"$set": dict(item)}, upsert=True) 
        return item 

这时,有聪明的盆友就会问:如果运行两次爬取到了一样的数据怎么办呢?(换句话说就是查重功能)

这个问题之前我也没有考虑,后来在我询问大佬的过程中知道了,在我们存数据的时候就已经做完这件事了,就是这句:

query = { 
    'article_url': item['article_url'] 
} 
self.collection.update_one(query, {"$set": dict(item)}, upsert=True) 

通过帖子的链接确定是否有数据爬取重复,如果重复可以理解为将其覆盖,这样也可以做到更新数据。

像多线程、headers头,管道传输顺序等问题,都在settings.py文件中设置,具体可以参考小编的项目去看,这里不再赘述。

1、点击运行,结果显示在控制台,如下图所示。

用Scrapy爬虫框架爬取食品论坛数据并存入数据库

2、中间会一直向队列中堆很多帖子的爬取任务,然后多线程处理,我设置的是16线程,速度还是很可观的。

用Scrapy爬虫框架爬取食品论坛数据并存入数据库

用Scrapy爬虫框架爬取食品论坛数据并存入数据库

content_info中存放着每个帖子的全部留言以及相关用户的公开信息。

1、这篇文章主要给大家介绍了食品网站的数据采集和存储过程,详解了如何分析网页结构、爬虫策略、网站类型、层级关系、爬虫方法和数据存储过程,最终实现将帖子的每条评论爬取到数据库中,并且做到可以更新数据,防止重复爬取,反爬等,干货满满。

2、本次项目总的来说,不是特别难搞,只要思路对了,找到了数据规则,爬起来可以说易如反掌,觉得难只是之前没有完整走过流程,有了这次比较水的介绍,希望能对你有所帮助,那将是我最大的荣幸。

3、遇到问题首先想的不是问同事,朋友,老师,而是去谷歌,百度,看有没有相似的情况,看别人的经历,一定要学会自己发现问题,思考问题,解决问题,这对于之后工作有非常大的帮助(我之前就被说过还没有脱离学生时代,就是我喜欢问同事),等网上查询了一定资料了,还是没有头绪,再去问别人,别人也会比较愿意帮助你的~


推荐阅读
  • EST:西湖大学鞠峰组污水厂病原菌与土著反硝化细菌是多重抗生素耐药基因的活跃表达者...
    点击蓝字关注我们编译:祝新宇校稿:鞠峰、袁凌论文ID原名:PathogenicandIndigenousDenitrifyingBacte ... [详细]
  • Python 数据可视化实战指南
    本文详细介绍如何使用 Python 进行数据可视化,涵盖从环境搭建到具体实例的全过程。 ... [详细]
  • 网站访问全流程解析
    本文详细介绍了从用户在浏览器中输入一个域名(如www.yy.com)到页面完全展示的整个过程,包括DNS解析、TCP连接、请求响应等多个步骤。 ... [详细]
  • 服务器部署中的安全策略实践与优化
    服务器部署中的安全策略实践与优化 ... [详细]
  • Python 3 Scrapy 框架执行流程详解
    本文详细介绍了如何在 Python 3 环境下安装和使用 Scrapy 框架,包括常用命令和执行流程。Scrapy 是一个强大的 Web 抓取框架,适用于数据挖掘、监控和自动化测试等多种场景。 ... [详细]
  • 本项目通过Python编程实现了一个简单的汇率转换器v1.02。主要内容包括:1. Python的基本语法元素:(1)缩进:用于表示代码的层次结构,是Python中定义程序框架的唯一方式;(2)注释:提供开发者说明信息,不参与实际运行,通常每个代码块添加一个注释;(3)常量和变量:用于存储和操作数据,是程序执行过程中的重要组成部分。此外,项目还涉及了函数定义、用户输入处理和异常捕获等高级特性,以确保程序的健壮性和易用性。 ... [详细]
  • 性能测试中的关键监控指标与深入分析
    在软件性能测试中,关键监控指标的选取至关重要。主要目的包括:1. 评估系统的当前性能,确保其符合预期的性能标准;2. 发现软件性能瓶颈,定位潜在问题;3. 优化系统性能,提高用户体验。通过综合分析这些指标,可以全面了解系统的运行状态,为后续的性能改进提供科学依据。 ... [详细]
  • B站服务器故障影响豆瓣评分?别担心,阿里巴巴架构师分享预防策略与技术方案
    13日晚上,在视频观看高峰时段,B站出现了服务器故障,引发网友在各大平台上的广泛吐槽。这一事件导致了连锁反应,大量用户纷纷涌入A站、豆瓣和晋江等平台,给这些网站带来了突如其来的流量压力。为了防止类似问题的发生,阿里巴巴架构师分享了一系列预防策略和技术方案,包括负载均衡、弹性伸缩和容灾备份等措施,以确保系统的稳定性和可靠性。 ... [详细]
  • 本文深入探讨了如何选择适合业务需求的MySQL存储引擎,详细解析了不同存储引擎的特点、适用场景及其在数据存储和管理中的优势。通过对比InnoDB、MyISAM等主流引擎,为读者提供了全面的技术指导和专业建议,帮助开发者在实际应用中做出明智的选择。 ... [详细]
  • 深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案
    深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案 ... [详细]
  • 在当今的软件开发领域,分布式技术已成为程序员不可或缺的核心技能之一,尤其在面试中更是考察的重点。无论是小微企业还是大型企业,掌握分布式技术对于提升工作效率和解决实际问题都至关重要。本周的Java架构师实战训练营中,我们深入探讨了Kafka这一高效的分布式消息系统,它不仅支持发布订阅模式,还能在高并发场景下保持高性能和高可靠性。通过实际案例和代码演练,学员们对Kafka的应用有了更加深刻的理解。 ... [详细]
  • 基于Java的客户关系管理(CRM)系统优化与应用
    本研究探讨了基于Java技术的客户关系管理(CRM)系统的优化与应用。首先介绍了CRM系统的概念及其分类,并详细阐述了企业项目开发的流程。随后,对CRM系统的模块进行了划分,包括系统功能模块图和各模块的具体功能描述,为系统的高效运行提供了理论和技术支持。 ... [详细]
  • 如何高效启动大数据应用之旅?
    在前一篇文章中,我探讨了大数据的定义及其与数据挖掘的区别。本文将重点介绍如何高效启动大数据应用项目,涵盖关键步骤和最佳实践,帮助读者快速踏上大数据之旅。 ... [详细]
  • PHP开发人员薪资水平分析:工程师平均工资概况
    PHP开发人员薪资水平分析:工程师平均工资概况 ... [详细]
  • 2016-2017学年《网络安全实战》第三次作业
    2016-2017学年《网络安全实战》第三次作业总结了教材中关于网络信息收集技术的内容。本章主要探讨了网络踩点、网络扫描和网络查点三个关键步骤。其中,网络踩点旨在通过公开渠道收集目标信息,为后续的安全测试奠定基础,而不涉及实际的入侵行为。 ... [详细]
author-avatar
mobiledu2502886443
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有