神刀安全网

[增强可拓展性]Scrapy博客爬虫

前言

最近赶毕业论文也是心力憔悴,现在写一篇文章谈谈我的毕业设计。

在最初学习Python的时候,爬虫是个不错的练手项目,但是几乎所有的爬虫教程都是教我们如何如何爬一个网站。
比如我写的不正经的妹子图爬虫,但是我从来没有考虑过,当我需要从大量网站上爬取大量结构相似的数据时,难道也要一个网站对应一个爬虫?

我的毕业设计来源于慕课网的实战视频 聚焦Python分布式爬虫必学框架Scrapy 打造搜索引擎

最初,我原封不动的照搬了这套实战视频做我的毕业设计,但是我删除了原课程中知乎和拉钩的爬虫,我最初的设计是只编写博客的爬虫,但是添加更多的数据源,丰富数据库中的文章数量,从而使得搜索的效果更好。

我最初的做法在我现在看来真的是蠢爆了……没错……为了增加数据源我竟然为一个网站对应写了一个爬虫,最后得到的结果就是大量的重复代码和逻辑……(我不是处女座么,好尴尬……)
我的毕业设计指导老师都看不下去了(更尴尬……),但是当时的我内心os是:这老师懂个锤子……(自以为是最可怕)

直到我去实习,看到公司的爬虫项目源代码时,我才发现我是真的蠢……

现在着手开始重构代码!!!

在开始之前的申明

我的设计思路和方法可能不是正确的,并且肯定不是最优的。
如果有朋友觉得我的思路和方法有任何不妥,请及时指正,万分感谢!!!

设计思路

首先考虑,我们需要爬一个网站的博客都需要哪些内容?
1.博客的列表
2.博客的URL
3.博客的题目
4.博客的正文
5.下一页的URL
一般我们爬取博客都是爬这些字段,所以我们往往都需要写这些字段的Xpath。
除了这些字段的Xpath以外的逻辑,基本上都是统一的。

[增强可拓展性]Scrapy博客爬虫

现在,爬虫的整体逻辑都清晰了。那么我是怎么实现增强可拓展性的呢?

我们发现几乎所有的博客爬虫都是上图的逻辑,不同的只是不同字段的Xpath不同,那么我们只要在一个爬虫中爬不同的网站使用不同的Xpath就可以了。

比如我们爬取Jobbole时,我们Xpath就使用Jobbole的Xpath,爬51CTO我们就使用51CTO的Xpath。
现在我们的设计思路如下。

[增强可拓展性]Scrapy博客爬虫

根据爬取的网站不同调用不同的配置文件进行爬取博客。这样我们就可以写一个爬虫,而爬取多个网站的博客了。

代码编写

配置文件的文件格式我选择yaml
每一个网站我们都需要五个配置,首先是起始页的URL,然后依次是文章URL的Xpath,文章标题的Xpath,文章正文的Xpath,最后一个是下一页内容的Xpath,我以Jobbole为例。

http://blog.jobbole.com/all-posts/:   article_url: '//a[@class="archive-title"]/@href'   article_title: '//div[@class="entry-header"]/h1/text()'   article_content: '//div[@class="entry"]/*/text()'   next_button: '//a[@class="next page-numbers"]/@href' 

现在有了配置文件,当爬虫运行时如何将配置文件中的内容进行解析呢?
我们使用Python读写yaml的库yaml,以及常用模块os

# -*- coding:utf-8 -*- import os import yaml  # 引入在settings中设置的配置文件路径 from blog_spider.settings import MONITOR_LIST_PATH   class ConfigReader(object):     def __init__(self):         self.config = {}         self.config_parse = []         self.start_urls = []         host_path = os.path.join(MONITOR_LIST_PATH)          try:             with open(host_path, 'r')as f:                 self.config = yaml.load(f.read())         except:             print("解析配置文件失败")          for key in self.config:             key_words = [                             key,                             self.config[key]['article_url'],                             self.config[key]['article_title'],                             self.config[key]['article_content'],                             self.config[key]['next_button']                           ]             self.start_urls.append(key)             self.config_parse.append(key_words)  

将配置文件解析的方法写成了一个类,当这个类实例化的时候就会解析配置文件,并以list的数据结构返回爬虫。

配置文件的解析完成之后,现在就可以开始写爬虫了。
首先重载Scrapy的start_requests()方法,在start_requests()方法中获取到解析后的Xpath。

    def start_requests(self):         c = ConfigReader()         for config in c.config_parse:             url = config[0]             meta = {                         'article_url': config[1],                         'article_title': config[2],                         'article_content': config[3],                         'next_button': config[4]                     }             yield Request(url=url, callback=self.parse, meta=meta) 

将解析好的Xpath放在meta中,传入接下来执行的parse()方法中。
parse()方法中爬取首页中文章的url,在获取到文章的url之后进入parse_blog()方法解析文章。
parse()中还需要获取下一页的url并再次回到parse()方法,爬取下一页的文章。

    def parse(self, response):         blog_urls = response.xpath("{}".format(response.meta['article_url'])).extract()         meta = response.meta          for blog_url in blog_urls:             yield Request(url=parse.urljoin(response.url, blog_url), dont_filter=True, callback=self.parse_blog, meta=meta)          next_url = response.xpath("{}".format(response.meta['next_button'])).extract()[0]         if next_url:             yield Request(url=parse.urljoin(response.url, next_url), dont_filter=True, callback=self.parse, meta=meta) 

最后是爬取文章的parse_blog()方法。

    def parse_blog(self, response):          article_item = BlogSpiderItem()          article_title_xpath = response.meta['article_title']         article_content_xpath = response.meta['article_content']          try:             title = response.xpath("{}".format(article_title_xpath)).extract()[0]             create_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")             content = response.xpath("{}".format(article_content_xpath)).extract()         except IndexError:             print("爬取失败")          contents = ''         for word in content:             contents += word          article_item['article_url'] = response.url         article_item['article_title'] = title         article_item['article_time'] = create_time         article_item['article_content'] = contents 

因为设计的初衷是实时爬取最新的文章,所以文章的时间就是爬取的时间,所以create_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

到这里整个的爬虫就结束了,剩下的就只要持久化数据就可以了。

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » [增强可拓展性]Scrapy博客爬虫

分享到:更多 ()