最新公告
  • 欢迎您光临悠哉网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入我们
  • 手把手教你用Scrapy爬虫框架爬取食品论坛数据并存入数据库

    大家好,我是杯酒先生,这是我第一次写这种分享项目的文章,可能很水,很不全面,而且肯定存在说错的地方,希望大家可以评论里加以指点,不胜感激!

    一、前言

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

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

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

    二、项目目标

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

    三、项目准备

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

    软件:PyCharm

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

    目标网站:

    1. http://bbs.foodmate.net 

    插件:chromedriver(版本要对)

    四、项目分析

    1、确定爬取网站的结构

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

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

    2、如何选择合适的方式爬取数据?

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

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

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

    五、项目实现

    1、第一步:确定网站类型

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

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

    2、第二步:确定层级关系

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

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

    部分代码展示:

    一级界面:

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

    二级界面:

    1. def get_next_path(self, response): 
    2.     self.logger.info("已进入版块!"
    3.     self.logger.info("正在获取文章列表!"
    4.     if response.url == \'http://www.foodmate.net/know/\'
    5.         pass 
    6.     else
    7.         try: 
    8.             nums = response.css(\'#fd_page_bottom > div > label > span::text\').extract_first().split(\' \')[-2] 
    9.         except
    10.             nums = 1 
    11.         for num in range(1, int(nums) + 1): 
    12.             tbody_list = response.css(\'#threadlisttableid > tbody\'
    13.             for tbody in tbody_list: 
    14.                 if \'normalthread\' in str(tbody): 
    15.                     item = LunTanItem() 
    16.                     item[\'article_url\'] = response.urljoin( 
    17.                         tbody.css(\'* > tr > th > a.s.xst\').xpath(\'@href\').extract_first()) 
    18.                     item[\'type\'] = response.css( 
    19.                         \'#ct > div > div.bm.bml.pbn > div.bm_h.cl > h1 > a::text\').extract_first() 
    20.                     item[\'title\'] = tbody.css(\'* > tr > th > a.s.xst::text\').extract_first() 
    21.                     item[\'spider_type\'] = "论坛" 
    22.                     item[\'source\'] = "食品论坛" 
    23.                     if item[\'article_url\'] != \'http://bbs.foodmate.net/\'
    24.                         yield scrapy.Request( 
    25.                             url=item[\'article_url\'], 
    26.                             callback=self.get_data, 
    27.                             meta={\'item\': item, \'content_info\': []} 
    28.                         ) 
    29.         try: 
    30.             callback_url = response.css(\'#fd_page_bottom > div > a.nxt\').xpath(\'@href\').extract_first() 
    31.             callback_url = response.urljoin(callback_url) 
    32.             yield scrapy.Request( 
    33.                 url=callback_url, 
    34.                 callback=self.get_next_path, 
    35.             ) 
    36.         except IndexError: 
    37.             pass 

    三级界面:

    1. def get_data(self, response): 
    2.     self.logger.info("正在爬取论坛数据!"
    3.     item = response.meta[\'item\'
    4.     content_list = [] 
    5.     divs = response.xpath(\'//*[@id="postlist"]/div\'
    6.     user_name = response.css(\'div > div.pi > div:nth-child(1) > a::text\').extract() 
    7.     publish_time = response.css(\'div.authi > em::text\').extract() 
    8.     floor = divs.css(\'* strong> a> em::text\').extract() 
    9.     s_id = divs.xpath(\'@id\').extract() 
    10.     for i in range(len(divs) - 1): 
    11.         content = \'\' 
    12.         try: 
    13.  
    14.             strong = response.css(\'#postmessage_\' + s_id[i].split(\'_\')[-1] + \'\').xpath(\'string(.)\').extract() 
    15.             for s in strong: 
    16.                 content += s.split(\';\')[-1].lstrip(\'\\r\\n\'
    17.             datas = dict(content=content,  # 内容 
    18.                          reply_id=0,  # 回复的楼层,默认0 
    19.                          user_name=user_name[i],  # ⽤户名 
    20.                          publish_time=publish_time[i].split(\'于 \')[-1],  # %Y-%m-%d %H:%M:%S\' 
    21.                          id=\'#\' + floor[i],  # 楼层 
    22.                          ) 
    23.             content_list.append(datas) 
    24.         except IndexError: 
    25.             pass 
    26.     item[\'content_info\'] = response.meta[\'content_info\'
    27.     item[\'scrawl_time\'] = datetime.now().strftime(\'%Y-%m-%d %H:%M:%S\'
    28.     item[\'content_info\'] += content_list 
    29.  
    30.     data_url = response.css(\'#ct > div.pgbtn > a\').xpath(\'@href\').extract_first() 
    31.     if data_url != None: 
    32.         data_url = response.urljoin(data_url) 
    33.         yield scrapy.Request( 
    34.             url=data_url, 
    35.             callback=self.get_data, 
    36.             meta={\'item\': item, \'content_info\': item[\'content_info\']} 
    37.         ) 
    38.     else
    39.         item[\'scrawl_time\'] = datetime.now().strftime(\'%Y-%m-%d %H:%M:%S\'
    40.         self.logger.info("正在存储!"
    41.         print(\'储存成功\'
    42.         yield item 

    3、第三步:确定爬取方法

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

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

    实现部分代码如下:

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

    4、第四步:确定爬取数据的储存格式

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

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

    5、第五步:确定保存数据库

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

    代码:

    1. import pymongo 
    2.  
    3. class FMPipeline(): 
    4.     def __init__(self): 
    5.         super(FMPipeline, self).__init__() 
    6.         # client = pymongo.MongoClient(\'139.217.92.75\'
    7.         client = pymongo.MongoClient(\'localhost\'
    8.         db = client.scrapy_FM 
    9.         self.collection = db.FM 
    10.  
    11.     def process_item(self, item, spider): 
    12.         query = { 
    13.             \'article_url\': item[\'article_url\'
    14.         } 
    15.         self.collection.update_one(query, {"$set": dict(item)}, upsert=True
    16.         return item 

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

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

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

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

    6、其他设置

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

    七、效果展示

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

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

    3、数据库数据展示:

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

    八、总结

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

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

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

    我是杯酒先生,最后分享我的座右铭给大家:保持独立思考,不卑不亢不怂。

    最后需要本文项目代码的小伙伴,请在公众号后台回复“食品论坛”关键字进行获取,如果在运行过程中有遇到任何问题,请随时留言或者加小编好友,小编看到会帮助大家解决bug噢!

    【编辑推荐】

    1. Gartner口中的SASE,为什么会成为国内外追捧的热点
    2. 关键时刻看昭阳 联想昭阳以硬核实力助力每个重要瞬间
    3. 榨干服务器:一次惨无人道的性能优化
    4. Photoshop精彩实例教程[2021版]
    5. 大厂Redis热点key解决之道
    1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
    2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
    3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
    4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
    5. 如有链接无法下载、失效或广告,请联系管理员处理!
    6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
    7. 如遇到加密压缩包,默认解压密码为"www.yoozai.net",如遇到无法解压的请联系管理员!
    悠哉网 » 手把手教你用Scrapy爬虫框架爬取食品论坛数据并存入数据库

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    悠哉网 WWW.YOOZAI.NET
    悠哉网,用户消费首选的网站,喜欢你就悠哉一下。

    发表评论

    • 1029会员总数(位)
    • 40455资源总数(个)
    • 33本周发布(个)
    • 19 今日发布(个)
    • 400稳定运行(天)

    提供最优质的资源集合

    立即查看 了解详情